diff --git a/include/zephyr/bluetooth/classic/goep.h b/include/zephyr/bluetooth/classic/goep.h index 79a5552386d67..1711f0e4f2b04 100644 --- a/include/zephyr/bluetooth/classic/goep.h +++ b/include/zephyr/bluetooth/classic/goep.h @@ -143,6 +143,11 @@ struct bt_goep_transport_rfcomm_server { * This callback is called whenever a new incoming GOEP connection requires * authorization. * + * Before returning the callback, @ref bt_goep::transport_ops should be initialized with + * valid address of type @ref bt_goep_transport_ops object. The field `mtu` of + * @ref bt_obex::rx could be passed with valid value. Or set it to zero, the mtu will be + * calculated according to @kconfig{CONFIG_BT_GOEP_RFCOMM_MTU}. + * * @warning It is the responsibility of the caller to zero out the parent of the GOEP * object. * @@ -184,10 +189,9 @@ int bt_goep_transport_rfcomm_server_register(struct bt_goep_transport_rfcomm_ser * The GOEP object is passed (over an address of it) as second parameter, application should * create transport dedicated GOEP object @ref bt_goep. Then pass to this API the location * (address). - * Before calling the API, @ref bt_obex::client_ops and @ref bt_goep::transport_ops should - * be initialized with valid address of type @ref bt_obex_client_ops object and - * @ref bt_goep_transport_ops object. The field `mtu` of @ref bt_obex::rx could be passed with - * valid value. Or set it to zero, the mtu will be calculated according to + * Before calling the API, @ref bt_goep::transport_ops should be initialized with valid address + * of type @ref bt_goep_transport_ops object. The field `mtu` of @ref bt_obex::rx could be passed + * with valid value. Or set it to zero, the mtu will be calculated according to * @kconfig{CONFIG_BT_GOEP_RFCOMM_MTU}. * The RFCOMM channel is passed as third parameter. It is the RFCOMM channel of RFCOMM server of * peer device. @@ -241,6 +245,11 @@ struct bt_goep_transport_l2cap_server { * This callback is called whenever a new incoming GOEP connection requires * authorization. * + * Before returning the callback, @ref bt_goep::transport_ops should be initialized with + * valid address of type @ref bt_goep_transport_ops object. The field `mtu` of + * @ref bt_obex::rx could be passed with valid value. Or set it to zero, the mtu will be + * calculated according to @kconfig{CONFIG_BT_GOEP_L2CAP_MTU}. + * * @warning It is the responsibility of the caller to zero out the parent of the GOEP * object. * @@ -286,10 +295,9 @@ int bt_goep_transport_l2cap_server_register(struct bt_goep_transport_l2cap_serve * The GOEP object is passed (over an address of it) as second parameter, application should * create transport dedicated GOEP object @ref bt_goep. Then pass to this API the location * (address). - * Before calling the API, @ref bt_obex::client_ops and @ref bt_goep::transport_ops should - * be initialized with valid address of type @ref bt_obex_client_ops object and - * @ref bt_goep_transport_ops object. The field `mtu` of @ref bt_obex::rx could be passed with - * valid value. Or set it to zero, the mtu will be calculated according to + * Before calling the API, @ref bt_goep::transport_ops should be initialized with valid address + * of type @ref bt_goep_transport_ops object. The field `mtu` of @ref bt_obex::rx could be passed + * with valid value. Or set it to zero, the mtu will be calculated according to * @kconfig{CONFIG_BT_GOEP_L2CAP_MTU}. * The L2CAP PSM is passed as third parameter. It is the RFCOMM channel of RFCOMM server of peer * device. diff --git a/include/zephyr/bluetooth/classic/obex.h b/include/zephyr/bluetooth/classic/obex.h index 8f10f52fdabc8..3d56edbd0786c 100644 --- a/include/zephyr/bluetooth/classic/obex.h +++ b/include/zephyr/bluetooth/classic/obex.h @@ -207,9 +207,38 @@ enum __packed bt_obex_header_id { BT_OBEX_HEADER_ID_SRM_PARAM = 0x98, }; +/** The OBEX packet header length for PUT and GET request and response. */ +#define BT_OBEX_HDR_LEN 3 + +/** The max PDU data length of OBEX pachet. */ +#define BT_OBEX_PDU_LEN(mopl) ((mopl) - BT_OBEX_HDR_LEN) + +/** The header length of the OBEX header body/end body. */ +#define BT_OBEX_HDR_LEN_OF_HEADER_BODY 3 + +/** The max remaining length of the buffer if the adding header is body/end body. + * + * It is used to calculate the max sending length when adding the header body data. + * @code{.c} + * uint16_t len = BT_OBEX_PDU_LEN(mopl); + * + * len = MIN(len - buf->len, net_buf_tailroom(buf)); + * if (len > BT_OBEX_HDR_LEN_OF_HEADER_BODY) { + * len = BT_OBEX_DATA_LEN_OF_HEADER_BODY(len); + * Calling bt_obex_add_header_body() or bt_obex_add_header_end_body() + * ... + * } + * @endcode + * + * @param len The max remaining length. + */ +#define BT_OBEX_DATA_LEN_OF_HEADER_BODY(len) ((len) - BT_OBEX_HDR_LEN_OF_HEADER_BODY) + #define BT_OBEX_SEND_BUF_RESERVE 7 struct bt_obex; +struct bt_obex_server; +struct bt_obex_client; /** @brief OBEX server operations structure. * @@ -221,76 +250,77 @@ struct bt_obex_server_ops { * If this callback is provided it will be called whenever the OBEX connect request * is received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param version OBEX version number. * @param mopl Maximum OBEX packet length. * @param buf Sequence of headers. */ - void (*connect)(struct bt_obex *obex, uint8_t version, uint16_t mopl, struct net_buf *buf); + void (*connect)(struct bt_obex_server *server, uint8_t version, uint16_t mopl, + struct net_buf *buf); /** @brief OBEX disconnect request callback * * If this callback is provided it will be called whenever the OBEX disconnect request * is received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param buf Sequence of headers. */ - void (*disconnect)(struct bt_obex *obex, struct net_buf *buf); + void (*disconnect)(struct bt_obex_server *server, struct net_buf *buf); /** @brief OBEX put request callback * * If this callback is provided it will be called whenever the OBEX put request is * received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param final If the final bit is set. * @param buf Sequence of headers. */ - void (*put)(struct bt_obex *obex, bool final, struct net_buf *buf); + void (*put)(struct bt_obex_server *server, bool final, struct net_buf *buf); /** @brief OBEX get request callback * * If this callback is provided it will be called whenever the OBEX get request is * received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param final If the final bit is set. * @param buf Sequence of headers. */ - void (*get)(struct bt_obex *obex, bool final, struct net_buf *buf); + void (*get)(struct bt_obex_server *server, bool final, struct net_buf *buf); /** @brief OBEX abort request callback * * If this callback is provided it will be called whenever the OBEX abort request is * received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param buf Optional headers. */ - void (*abort)(struct bt_obex *obex, struct net_buf *buf); + void (*abort)(struct bt_obex_server *server, struct net_buf *buf); /** @brief OBEX SetPath request callback * * If this callback is provided it will be called whenever the OBEX SetPath request is * received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param flags The flags. * @param buf Optional headers. */ - void (*setpath)(struct bt_obex *obex, uint8_t flags, struct net_buf *buf); + void (*setpath)(struct bt_obex_server *server, uint8_t flags, struct net_buf *buf); /** @brief OBEX action request callback * * If this callback is provided it will be called whenever the OBEX action request is * received. * - * @param obex The OBEX object. + * @param server The OBEX server object. * @param final If the final bit is set. * @param buf Sequence of headers (Including action identifier header if it exists). */ - void (*action)(struct bt_obex *obex, bool final, struct net_buf *buf); + void (*action)(struct bt_obex_server *server, bool final, struct net_buf *buf); }; /** @brief OBEX client operations structure. @@ -303,80 +333,80 @@ struct bt_obex_client_ops { * If this callback is provided it will be called whenever the OBEX connect response * is received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param version OBEX version number. * @param mopl Maximum OBEX packet length. * @param buf Sequence of headers. */ - void (*connect)(struct bt_obex *obex, uint8_t rsp_code, uint8_t version, uint16_t mopl, - struct net_buf *buf); + void (*connect)(struct bt_obex_client *client, uint8_t rsp_code, uint8_t version, + uint16_t mopl, struct net_buf *buf); /** @brief OBEX disconnect response callback * * If this callback is provided it will be called whenever the OBEX disconnect response * is received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Sequence of headers. */ - void (*disconnect)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*disconnect)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX put response callback * * If this callback is provided it will be called whenever the OBEX put response is * received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Optional response headers. */ - void (*put)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*put)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX get response callback * * If this callback is provided it will be called whenever the OBEX get response is * received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Optional response headers. */ - void (*get)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*get)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX abort response callback * * If this callback is provided it will be called whenever the OBEX abort response is * received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Optional response headers. */ - void (*abort)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*abort)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX SetPath response callback * * If this callback is provided it will be called whenever the OBEX SetPath response is * received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Optional response headers. */ - void (*setpath)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*setpath)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX action response callback * * If this callback is provided it will be called whenever the OBEX action response is * received. * - * @param obex The OBEX object. + * @param client The OBEX client object. * @param rsp_code Response code. * @param buf Optional response headers. */ - void (*action)(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); + void (*action)(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf); }; /** @brief OBEX transport operations structure. @@ -447,39 +477,84 @@ enum __packed bt_obex_state { BT_OBEX_DISCONNECTING, }; -/** @brief OBEX structure. */ -struct bt_obex { +/* bt_obex flags */ +enum { + BT_OBEX_HAS_TARGET, /* Has target_header */ +}; + +union bt_obex_uuid { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_32 u32; + struct bt_uuid_128 u128; +}; + +/** @brief OBEX server structure. */ +struct bt_obex_server { + /** @brief OBEX Object */ + struct bt_obex *obex; + + /** @brief UUID of the service */ + const struct bt_uuid_128 *uuid; + /** @brief OBEX Server operations * * If it is a obex sever, the upper layer should pass the operations of server to - * `server_ops` when providing the OBEX structure. + * `server_ops` when providing the OBEX server structure. */ - const struct bt_obex_server_ops *server_ops; + const struct bt_obex_server_ops *ops; + + struct { + /** @brief The Maximum OBEX Packet Length (MOPL) */ + uint16_t mopl; + } rx; + + struct { + /** @brief The Maximum OBEX Packet Length (MOPL) */ + uint16_t mopl; + } tx; + + /** @internal Saves the current state, @ref bt_obex_state */ + atomic_t _state; + + /** @internal OBEX opcode */ + atomic_t _opcode; + + /** @internal OBEX flags */ + atomic_t _flags; + + /** @internal Target of service */ + union bt_obex_uuid _target; + + /** @internal OBEX connection identifier */ + uint32_t _conn_id; + + /** @internal sys snode */ + sys_snode_t _node; +}; + +/** @brief OBEX client structure. */ +struct bt_obex_client { + /** @brief OBEX Object */ + struct bt_obex *obex; /** @brief OBEX Client operations * * If it is a obex client, the upper layer should pass the operations of client to - * `client_ops` when providing the OBEX structure. + * `client_ops` when providing the OBEX client structure. */ - const struct bt_obex_client_ops *client_ops; + const struct bt_obex_client_ops *ops; struct { - /** @brief MTU of OBEX transport */ - uint16_t mtu; /** @brief The Maximum OBEX Packet Length (MOPL) */ uint16_t mopl; } rx; struct { - /** @brief MTU of OBEX transport */ - uint16_t mtu; /** @brief The Maximum OBEX Packet Length (MOPL) */ uint16_t mopl; } tx; - /** @internal OBEX transport operations */ - const struct bt_obex_transport_ops *_transport_ops; - /** @internal Saves the current state, @ref bt_obex_state */ atomic_t _state; @@ -488,8 +563,78 @@ struct bt_obex { /** @internal OBEX previous opcode */ atomic_t _pre_opcode; + + /** @internal OBEX flags */ + atomic_t _flags; + + /** @internal target of the conn req */ + union bt_obex_uuid _target; + + /** @internal OBEX connection identifier */ + uint32_t _conn_id; + + /** @internal sys snode */ + sys_snode_t _node; +}; + +/** @brief OBEX structure. */ +struct bt_obex { + struct { + /** @brief MTU of OBEX transport */ + uint16_t mtu; + } rx; + + struct { + /** @brief MTU of OBEX transport */ + uint16_t mtu; + } tx; + + /** @internal OBEX transport operations */ + const struct bt_obex_transport_ops *_transport_ops; + + /** @internal OBEX executing client */ + atomic_ptr_t _active_client; + + /** @internal OBEX executing client */ + atomic_ptr_t _active_server; + + /** @internal OBEX clients */ + sys_slist_t _clients; + + /** @internal OBEX servers */ + sys_slist_t _servers; }; +/** @brief OBEX server register + * + * Register a OBEX server with the specific UUID. + * + * The OBEX object of the server should be set before calling the function. + * If the UUID is NULL, any connection request will be accepted. Or, the space of the UUID should + * be retained. + * Before calling the API, @ref bt_obex_server::ops should be initialized with valid + * address of type @ref bt_obex_server_ops object. And the @ref bt_obex_server::obex should be + * initialized with the valid OBEX object of type @ref bt_obex. + * + * @param server The OBEX server object. + * @param uuid The UUID of the service. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_obex_server_register(struct bt_obex_server *server, const struct bt_uuid_128 *uuid); + +/** @brief OBEX server unregister + * + * Unregister a registered OBEX server. + * + * The server should be disconnected when calling the function. + * + * @param server The OBEX server object. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_obex_server_unregister(struct bt_obex_server *server); + /** @brief OBEX connect request * * The connect operation initiates the connection and sets up the basic expectations of each side @@ -498,17 +643,21 @@ struct bt_obex { * stored in thrid parameter `buf`. All headers are packed by calling function * bt_obex_add_header_*. Such as header `Authenticate Challenge` is packed by calling * @ref bt_obex_add_header_auth_challenge. + * The OBEX object of the client should be set before calling the function. + * Before calling the API, @ref bt_obex_client::ops should be initialized with valid + * address of type @ref bt_obex_client_ops object. And the @ref bt_obex_client::obex should be + * initialized with the valid OBEX object of type @ref bt_obex. * * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param mopl Maximum OBEX packet length. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf); +int bt_obex_connect(struct bt_obex_client *client, uint16_t mopl, struct net_buf *buf); /** @brief OBEX connect response * @@ -525,14 +674,15 @@ int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param mopl Maximum OBEX packet length. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, struct net_buf *buf); +int bt_obex_connect_rsp(struct bt_obex_server *server, uint8_t rsp_code, uint16_t mopl, + struct net_buf *buf); /** @brief OBEX disconnect request * @@ -547,12 +697,12 @@ int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, s * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_disconnect(struct bt_obex *obex, struct net_buf *buf); +int bt_obex_disconnect(struct bt_obex_client *client, struct net_buf *buf); /** @brief OBEX disconnect response * @@ -569,13 +719,13 @@ int bt_obex_disconnect(struct bt_obex *obex, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_disconnect_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_disconnect_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX put request * @@ -595,13 +745,13 @@ int bt_obex_disconnect_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_bu * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param final The final bit of opcode. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf); +int bt_obex_put(struct bt_obex_client *client, bool final, struct net_buf *buf); /** @brief OBEX put response * @@ -618,13 +768,13 @@ int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_put_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX get request * @@ -641,13 +791,13 @@ int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param final The final bit of opcode. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf); +int bt_obex_get(struct bt_obex_client *client, bool final, struct net_buf *buf); /** @brief OBEX get response * @@ -677,13 +827,13 @@ int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_get_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_get_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX abort request * @@ -699,12 +849,12 @@ int bt_obex_get_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_abort(struct bt_obex *obex, struct net_buf *buf); +int bt_obex_abort(struct bt_obex_client *client, struct net_buf *buf); /** @brief OBEX abort response * @@ -721,13 +871,13 @@ int bt_obex_abort(struct bt_obex *obex, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_abort_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_abort_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX setpath request * @@ -754,13 +904,13 @@ int bt_obex_abort_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *bu * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param flags Flags for setpath request. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf); +int bt_obex_setpath(struct bt_obex_client *client, uint8_t flags, struct net_buf *buf); /** @brief OBEX setpath response * @@ -780,13 +930,13 @@ int bt_obex_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_setpath_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_setpath_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief OBEX Actions. */ enum __packed bt_obex_action_id { @@ -831,13 +981,13 @@ enum __packed bt_obex_action_id { * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param client OBEX client object. * @param final The final bit of opcode. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf); +int bt_obex_action(struct bt_obex_client *client, bool final, struct net_buf *buf); /** @brief OBEX action response * @@ -886,13 +1036,13 @@ int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf); * @note Buffer ownership is transferred to the stack in case of success, in case of an error * the caller retains the ownership of the buffer. * - * @param obex OBEX object. + * @param server OBEX server object. * @param rsp_code Response code. * @param buf Sequence of headers to be sent out. * * @return 0 in case of success or negative value in case of error. */ -int bt_obex_action_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf); +int bt_obex_action_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf); /** @brief Add Header: number of objects (used by Connect) * @@ -1511,6 +1661,16 @@ int bt_obex_get_header_srm(struct net_buf *buf, uint8_t *srm); */ int bt_obex_get_header_srm_param(struct net_buf *buf, uint8_t *srm_param); +/** @brief Make the UUID with specific data and len + * + * @param uuid OBEX uuid. + * @param data UUID data. + * @param len UUID data length. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_obex_make_uuid(union bt_obex_uuid *uuid, const uint8_t *data, uint16_t len); + #ifdef __cplusplus } #endif diff --git a/subsys/bluetooth/host/classic/goep.c b/subsys/bluetooth/host/classic/goep.c index a565f547971e4..8913f65dc3a66 100644 --- a/subsys/bluetooth/host/classic/goep.c +++ b/subsys/bluetooth/host/classic/goep.c @@ -62,11 +62,6 @@ static void goep_rfcomm_connected(struct bt_rfcomm_dlc *dlc) goep->obex.rx.mtu = dlc->mtu; goep->obex.tx.mtu = dlc->mtu; - /* Set MOPL of RX to MTU by default */ - goep->obex.rx.mopl = dlc->mtu; - /* Set MOPL of TX to MTU by default to avoid the OBEX connect req cannot be sent. */ - goep->obex.tx.mopl = dlc->mtu; - atomic_set(&goep->_state, BT_GOEP_TRANSPORT_CONNECTED); err = bt_obex_transport_connected(&goep->obex); @@ -186,8 +181,7 @@ static int goep_rfcomm_accept(struct bt_conn *conn, struct bt_rfcomm_server *ser return err; } - if (!goep || !goep->transport_ops || !goep->obex.server_ops || - !goep->obex.server_ops->connect || !goep->obex.server_ops->disconnect) { + if (goep == NULL || goep->transport_ops == NULL) { LOG_DBG("Invalid parameter"); return -EINVAL; } @@ -263,8 +257,7 @@ int bt_goep_transport_rfcomm_connect(struct bt_conn *conn, struct bt_goep *goep, uint32_t mtu; uint32_t hdr_size; - if (!conn || !goep || !goep->transport_ops || !goep->obex.client_ops || - !goep->obex.client_ops->connect || !goep->obex.client_ops->disconnect) { + if (conn == NULL || goep == NULL || goep->transport_ops == NULL) { LOG_DBG("Invalid parameter"); return -EINVAL; } @@ -372,11 +365,6 @@ static void goep_l2cap_connected(struct bt_l2cap_chan *chan) goep->obex.rx.mtu = goep->_transport.chan.rx.mtu; goep->obex.tx.mtu = goep->_transport.chan.tx.mtu; - /* Set MOPL of RX to MTU by default */ - goep->obex.rx.mopl = goep->_transport.chan.rx.mtu; - /* Set MOPL of TX to MTU by default to avoid the OBEX connect req cannot be sent. */ - goep->obex.tx.mopl = goep->_transport.chan.tx.mtu; - atomic_set(&goep->_state, BT_GOEP_TRANSPORT_CONNECTED); err = bt_obex_transport_connected(&goep->obex); @@ -485,8 +473,7 @@ static int goep_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *serve return err; } - if (!goep || !goep->transport_ops || !goep->obex.server_ops || - !goep->obex.server_ops->connect || !goep->obex.server_ops->disconnect) { + if (goep == NULL || goep->transport_ops == NULL) { LOG_DBG("Invalid parameter"); return -EINVAL; } @@ -566,8 +553,7 @@ int bt_goep_transport_l2cap_connect(struct bt_conn *conn, struct bt_goep *goep, uint32_t mtu; uint32_t hdr_size; - if (!conn || !goep || !goep->transport_ops || !goep->obex.client_ops || - !goep->obex.client_ops->connect || !goep->obex.client_ops->disconnect) { + if (conn == NULL || goep == NULL || goep->transport_ops == NULL) { LOG_DBG("Invalid parameter"); return -EINVAL; } diff --git a/subsys/bluetooth/host/classic/obex.c b/subsys/bluetooth/host/classic/obex.c index 29572d8bf0568..2f30cec1d2dc9 100644 --- a/subsys/bluetooth/host/classic/obex.c +++ b/subsys/bluetooth/host/classic/obex.c @@ -28,11 +28,14 @@ #include LOG_MODULE_REGISTER(bt_obex); +#define OBEX_SERVER(node) CONTAINER_OF(node, struct bt_obex_server, _node) + static struct net_buf *obex_alloc_buf(struct bt_obex *obex) { struct net_buf *buf; - if (!obex->_transport_ops || !obex->_transport_ops->alloc_buf) { + if (obex == NULL || obex->_transport_ops == NULL || + obex->_transport_ops->alloc_buf == NULL) { LOG_WRN("Buffer allocation is unsupported"); return NULL; } @@ -44,15 +47,15 @@ static struct net_buf *obex_alloc_buf(struct bt_obex *obex) return buf; } -static int obex_send(struct bt_obex *obex, struct net_buf *buf) +static int obex_send(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) { - if (!obex->_transport_ops || !obex->_transport_ops->send) { + if (obex == NULL || obex->_transport_ops == NULL || obex->_transport_ops->send == NULL) { LOG_WRN("OBEX sending is unsupported"); return -EIO; } - if (buf->len > obex->tx.mopl) { - LOG_WRN("Data length too long (%d > %d)", buf->len, obex->tx.mopl); + if (buf->len > mopl) { + LOG_WRN("Data length too long (%d > %d)", buf->len, mopl); return -EMSGSIZE; } @@ -63,7 +66,8 @@ static int obex_transport_disconn(struct bt_obex *obex) { int err; - if (!obex->_transport_ops || !obex->_transport_ops->disconnect) { + if (obex == NULL || obex->_transport_ops == NULL || + obex->_transport_ops->disconnect == NULL) { LOG_WRN("OBEX transport disconn is unsupported"); return -EIO; } @@ -75,12 +79,75 @@ static int obex_transport_disconn(struct bt_obex *obex) return -EINVAL; } +struct bt_obex_has_header { + uint8_t id; + bool found; +}; + +static bool bt_obex_has_header_cb(struct bt_obex_hdr *hdr, void *user_data) +{ + struct bt_obex_has_header *data; + + data = user_data; + + if (hdr->id == data->id) { + data->found = true; + return false; + } + return true; +} + +static bool bt_obex_has_header(struct net_buf *buf, uint8_t id) +{ + struct bt_obex_has_header data; + int err; + + if (buf == NULL) { + LOG_WRN("Invalid parameter"); + return false; + } + + data.id = id; + data.found = false; + + err = bt_obex_header_parse(buf, bt_obex_has_header_cb, &data); + if (err != 0) { + return false; + } + + return data.found; +} + +int bt_obex_make_uuid(union bt_obex_uuid *uuid, const uint8_t *data, uint16_t len) +{ + switch (len) { + case BT_UUID_SIZE_16: + uuid->uuid.type = BT_UUID_TYPE_16; + uuid->u16.val = sys_get_be16(data); + break; + case BT_UUID_SIZE_32: + uuid->uuid.type = BT_UUID_TYPE_32; + uuid->u32.val = sys_get_be32(data); + break; + case BT_UUID_SIZE_128: + uuid->uuid.type = BT_UUID_TYPE_128; + sys_memcpy_swap(uuid->u128.val, data, len); + break; + default: + LOG_WRN("Unsupported UUID len %u", len); + return -EINVAL; + } + + return 0; +} + struct server_handler { uint8_t opcode; - int (*handler)(struct bt_obex *obex, uint16_t len, struct net_buf *buf); + uint16_t min_len; + int (*handler)(struct bt_obex_server *server, uint16_t len, struct net_buf *buf); }; -static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_connect(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { struct bt_obex_conn_req_hdr *hdr; struct bt_obex_conn_rsp_hdr *rsp_conn_hdr; @@ -90,6 +157,8 @@ static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_bu uint16_t mopl; uint8_t rsp_code; int err; + uint16_t target_len; + const uint8_t *target_data; LOG_DBG(""); @@ -99,19 +168,19 @@ static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_bu goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_DISCONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_DISCONNECTED) { LOG_WRN("Invalid state, connect refused"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->connect) { + if (server->ops->connect == NULL) { LOG_WRN("Conn req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_CONNECT)) { + if (!atomic_cas(&server->_opcode, 0, BT_OBEX_OPCODE_CONNECT)) { LOG_WRN("Unexpected conn request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; @@ -121,22 +190,47 @@ static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_bu flags = net_buf_pull_u8(buf); mopl = net_buf_pull_be16(buf); + atomic_set_bit_to(&server->_flags, BT_OBEX_HAS_TARGET, + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TARGET)); + + err = bt_obex_get_header_target(buf, &target_len, &target_data); + if (err != 0 && atomic_test_bit(&server->_flags, BT_OBEX_HAS_TARGET)) { + LOG_WRN("Invalid target header"); + rsp_code = BT_OBEX_RSP_CODE_PRECON_FAIL; + goto failed; + } + + if (err == 0) { + err = bt_obex_make_uuid(&server->_target, target_data, target_len); + if (err != 0) { + LOG_WRN("Invalid UUID header"); + rsp_code = BT_OBEX_RSP_CODE_PRECON_FAIL; + goto failed; + } + } + LOG_DBG("version %u, flags %u, mopl %u", version, flags, mopl); if (mopl < BT_OBEX_MIN_MTU) { - LOG_WRN("Invalid MTU length (%d < %d)", mopl, BT_OBEX_MIN_MTU); + LOG_WRN("Invalid MOPL (%d < %d)", mopl, BT_OBEX_MIN_MTU); rsp_code = BT_OBEX_RSP_CODE_PRECON_FAIL; goto failed; } - obex->tx.mopl = mopl; + if (mopl > server->obex->tx.mtu) { + LOG_WRN("MOPL exceeds MTU (%d > %d)", mopl, server->obex->tx.mtu); + rsp_code = BT_OBEX_RSP_CODE_PRECON_FAIL; + goto failed; + } - atomic_set(&obex->_state, BT_OBEX_CONNECTING); - obex->server_ops->connect(obex, version, mopl, buf); + server->tx.mopl = mopl; + + atomic_set(&server->_state, BT_OBEX_CONNECTING); + server->ops->connect(server, version, mopl, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -150,7 +244,7 @@ static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_bu rsp_conn_hdr->version = BT_OBEX_VERSION; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -158,7 +252,7 @@ static int obex_server_connect(struct bt_obex *obex, uint16_t len, struct net_bu return err; } -static int obex_server_disconn(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_disconn(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; uint8_t rsp_code; @@ -172,30 +266,30 @@ static int obex_server_disconn(struct bt_obex *obex, uint16_t len, struct net_bu goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->disconnect) { + if (server->ops->disconnect == NULL) { LOG_WRN("Conn req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_DISCONN)) { + if (!atomic_cas(&server->_opcode, 0, BT_OBEX_OPCODE_DISCONN)) { LOG_WRN("Unexpected disconn request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - atomic_set(&obex->_state, BT_OBEX_DISCONNECTING); - obex->server_ops->disconnect(obex, buf); + atomic_set(&server->_state, BT_OBEX_DISCONNECTING); + server->ops->disconnect(server, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -205,7 +299,7 @@ static int obex_server_disconn(struct bt_obex *obex, uint16_t len, struct net_bu rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -213,7 +307,7 @@ static int obex_server_disconn(struct bt_obex *obex, uint16_t len, struct net_bu return err; } -static int obex_server_put_common(struct bt_obex *obex, bool final, uint16_t len, +static int obex_server_put_common(struct bt_obex_server *server, bool final, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; @@ -230,21 +324,21 @@ static int obex_server_put_common(struct bt_obex *obex, bool final, uint16_t len goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->put) { + if (server->ops->put == NULL) { LOG_WRN("Put req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } req_code = final ? BT_OBEX_OPCODE_PUT_F : BT_OBEX_OPCODE_PUT; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { - opcode = atomic_get(&obex->_opcode); + if (!atomic_cas(&server->_opcode, 0, req_code)) { + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_PUT_F) && (opcode != BT_OBEX_OPCODE_PUT)) { LOG_WRN("Unexpected put request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; @@ -258,15 +352,15 @@ static int obex_server_put_common(struct bt_obex *obex, bool final, uint16_t len } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&server->_opcode, opcode, req_code); } } - obex->server_ops->put(obex, final, buf); + server->ops->put(server, final, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -276,7 +370,7 @@ static int obex_server_put_common(struct bt_obex *obex, bool final, uint16_t len rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -284,17 +378,17 @@ static int obex_server_put_common(struct bt_obex *obex, bool final, uint16_t len return err; } -static int obex_server_put(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_put(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { - return obex_server_put_common(obex, false, len, buf); + return obex_server_put_common(server, false, len, buf); } -static int obex_server_put_final(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_put_final(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { - return obex_server_put_common(obex, true, len, buf); + return obex_server_put_common(server, true, len, buf); } -static int obex_server_get_common(struct bt_obex *obex, bool final, uint16_t len, +static int obex_server_get_common(struct bt_obex_server *server, bool final, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; @@ -311,21 +405,21 @@ static int obex_server_get_common(struct bt_obex *obex, bool final, uint16_t len goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->get) { + if (server->ops->get == NULL) { LOG_WRN("Get req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } req_code = final ? BT_OBEX_OPCODE_GET_F : BT_OBEX_OPCODE_GET; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { - opcode = atomic_get(&obex->_opcode); + if (!atomic_cas(&server->_opcode, 0, req_code)) { + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_GET_F) && (opcode != BT_OBEX_OPCODE_GET)) { LOG_WRN("Unexpected get request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; @@ -339,15 +433,15 @@ static int obex_server_get_common(struct bt_obex *obex, bool final, uint16_t len } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&server->_opcode, opcode, req_code); } } - obex->server_ops->get(obex, final, buf); + server->ops->get(server, final, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -357,7 +451,7 @@ static int obex_server_get_common(struct bt_obex *obex, bool final, uint16_t len rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -365,17 +459,17 @@ static int obex_server_get_common(struct bt_obex *obex, bool final, uint16_t len return err; } -static int obex_server_get(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_get(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { - return obex_server_get_common(obex, false, len, buf); + return obex_server_get_common(server, false, len, buf); } -static int obex_server_get_final(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_get_final(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { - return obex_server_get_common(obex, true, len, buf); + return obex_server_get_common(server, true, len, buf); } -static int obex_server_setpath(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_setpath(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; struct bt_obex_setpath_req_hdr *req_hdr; @@ -390,30 +484,30 @@ static int obex_server_setpath(struct bt_obex *obex, uint16_t len, struct net_bu goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->setpath) { + if (server->ops->setpath == NULL) { LOG_WRN("Setpath req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_SETPATH)) { + if (!atomic_cas(&server->_opcode, 0, BT_OBEX_OPCODE_SETPATH)) { LOG_WRN("Unexpected setpath request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } req_hdr = net_buf_pull_mem(buf, sizeof(*req_hdr)); - obex->server_ops->setpath(obex, req_hdr->flags, buf); + server->ops->setpath(server, req_hdr->flags, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -423,7 +517,7 @@ static int obex_server_setpath(struct bt_obex *obex, uint16_t len, struct net_bu rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -431,7 +525,7 @@ static int obex_server_setpath(struct bt_obex *obex, uint16_t len, struct net_bu return err; } -static int obex_server_action_common(struct bt_obex *obex, bool final, uint16_t len, +static int obex_server_action_common(struct bt_obex_server *server, bool final, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; @@ -448,21 +542,21 @@ static int obex_server_action_common(struct bt_obex *obex, bool final, uint16_t goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->action) { + if (server->ops->action == NULL) { LOG_WRN("Action req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } req_code = final ? BT_OBEX_OPCODE_ACTION_F : BT_OBEX_OPCODE_ACTION; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { - opcode = atomic_get(&obex->_opcode); + if (!atomic_cas(&server->_opcode, 0, req_code)) { + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_ACTION_F) && (opcode != BT_OBEX_OPCODE_ACTION)) { LOG_WRN("Unexpected action request"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; @@ -476,15 +570,15 @@ static int obex_server_action_common(struct bt_obex *obex, bool final, uint16_t } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&server->_opcode, opcode, req_code); } } - obex->server_ops->action(obex, final, buf); + server->ops->action(server, final, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -494,7 +588,7 @@ static int obex_server_action_common(struct bt_obex *obex, bool final, uint16_t rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -502,17 +596,18 @@ static int obex_server_action_common(struct bt_obex *obex, bool final, uint16_t return err; } -static int obex_server_action(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_action(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { - return obex_server_action_common(obex, false, len, buf); + return obex_server_action_common(server, false, len, buf); } -static int obex_server_action_final(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_action_final(struct bt_obex_server *server, uint16_t len, + struct net_buf *buf) { - return obex_server_action_common(obex, true, len, buf); + return obex_server_action_common(server, true, len, buf); } -static int obex_server_session(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_session(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; uint8_t rsp_code; @@ -526,7 +621,7 @@ static int obex_server_session(struct bt_obex *obex, uint16_t len, struct net_bu goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; @@ -536,7 +631,7 @@ static int obex_server_session(struct bt_obex *obex, uint16_t len, struct net_bu rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -546,7 +641,7 @@ static int obex_server_session(struct bt_obex *obex, uint16_t len, struct net_bu rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -554,7 +649,7 @@ static int obex_server_session(struct bt_obex *obex, uint16_t len, struct net_bu return err; } -static int obex_server_abort(struct bt_obex *obex, uint16_t len, struct net_buf *buf) +static int obex_server_abort(struct bt_obex_server *server, uint16_t len, struct net_buf *buf) { struct bt_obex_rsp_hdr *rsp_hdr; uint8_t rsp_code; @@ -568,25 +663,25 @@ static int obex_server_abort(struct bt_obex *obex, uint16_t len, struct net_buf goto failed; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); rsp_code = BT_OBEX_RSP_CODE_FORBIDDEN; goto failed; } - if (!obex->server_ops->abort) { + if (server->ops->abort == NULL) { LOG_WRN("Abort req handling not implemented"); rsp_code = BT_OBEX_RSP_CODE_NOT_IMPL; goto failed; } - atomic_set(&obex->_opcode, BT_OBEX_OPCODE_ABORT); + atomic_set(&server->_opcode, BT_OBEX_OPCODE_ABORT); - obex->server_ops->abort(obex, buf); + server->ops->abort(server, buf); return 0; failed: - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("Cannot allocate buffer"); return -ENOBUFS; @@ -596,7 +691,7 @@ static int obex_server_abort(struct bt_obex *obex, uint16_t len, struct net_buf rsp_hdr->code = rsp_code; rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { net_buf_unref(buf); } @@ -605,25 +700,79 @@ static int obex_server_abort(struct bt_obex *obex, uint16_t len, struct net_buf } struct server_handler server_handler[] = { - {BT_OBEX_OPCODE_CONNECT, obex_server_connect}, - {BT_OBEX_OPCODE_DISCONN, obex_server_disconn}, - {BT_OBEX_OPCODE_PUT, obex_server_put}, - {BT_OBEX_OPCODE_PUT_F, obex_server_put_final}, - {BT_OBEX_OPCODE_GET, obex_server_get}, - {BT_OBEX_OPCODE_GET_F, obex_server_get_final}, - {BT_OBEX_OPCODE_SETPATH, obex_server_setpath}, - {BT_OBEX_OPCODE_ACTION, obex_server_action}, - {BT_OBEX_OPCODE_ACTION_F, obex_server_action_final}, - {BT_OBEX_OPCODE_SESSION, obex_server_session}, - {BT_OBEX_OPCODE_ABORT, obex_server_abort}, + {BT_OBEX_OPCODE_CONNECT, sizeof(struct bt_obex_conn_req_hdr), obex_server_connect}, + {BT_OBEX_OPCODE_DISCONN, 0, obex_server_disconn}, + {BT_OBEX_OPCODE_PUT, 0, obex_server_put}, + {BT_OBEX_OPCODE_PUT_F, 0, obex_server_put_final}, + {BT_OBEX_OPCODE_GET, 0, obex_server_get}, + {BT_OBEX_OPCODE_GET_F, 0, obex_server_get_final}, + {BT_OBEX_OPCODE_SETPATH, sizeof(struct bt_obex_setpath_req_hdr), obex_server_setpath}, + {BT_OBEX_OPCODE_ACTION, 0, obex_server_action}, + {BT_OBEX_OPCODE_ACTION_F, 0, obex_server_action_final}, + {BT_OBEX_OPCODE_SESSION, 0, obex_server_session}, + {BT_OBEX_OPCODE_ABORT, 0, obex_server_abort}, }; +static struct bt_obex_server *obex_get_server_from_conn_id(struct bt_obex *obex, uint32_t conn_id) +{ + struct bt_obex_server *server, *next; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obex->_servers, server, next, _node) { + if (server->_conn_id == conn_id) { + return server; + } + } + return NULL; +} + +static struct bt_obex_server *obex_get_server_from_uuid(struct bt_obex *obex, struct bt_uuid *uuid) +{ + struct bt_obex_server *server, *next; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obex->_servers, server, next, _node) { + if (server->uuid != NULL && bt_uuid_cmp(&server->uuid->uuid, uuid) == 0) { + return server; + } + } + return NULL; +} + +static struct bt_obex_server *obex_get_server_with_state(struct bt_obex *obex, + enum bt_obex_state state) +{ + struct bt_obex_server *server, *next; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obex->_servers, server, next, _node) { + if (server->uuid == NULL && atomic_get(&server->_state) == state) { + return server; + } + } + return NULL; +} + +static uint16_t obex_server_get_hdr_len(uint8_t opcode) +{ + ARRAY_FOR_EACH(server_handler, i) { + if (server_handler[i].opcode == opcode) { + return server_handler[i].min_len; + } + } + + return 0; +} + static int obex_server_recv(struct bt_obex *obex, struct net_buf *buf) { - struct bt_obex_req_hdr *hdr; + struct bt_obex_req_hdr *hdr = NULL; struct bt_obex_rsp_hdr *rsp_hdr; + struct bt_obex_server *server = NULL; + uint32_t conn_id; uint16_t len; + uint16_t target_len; + const uint8_t *target_data; + uint16_t min_hdr_len; int err; + enum bt_obex_rsp_code rsp_code = BT_OBEX_RSP_CODE_BAD_REQ; if (buf->len < sizeof(*hdr)) { LOG_WRN("Too small header size (%d < %d)", buf->len, sizeof(*hdr)); @@ -632,30 +781,95 @@ static int obex_server_recv(struct bt_obex *obex, struct net_buf *buf) hdr = net_buf_pull_mem(buf, sizeof(*hdr)); len = sys_be16_to_cpu(hdr->len); + min_hdr_len = obex_server_get_hdr_len(hdr->code); + + if (min_hdr_len > buf->len) { + LOG_WRN("Malformed request (%d > %d)", min_hdr_len, buf->len); + goto failed; + } + + if (hdr->code == BT_OBEX_OPCODE_CONNECT) { + struct net_buf_simple_state state; - for (size_t i = 0; i < ARRAY_SIZE(server_handler); i++) { + net_buf_simple_save(&buf->b, &state); + net_buf_pull(buf, min_hdr_len); + err = bt_obex_get_header_target(buf, &target_len, &target_data); + net_buf_simple_restore(&buf->b, &state); + if (err == 0) { + union bt_obex_uuid u; + + err = bt_obex_make_uuid(&u, target_data, target_len); + if (err != 0) { + LOG_WRN("Unsupported target header len %u", target_len); + goto failed; + } + + server = obex_get_server_from_uuid(obex, &u.uuid); + } + + if (server == NULL) { + server = obex_get_server_with_state(obex, BT_OBEX_DISCONNECTED); + } + } else { + struct net_buf_simple_state state; + + net_buf_simple_save(&buf->b, &state); + net_buf_pull(buf, min_hdr_len); + err = bt_obex_get_header_conn_id(buf, &conn_id); + net_buf_simple_restore(&buf->b, &state); + if (err == 0) { + server = obex_get_server_from_conn_id(obex, conn_id); + } else if (atomic_ptr_get(&obex->_active_server) != NULL) { + server = atomic_ptr_get(&obex->_active_server); + } else if (sys_slist_len(&obex->_servers) == 1) { + /* Only if the count of servers is 1, use it as default. */ + server = OBEX_SERVER(sys_slist_peek_head(&obex->_servers)); + } + } + + if (server == NULL) { + LOG_WRN("No server found from %p", obex); + rsp_code = BT_OBEX_RSP_CODE_NOT_FOUND; + goto failed; + } + + ARRAY_FOR_EACH(server_handler, i) { if (server_handler[i].opcode == hdr->code) { - err = server_handler[i].handler(obex, len - sizeof(*hdr), buf); - if (err) { + err = server_handler[i].handler(server, len - sizeof(*hdr), buf); + if (err != 0) { + atomic_ptr_clear(&obex->_active_server); LOG_WRN("Handler err %d", err); + } else { + /* Save the server */ + atomic_ptr_set(&obex->_active_server, server); } return err; } } failed: - LOG_WRN("Unsupported request"); + LOG_WRN("Failed to process request"); buf = obex_alloc_buf(obex); if (!buf) { + LOG_ERR("Failed to allocate buf"); return -ENOBUFS; } rsp_hdr = (void *)net_buf_add(buf, sizeof(*rsp_hdr)); - rsp_hdr->code = BT_OBEX_RSP_CODE_BAD_REQ; + rsp_hdr->code = rsp_code; + if (hdr != NULL && hdr->code == BT_OBEX_OPCODE_CONNECT) { + struct bt_obex_conn_rsp_hdr *conn_rsp_hdr; + + conn_rsp_hdr = (void *)net_buf_add(buf, sizeof(*conn_rsp_hdr)); + conn_rsp_hdr->flags = 0; + conn_rsp_hdr->mopl = sys_cpu_to_be16(obex->rx.mtu); + conn_rsp_hdr->version = BT_OBEX_VERSION; + } rsp_hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(obex, BT_OBEX_MIN_MTU, buf); if (err) { + LOG_ERR("Failed to send obex rep err %d", err); net_buf_unref(buf); } @@ -664,10 +878,11 @@ static int obex_server_recv(struct bt_obex *obex, struct net_buf *buf) struct client_handler { uint8_t opcode; - int (*handler)(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, struct net_buf *buf); + int (*handler)(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, + struct net_buf *buf); }; -static int obex_client_connect(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_connect(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { struct bt_obex_conn_rsp_hdr *rsp_conn_hdr; @@ -677,64 +892,124 @@ static int obex_client_connect(struct bt_obex *obex, uint8_t rsp_code, uint16_t LOG_DBG(""); - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTING) { - return -EINVAL; + if (atomic_get(&client->_state) != BT_OBEX_CONNECTING) { + LOG_WRN("Invalid connection state %u", (uint8_t)atomic_get(&client->_state)); + goto failed; } if ((len != buf->len) || (buf->len < sizeof(*rsp_conn_hdr))) { LOG_WRN("Invalid packet size"); goto failed; - } else { - version = net_buf_pull_u8(buf); - flags = net_buf_pull_u8(buf); - mopl = net_buf_pull_be16(buf); + } + + version = net_buf_pull_u8(buf); + flags = net_buf_pull_u8(buf); + mopl = net_buf_pull_be16(buf); + + LOG_DBG("version %u, flags %u, mopl %u", version, flags, mopl); + + if (atomic_test_bit(&client->_flags, BT_OBEX_HAS_TARGET) && + rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + union bt_obex_uuid u; + int err; + uint16_t who_len; + const uint8_t *who_data; + + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO) != + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + LOG_WRN("Missing required who or connect id headers"); + goto failed; + } + + if (atomic_test_bit(&client->_flags, BT_OBEX_HAS_TARGET) != + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO)) { + LOG_WRN("Missing required who header"); + goto failed; + } + + err = bt_obex_get_header_conn_id(buf, &client->_conn_id); + if (err != 0) { + LOG_WRN("Invalid connection id header"); + goto failed; + } - LOG_DBG("version %u, flags %u, mopl %u", version, flags, mopl); + err = bt_obex_get_header_who(buf, &who_len, &who_data); + if (err != 0) { + LOG_WRN("Invalid who header"); + goto failed; + } + + err = bt_obex_make_uuid(&u, who_data, who_len); + if (err != 0) { + LOG_WRN("Unsupported who header len %u", who_len); + goto failed; + } - if (mopl < BT_OBEX_MIN_MTU) { - LOG_WRN("Invalid MTU length (%d < %d)", mopl, BT_OBEX_MIN_MTU); + if (bt_uuid_cmp(&u.uuid, &client->_target.uuid) != 0) { + LOG_WRN("Who header is mismatched with target"); goto failed; } - obex->tx.mopl = mopl; } - atomic_set(&obex->_state, + if (mopl < BT_OBEX_MIN_MTU) { + LOG_WRN("Invalid MOPL (%d < %d)", mopl, BT_OBEX_MIN_MTU); + goto failed; + } + + if (mopl > client->obex->tx.mtu) { + LOG_WRN("MOPL exceeds MTU (%d > %d)", mopl, client->obex->tx.mtu); + goto failed; + } + + client->tx.mopl = mopl; + + atomic_set(&client->_state, rsp_code == BT_OBEX_RSP_CODE_SUCCESS ? BT_OBEX_CONNECTED : BT_OBEX_DISCONNECTED); - if (obex->client_ops->connect) { - obex->client_ops->connect(obex, rsp_code, version, mopl, buf); + if (atomic_get(&client->_state) == BT_OBEX_DISCONNECTED) { + sys_slist_find_and_remove(&client->obex->_clients, &client->_node); + } + + if (client->ops->connect) { + client->ops->connect(client, rsp_code, version, mopl, buf); } return 0; failed: LOG_WRN("Disconnect transport"); - return obex_transport_disconn(obex); + return obex_transport_disconn(client->obex); } -static int obex_client_disconn(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_disconn(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); - if (atomic_get(&obex->_state) != BT_OBEX_DISCONNECTING) { + if (atomic_get(&client->_state) != BT_OBEX_DISCONNECTING) { return -EINVAL; } - atomic_set(&obex->_state, + atomic_set(&client->_state, rsp_code == BT_OBEX_RSP_CODE_SUCCESS ? BT_OBEX_DISCONNECTED : BT_OBEX_CONNECTED); - if (obex->client_ops->disconnect) { - obex->client_ops->disconnect(obex, rsp_code, buf); + if (atomic_get(&client->_state) == BT_OBEX_DISCONNECTED) { + sys_slist_find_and_remove(&client->obex->_clients, &client->_node); + } + + if (client->ops->disconnect) { + client->ops->disconnect(client, rsp_code, buf); } return 0; } -static int obex_client_put_common(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_put_common(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); @@ -744,37 +1019,38 @@ static int obex_client_put_common(struct bt_obex *obex, uint8_t rsp_code, uint16 return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (!obex->client_ops->put) { + if (client->ops->put == NULL) { LOG_WRN("Put rsp handling not implemented"); return -ENOTSUP; } if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); } - obex->client_ops->put(obex, rsp_code, buf); + client->ops->put(client, rsp_code, buf); return 0; } -static int obex_client_put(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_put(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_put_common(obex, rsp_code, len, buf); + return obex_client_put_common(client, rsp_code, len, buf); } -static int obex_client_put_final(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_put_final(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_put_common(obex, rsp_code, len, buf); + return obex_client_put_common(client, rsp_code, len, buf); } -static int obex_client_get_common(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_get_common(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); @@ -784,37 +1060,38 @@ static int obex_client_get_common(struct bt_obex *obex, uint8_t rsp_code, uint16 return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (!obex->client_ops->get) { + if (client->ops->get == NULL) { LOG_WRN("Get rsp handling not implemented"); return -ENOTSUP; } if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); } - obex->client_ops->get(obex, rsp_code, buf); + client->ops->get(client, rsp_code, buf); return 0; } -static int obex_client_get(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_get(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_get_common(obex, rsp_code, len, buf); + return obex_client_get_common(client, rsp_code, len, buf); } -static int obex_client_get_final(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_get_final(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_get_common(obex, rsp_code, len, buf); + return obex_client_get_common(client, rsp_code, len, buf); } -static int obex_client_setpath(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_setpath(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); @@ -824,25 +1101,26 @@ static int obex_client_setpath(struct bt_obex *obex, uint8_t rsp_code, uint16_t return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (!obex->client_ops->setpath) { + if (client->ops->setpath == NULL) { LOG_WRN("Setpath rsp handling not implemented"); return -ENOTSUP; } if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); } - obex->client_ops->setpath(obex, rsp_code, buf); + client->ops->setpath(client, rsp_code, buf); return 0; } -static int obex_client_action_common(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_action_common(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); @@ -852,37 +1130,38 @@ static int obex_client_action_common(struct bt_obex *obex, uint8_t rsp_code, uin return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (!obex->client_ops->action) { + if (client->ops->action == NULL) { LOG_WRN("Setpath rsp handling not implemented"); return -ENOTSUP; } if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); } - obex->client_ops->action(obex, rsp_code, buf); + client->ops->action(client, rsp_code, buf); return 0; } -static int obex_client_action(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_action(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_action_common(obex, rsp_code, len, buf); + return obex_client_action_common(client, rsp_code, len, buf); } -static int obex_client_action_final(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_action_final(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { - return obex_client_action_common(obex, rsp_code, len, buf); + return obex_client_action_common(client, rsp_code, len, buf); } -static int obex_client_session(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_session(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { return -ENOTSUP; @@ -890,7 +1169,7 @@ static int obex_client_session(struct bt_obex *obex, uint8_t rsp_code, uint16_t static struct client_handler *obex_client_find_handler(uint8_t opcode); -static int obex_client_abort(struct bt_obex *obex, uint8_t rsp_code, uint16_t len, +static int obex_client_abort(struct bt_obex_client *client, uint8_t rsp_code, uint16_t len, struct net_buf *buf) { LOG_DBG(""); @@ -900,7 +1179,7 @@ static int obex_client_abort(struct bt_obex *obex, uint8_t rsp_code, uint16_t le return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } @@ -909,9 +1188,9 @@ static int obex_client_abort(struct bt_obex *obex, uint8_t rsp_code, uint16_t le struct client_handler *handler; int err = -EINVAL; - handler = obex_client_find_handler(atomic_get(&obex->_pre_opcode)); + handler = obex_client_find_handler(atomic_get(&client->_pre_opcode)); if (handler) { - err = handler->handler(obex, rsp_code, len, buf); + err = handler->handler(client, rsp_code, len, buf); if (err) { LOG_WRN("Handler err %d", err); } @@ -919,23 +1198,32 @@ static int obex_client_abort(struct bt_obex *obex, uint8_t rsp_code, uint16_t le return err; } - if (!obex->client_ops->abort) { + if (client->ops->abort == NULL) { LOG_WRN("Abort rsp handling not implemented"); return -ENOTSUP; } - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); if (rsp_code != BT_OBEX_RSP_CODE_SUCCESS) { LOG_WRN("Disconnect transport"); - return obex_transport_disconn(obex); + return obex_transport_disconn(client->obex); } - obex->client_ops->abort(obex, rsp_code, buf); + client->ops->abort(client, rsp_code, buf); return 0; } +#define BT_OBEX_REQUEST_CODE_START 0x00 +#define BT_OBEX_REQUEST_CODE_END 0x0f +#define BT_OBEX_REQUEST_CODE_F_BIT BIT(7) +#define IN_OBEX_REQUEST_RANGE(code) \ + ((code) >= BT_OBEX_REQUEST_CODE_START && (code) <= BT_OBEX_REQUEST_CODE_END) +#define IS_OBEX_REQUEST(code) (IN_OBEX_REQUEST_RANGE((code) & ~(BT_OBEX_REQUEST_CODE_F_BIT)) || \ + ((code) == BT_OBEX_OPCODE_ABORT)) + struct client_handler client_handler[] = { {BT_OBEX_OPCODE_CONNECT, obex_client_connect}, {BT_OBEX_OPCODE_DISCONN, obex_client_disconn}, @@ -952,7 +1240,7 @@ struct client_handler client_handler[] = { static struct client_handler *obex_client_find_handler(uint8_t opcode) { - for (size_t i = 0; i < ARRAY_SIZE(client_handler); i++) { + ARRAY_FOR_EACH(client_handler, i) { if (client_handler[i].opcode == opcode) { return &client_handler[i]; } @@ -964,6 +1252,7 @@ static int obex_client_recv(struct bt_obex *obex, struct net_buf *buf) { struct client_handler *handler; struct bt_obex_rsp_hdr *hdr; + struct bt_obex_client *client = atomic_ptr_get(&obex->_active_client); uint16_t len; int err; @@ -972,11 +1261,16 @@ static int obex_client_recv(struct bt_obex *obex, struct net_buf *buf) return -EINVAL; } + if (client == NULL) { + LOG_WRN("No executing OBEX request"); + return -EINVAL; + } + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); len = sys_be16_to_cpu(hdr->len); - handler = obex_client_find_handler(atomic_get(&obex->_opcode)); + handler = obex_client_find_handler(atomic_get(&client->_opcode)); if (handler) { - err = handler->handler(obex, hdr->code, len - sizeof(*hdr), buf); + err = handler->handler(client, hdr->code, len - sizeof(*hdr), buf); if (err) { LOG_WRN("Handler err %d", err); } @@ -989,7 +1283,7 @@ static int obex_client_recv(struct bt_obex *obex, struct net_buf *buf) int bt_obex_transport_connected(struct bt_obex *obex) { - if (!obex) { + if (obex == NULL) { return -EINVAL; } @@ -1003,24 +1297,32 @@ int bt_obex_transport_connected(struct bt_obex *obex) return -EINVAL; } - atomic_clear(&obex->_opcode); - atomic_clear(&obex->_pre_opcode); - atomic_set(&obex->_state, BT_OBEX_DISCONNECTED); + atomic_ptr_clear(&obex->_active_client); return 0; } int bt_obex_transport_disconnected(struct bt_obex *obex) { - if (obex) { - atomic_clear(&obex->_opcode); - atomic_clear(&obex->_pre_opcode); - atomic_set(&obex->_state, BT_OBEX_DISCONNECTED); - obex->_transport_ops = NULL; - return 0; + struct bt_obex_client *client, *cnext; + struct bt_obex_server *server, *snext; + + obex->_transport_ops = NULL; + atomic_ptr_clear(&obex->_active_client); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obex->_clients, client, cnext, _node) { + atomic_clear(&client->_opcode); + atomic_clear(&client->_pre_opcode); + atomic_set(&client->_state, BT_OBEX_DISCONNECTED); } - return -EINVAL; + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obex->_servers, server, snext, _node) { + atomic_clear(&server->_opcode); + atomic_set(&server->_state, BT_OBEX_DISCONNECTED); + } + + sys_slist_init(&obex->_clients); + return 0; } int bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_ops *ops) @@ -1036,40 +1338,109 @@ int bt_obex_reg_transport(struct bt_obex *obex, const struct bt_obex_transport_o int bt_obex_recv(struct bt_obex *obex, struct net_buf *buf) { - if (!obex || !buf) { + struct bt_obex_comm_hdr *hdr; + + if (obex == NULL || buf == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (obex->server_ops) { - return obex_server_recv(obex, buf); - } else if (obex->client_ops) { + if (buf->len < sizeof(*hdr)) { + LOG_WRN("Malformed packet"); + return -EINVAL; + } + + hdr = (struct bt_obex_comm_hdr *)buf->data; + if (!IS_OBEX_REQUEST(hdr->code)) { return obex_client_recv(obex, buf); } - LOG_WRN("Unknown role of OBEX %p", obex); - return -EINVAL; + return obex_server_recv(obex, buf); +} + +int bt_obex_server_register(struct bt_obex_server *server, const struct bt_uuid_128 *uuid) +{ + if (server == NULL || server->obex == NULL) { + LOG_ERR("Invalid parameter"); + return -EINVAL; + } + + if (server->ops == NULL || server->ops->connect == NULL || + server->ops->disconnect == NULL) { + LOG_ERR("Invalid OBEX role"); + return -EINVAL; + } + + if (atomic_get(&server->_state) != BT_OBEX_DISCONNECTED) { + LOG_ERR("Invalid state, connect is %u", (uint8_t)atomic_get(&server->_state)); + return -EINPROGRESS; + } + + if (sys_slist_find(&server->obex->_servers, &server->_node, NULL)) { + LOG_ERR("server %p has been registered", server); + return -EALREADY; + } + + if (!sys_slist_is_empty(&server->obex->_servers) && server->uuid == NULL) { + LOG_ERR("UUID of server %p should not be NULL", server); + return -EINVAL; + } + + server->rx.mopl = BT_OBEX_MIN_MTU; + /* Set MOPL of TX to MTU by default to avoid the OBEX connect rsp cannot be sent. */ + server->tx.mopl = BT_OBEX_MIN_MTU; + + server->uuid = uuid; + + sys_slist_append(&server->obex->_servers, &server->_node); + + return 0; +} + +int bt_obex_server_unregister(struct bt_obex_server *server) +{ + if (server == NULL || server->obex == NULL) { + LOG_ERR("Invalid parameter"); + return -EINVAL; + } + + if (!sys_slist_find(&server->obex->_servers, &server->_node, NULL)) { + LOG_ERR("server %p has not been registered", server); + return -EALREADY; + } + + if (atomic_get(&server->_state) != BT_OBEX_DISCONNECTED) { + LOG_ERR("Invalid state, connect is %u", (uint8_t)atomic_get(&server->_state)); + return -EINPROGRESS; + } + + sys_slist_find_and_remove(&server->obex->_servers, &server->_node); + + return 0; } -int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) +int bt_obex_connect(struct bt_obex_client *client, uint16_t mopl, struct net_buf *buf) { struct bt_obex_conn_req_hdr *req_hdr; struct bt_obex_req_hdr *hdr; int err; bool allocated = false; + uint16_t target_len; + const uint8_t *target_data; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL || client->ops->connect == NULL || + client->ops->disconnect == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (mopl > obex->rx.mtu) { - mopl = obex->rx.mtu; + if (mopl > client->obex->rx.mtu) { + mopl = client->obex->rx.mtu; } if (mopl < BT_OBEX_MIN_MTU) { @@ -1077,20 +1448,46 @@ int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_DISCONNECTED) { - LOG_WRN("Invalid state, connect is %s", - atomic_get(&obex->_state) == BT_OBEX_CONNECTING ? "ongoing" - : "established"); + if (atomic_get(&client->_state) != BT_OBEX_DISCONNECTED) { + LOG_WRN("Invalid state, connect is %u", (uint8_t)atomic_get(&client->_state)); return -EINPROGRESS; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_CONNECT)) { + if (!sys_slist_is_empty(&client->obex->_clients) && + !bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TARGET)) { + LOG_ERR("The Header target should be added"); + return -EINVAL; + } + + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + + if (!atomic_cas(&client->_opcode, 0, BT_OBEX_OPCODE_CONNECT)) { LOG_WRN("Operation inprogress"); return -EBUSY; } - if (!buf) { - buf = obex_alloc_buf(obex); + atomic_set_bit_to(&client->_flags, BT_OBEX_HAS_TARGET, + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_TARGET)); + + if (atomic_test_bit(&client->_flags, BT_OBEX_HAS_TARGET)) { + err = bt_obex_get_header_target(buf, &target_len, &target_data); + if (err != 0) { + LOG_ERR("Invalid target header"); + return err; + } + + err = bt_obex_make_uuid(&client->_target, target_data, target_len); + if (err != 0) { + LOG_WRN("Unsupported target header len %u", target_len); + return err; + } + } + + if (buf == NULL) { + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1098,8 +1495,14 @@ int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) allocated = true; } - obex->rx.mopl = mopl; - atomic_set(&obex->_state, BT_OBEX_CONNECTING); + client->rx.mopl = mopl; + /* Set MOPL of TX to MTU by default to avoid the OBEX connect req cannot be sent. */ + client->tx.mopl = BT_OBEX_MIN_MTU; + atomic_set(&client->_state, BT_OBEX_CONNECTING); + + if (!sys_slist_find(&client->obex->_clients, &client->_node, NULL)) { + sys_slist_append(&client->obex->_clients, &client->_node); + } req_hdr = net_buf_push(buf, sizeof(*req_hdr)); req_hdr->flags = 0; @@ -1109,10 +1512,12 @@ int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) hdr->code = BT_OBEX_OPCODE_CONNECT; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_set(&obex->_state, BT_OBEX_DISCONNECTED); - atomic_clear(&obex->_opcode); + atomic_set(&client->_state, BT_OBEX_DISCONNECTED); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); + sys_slist_find_and_remove(&client->obex->_clients, &client->_node); if (allocated) { net_buf_unref(buf); @@ -1121,26 +1526,30 @@ int bt_obex_connect(struct bt_obex *obex, uint16_t mopl, struct net_buf *buf) return err; } -int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, struct net_buf *buf) +int bt_obex_connect_rsp(struct bt_obex_server *server, uint8_t rsp_code, uint16_t mopl, + struct net_buf *buf) { struct bt_obex_conn_rsp_hdr *rsp_hdr; struct bt_obex_rsp_hdr *hdr; int err; atomic_val_t old_state; + uint16_t who_len; + const uint8_t *who_data; + bool allocated = false; - if (!obex || (rsp_code == BT_OBEX_RSP_CODE_CONTINUE)) { + if (server == NULL || server->obex == NULL || (rsp_code == BT_OBEX_RSP_CODE_CONTINUE)) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (mopl > obex->rx.mtu) { - mopl = obex->rx.mtu; + if (mopl > server->obex->rx.mtu) { + mopl = server->obex->rx.mtu; } if (mopl < BT_OBEX_MIN_MTU) { @@ -1148,18 +1557,59 @@ int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, s return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTING) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTING) { LOG_WRN("Invalid state"); return -ENOTCONN; } - if (atomic_get(&obex->_opcode) != BT_OBEX_OPCODE_CONNECT) { + if (atomic_get(&server->_opcode) != BT_OBEX_OPCODE_CONNECT) { LOG_WRN("Invalid response"); return -EINVAL; } + if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { + if (bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO) != + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_CONN_ID)) { + LOG_ERR("Missing required who or connect id headers"); + return -EINVAL; + } + + if (atomic_test_bit(&server->_flags, BT_OBEX_HAS_TARGET) != + bt_obex_has_header(buf, BT_OBEX_HEADER_ID_WHO)) { + LOG_ERR("Missing required who header"); + return -EINVAL; + } + + if (atomic_test_bit(&server->_flags, BT_OBEX_HAS_TARGET)) { + union bt_obex_uuid u; + + err = bt_obex_get_header_conn_id(buf, &server->_conn_id); + if (err != 0) { + LOG_ERR("Invalid connection id header"); + return err; + } + + err = bt_obex_get_header_who(buf, &who_len, &who_data); + if (err != 0) { + LOG_ERR("Invalid who header"); + return err; + } + + err = bt_obex_make_uuid(&u, who_data, who_len); + if (err != 0) { + LOG_WRN("Unsupported who header len %u", who_len); + return err; + } + + if (bt_uuid_cmp(&u.uuid, &server->_target.uuid) != 0) { + LOG_WRN("Who header is mismatched with target"); + return -EINVAL; + } + } + } + if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1167,11 +1617,11 @@ int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, s allocated = true; } - obex->rx.mopl = mopl; + server->rx.mopl = mopl; if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { - old_state = atomic_set(&obex->_state, BT_OBEX_CONNECTED); + old_state = atomic_set(&server->_state, BT_OBEX_CONNECTED); } else { - old_state = atomic_set(&obex->_state, BT_OBEX_DISCONNECTED); + old_state = atomic_set(&server->_state, BT_OBEX_DISCONNECTED); } rsp_hdr = net_buf_push(buf, sizeof(*rsp_hdr)); @@ -1182,47 +1632,53 @@ int bt_obex_connect_rsp(struct bt_obex *obex, uint8_t rsp_code, uint16_t mopl, s hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { - atomic_set(&obex->_state, old_state); + atomic_set(&server->_state, old_state); if (allocated) { net_buf_unref(buf); } } else { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } return err; } -int bt_obex_disconnect(struct bt_obex *obex, struct net_buf *buf) +int bt_obex_disconnect(struct bt_obex_client *client, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; int err; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_DISCONN)) { + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + + if (!atomic_cas(&client->_opcode, 0, BT_OBEX_OPCODE_DISCONN)) { LOG_WRN("Operation inprogress"); return -EBUSY; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1234,48 +1690,49 @@ int bt_obex_disconnect(struct bt_obex *obex, struct net_buf *buf) hdr->code = BT_OBEX_OPCODE_DISCONN; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); if (allocated) { net_buf_unref(buf); } } else { - atomic_set(&obex->_state, BT_OBEX_DISCONNECTING); + atomic_set(&client->_state, BT_OBEX_DISCONNECTING); } return err; } -int bt_obex_disconnect_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_disconnect_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; atomic_val_t old_state; bool allocated = false; - if (!obex || (rsp_code == BT_OBEX_RSP_CODE_CONTINUE)) { + if (server == NULL || server->obex == NULL || (rsp_code == BT_OBEX_RSP_CODE_CONTINUE)) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_DISCONNECTING) { + if (atomic_get(&server->_state) != BT_OBEX_DISCONNECTING) { LOG_WRN("Invalid state"); return -EINVAL; } - if (atomic_get(&obex->_opcode) != BT_OBEX_OPCODE_DISCONN) { + if (atomic_get(&server->_opcode) != BT_OBEX_OPCODE_DISCONN) { LOG_WRN("Invalid response"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1284,53 +1741,62 @@ int bt_obex_disconnect_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_bu } if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) { - old_state = atomic_set(&obex->_state, BT_OBEX_DISCONNECTED); + old_state = atomic_set(&server->_state, BT_OBEX_DISCONNECTED); } else { - old_state = atomic_set(&obex->_state, BT_OBEX_CONNECTED); + old_state = atomic_set(&server->_state, BT_OBEX_CONNECTED); } hdr = net_buf_push(buf, sizeof(*hdr)); hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (err) { - atomic_set(&obex->_state, old_state); + atomic_set(&server->_state, old_state); if (allocated) { net_buf_unref(buf); } } else { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } return err; } -int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf) +int bt_obex_put(struct bt_obex_client *client, bool final, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; + struct bt_obex_client *active_client; int err; uint8_t req_code; uint8_t opcode; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } + active_client = atomic_ptr_get(&client->obex->_active_client); + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client) && + (active_client != client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1338,10 +1804,10 @@ int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf) allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&client->_opcode); req_code = final ? BT_OBEX_OPCODE_PUT_F : BT_OBEX_OPCODE_PUT; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { + if (!atomic_cas(&client->_opcode, 0, req_code)) { if ((opcode != BT_OBEX_OPCODE_PUT_F) && (opcode != BT_OBEX_OPCODE_PUT)) { LOG_WRN("Operation inprogress"); return -EBUSY; @@ -1353,7 +1819,7 @@ int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf) } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&client->_opcode, opcode, req_code); } } @@ -1361,9 +1827,10 @@ int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf) hdr->code = req_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_set(&obex->_opcode, opcode); + atomic_set(&client->_opcode, opcode); + atomic_ptr_set(&client->obex->_active_client, active_client); if (allocated) { net_buf_unref(buf); @@ -1372,30 +1839,30 @@ int bt_obex_put(struct bt_obex *obex, bool final, struct net_buf *buf) return err; } -int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_put_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; uint8_t opcode; bool allocated = false; - if (!obex) { + if (server == NULL || server->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1403,7 +1870,7 @@ int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_PUT_F) && (opcode != BT_OBEX_OPCODE_PUT)) { LOG_WRN("Invalid response"); return -EINVAL; @@ -1418,10 +1885,11 @@ int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (!err) { if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } } else { if (allocated) { @@ -1431,31 +1899,39 @@ int bt_obex_put_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) return err; } -int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf) +int bt_obex_get(struct bt_obex_client *client, bool final, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; + struct bt_obex_client *active_client; int err; uint8_t req_code; uint8_t opcode; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } + active_client = atomic_ptr_get(&client->obex->_active_client); + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client) && + (active_client != client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1463,10 +1939,10 @@ int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf) allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&client->_opcode); req_code = final ? BT_OBEX_OPCODE_GET_F : BT_OBEX_OPCODE_GET; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { + if (!atomic_cas(&client->_opcode, 0, req_code)) { if ((opcode != BT_OBEX_OPCODE_GET_F) && (opcode != BT_OBEX_OPCODE_GET)) { LOG_WRN("Operation inprogress"); return -EBUSY; @@ -1478,7 +1954,7 @@ int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf) } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&client->_opcode, opcode, req_code); } } @@ -1486,9 +1962,10 @@ int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf) hdr->code = req_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_set(&obex->_opcode, opcode); + atomic_set(&client->_opcode, opcode); + atomic_ptr_set(&client->obex->_active_client, active_client); if (allocated) { net_buf_unref(buf); @@ -1497,30 +1974,30 @@ int bt_obex_get(struct bt_obex *obex, bool final, struct net_buf *buf) return err; } -int bt_obex_get_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_get_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; uint8_t opcode; bool allocated = false; - if (!obex) { + if (server == NULL || server->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1528,7 +2005,7 @@ int bt_obex_get_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_GET_F) && (opcode != BT_OBEX_OPCODE_GET)) { LOG_WRN("Invalid response"); return -EINVAL; @@ -1543,10 +2020,11 @@ int bt_obex_get_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (!err) { if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } } else { if (allocated) { @@ -1575,41 +2053,48 @@ static bool obex_op_support_abort(uint8_t opcode) return false; } -int bt_obex_abort(struct bt_obex *obex, struct net_buf *buf) +int bt_obex_abort(struct bt_obex_client *client, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; + struct bt_obex_client *active_client; int err; uint8_t opcode; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } - if (!atomic_get(&obex->_opcode)) { + active_client = atomic_ptr_get(&client->obex->_active_client); + if (active_client != client) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + + if (!atomic_get(&client->_opcode)) { LOG_WRN("No operation is inprogress"); return -EINVAL; - } else if (atomic_get(&obex->_opcode) == BT_OBEX_OPCODE_ABORT) { + } else if (atomic_get(&client->_opcode) == BT_OBEX_OPCODE_ABORT) { LOG_WRN("Abort is inprogress"); return -EINPROGRESS; - } else if (!obex_op_support_abort((uint8_t)atomic_get(&obex->_opcode))) { - LOG_WRN("Opcode %02x cannot be aborted", (uint8_t)atomic_get(&obex->_opcode)); + } else if (!obex_op_support_abort((uint8_t)atomic_get(&client->_opcode))) { + LOG_WRN("Opcode %02x cannot be aborted", (uint8_t)atomic_get(&client->_opcode)); return -ENOTSUP; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1617,54 +2102,55 @@ int bt_obex_abort(struct bt_obex *obex, struct net_buf *buf) allocated = true; } - opcode = atomic_set(&obex->_opcode, BT_OBEX_OPCODE_ABORT); - atomic_clear(&obex->_pre_opcode); + opcode = atomic_set(&client->_opcode, BT_OBEX_OPCODE_ABORT); + atomic_clear(&client->_pre_opcode); hdr = net_buf_push(buf, sizeof(*hdr)); hdr->code = BT_OBEX_OPCODE_ABORT; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_set(&obex->_opcode, opcode); + atomic_set(&client->_opcode, opcode); + atomic_ptr_set(&client->obex->_active_client, active_client); if (allocated) { net_buf_unref(buf); } } else { - atomic_set(&obex->_pre_opcode, opcode); + atomic_set(&client->_pre_opcode, opcode); } return err; } -int bt_obex_abort_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_abort_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; bool allocated = false; - if (!obex) { + if (server == NULL || server->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (atomic_get(&obex->_opcode) != BT_OBEX_OPCODE_ABORT) { + if (atomic_get(&server->_opcode) != BT_OBEX_OPCODE_ABORT) { LOG_WRN("Invalid response"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1676,9 +2162,10 @@ int bt_obex_abort_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *bu hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (!err) { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } else { if (allocated) { net_buf_unref(buf); @@ -1687,35 +2174,40 @@ int bt_obex_abort_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *bu return err; } -int bt_obex_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf) +int bt_obex_setpath(struct bt_obex_client *client, uint8_t flags, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; struct bt_obex_setpath_req_hdr *req_hdr; int err; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } - if (!atomic_cas(&obex->_opcode, 0, BT_OBEX_OPCODE_SETPATH)) { + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + + if (!atomic_cas(&client->_opcode, 0, BT_OBEX_OPCODE_SETPATH)) { LOG_WRN("Operation inprogress"); return -EINPROGRESS; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1730,9 +2222,10 @@ int bt_obex_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf) hdr->code = BT_OBEX_OPCODE_SETPATH; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_clear(&obex->_opcode); + atomic_clear(&client->_opcode); + atomic_ptr_clear(&client->obex->_active_client); if (allocated) { net_buf_unref(buf); @@ -1741,34 +2234,34 @@ int bt_obex_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf) return err; } -int bt_obex_setpath_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_setpath_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; bool allocated = false; - if (!obex) { + if (server == NULL || server->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } - if (atomic_get(&obex->_opcode) != BT_OBEX_OPCODE_SETPATH) { + if (atomic_get(&server->_opcode) != BT_OBEX_OPCODE_SETPATH) { LOG_WRN("Invalid response"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1780,9 +2273,10 @@ int bt_obex_setpath_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf * hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (!err) { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } else { if (allocated) { net_buf_unref(buf); @@ -1791,31 +2285,39 @@ int bt_obex_setpath_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf * return err; } -int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf) +int bt_obex_action(struct bt_obex_client *client, bool final, struct net_buf *buf) { struct bt_obex_req_hdr *hdr; + struct bt_obex_client *active_client; int err; uint8_t req_code; uint8_t opcode; bool allocated = false; - if (!obex) { + if (client == NULL || client->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->client_ops) { + if (client->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&client->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -ENOTCONN; } + active_client = atomic_ptr_get(&client->obex->_active_client); + if (!atomic_ptr_cas(&client->obex->_active_client, NULL, client) && + (active_client != client)) { + LOG_WRN("One OBEX request is executing"); + return -EBUSY; + } + if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(client->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1823,10 +2325,10 @@ int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf) allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&client->_opcode); req_code = final ? BT_OBEX_OPCODE_ACTION_F : BT_OBEX_OPCODE_ACTION; - if (!atomic_cas(&obex->_opcode, 0, req_code)) { + if (!atomic_cas(&client->_opcode, 0, req_code)) { if ((opcode != BT_OBEX_OPCODE_ACTION_F) && (opcode != BT_OBEX_OPCODE_ACTION)) { LOG_WRN("Operation inprogress"); return -EBUSY; @@ -1838,7 +2340,7 @@ int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf) } if (opcode != req_code) { - atomic_cas(&obex->_opcode, opcode, req_code); + atomic_cas(&client->_opcode, opcode, req_code); } } @@ -1846,9 +2348,10 @@ int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf) hdr->code = req_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(client->obex, client->tx.mopl, buf); if (err) { - atomic_set(&obex->_opcode, opcode); + atomic_set(&client->_opcode, opcode); + atomic_ptr_set(&client->obex->_active_client, active_client); if (allocated) { net_buf_unref(buf); @@ -1857,30 +2360,30 @@ int bt_obex_action(struct bt_obex *obex, bool final, struct net_buf *buf) return err; } -int bt_obex_action_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +int bt_obex_action_rsp(struct bt_obex_server *server, uint8_t rsp_code, struct net_buf *buf) { struct bt_obex_rsp_hdr *hdr; int err; uint8_t opcode; bool allocated = false; - if (!obex) { + if (server == NULL || server->obex == NULL) { LOG_WRN("Invalid parameter"); return -EINVAL; } - if (!obex->server_ops) { + if (server->ops == NULL) { LOG_WRN("Invalid OBEX role"); return -EINVAL; } - if (atomic_get(&obex->_state) != BT_OBEX_CONNECTED) { + if (atomic_get(&server->_state) != BT_OBEX_CONNECTED) { LOG_WRN("Invalid state, connect is not established"); return -EINVAL; } if (!buf) { - buf = obex_alloc_buf(obex); + buf = obex_alloc_buf(server->obex); if (!buf) { LOG_WRN("No buffers"); return -ENOBUFS; @@ -1888,7 +2391,7 @@ int bt_obex_action_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *b allocated = true; } - opcode = atomic_get(&obex->_opcode); + opcode = atomic_get(&server->_opcode); if ((opcode != BT_OBEX_OPCODE_ACTION_F) && (opcode != BT_OBEX_OPCODE_ACTION)) { LOG_WRN("Invalid response"); return -EINVAL; @@ -1903,10 +2406,11 @@ int bt_obex_action_rsp(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *b hdr->code = rsp_code; hdr->len = sys_cpu_to_be16(buf->len); - err = obex_send(obex, buf); + err = obex_send(server->obex, server->tx.mopl, buf); if (!err) { if (rsp_code != BT_OBEX_RSP_CODE_CONTINUE) { - atomic_clear(&obex->_opcode); + atomic_clear(&server->_opcode); + atomic_ptr_clear(&server->obex->_active_server); } } else { if (allocated) { @@ -2683,6 +3187,7 @@ static bool bt_obex_find_header_cb(struct bt_obex_hdr *hdr, void *user_data) if (hdr->id == data->hdr.id) { data->hdr.data = hdr->data; data->hdr.len = hdr->len; + data->found = true; return false; } return true; diff --git a/subsys/bluetooth/host/classic/obex_internal.h b/subsys/bluetooth/host/classic/obex_internal.h index 58ffa3e40c003..a184aa15c3539 100644 --- a/subsys/bluetooth/host/classic/obex_internal.h +++ b/subsys/bluetooth/host/classic/obex_internal.h @@ -20,7 +20,11 @@ #define BT_OBEX_OPCODE_ACTION 0x06 #define BT_OBEX_OPCODE_ACTION_F 0x86 #define BT_OBEX_OPCODE_SESSION 0x87 -#define BT_OBEX_OPCODE_ABORT 0xFF +#define BT_OBEX_OPCODE_ABORT 0xff + +struct bt_obex_comm_hdr { + uint8_t code; +} __packed; struct bt_obex_req_hdr { uint8_t code; diff --git a/subsys/bluetooth/host/classic/shell/goep.c b/subsys/bluetooth/host/classic/shell/goep.c index e423028563058..6b143d08e6ab3 100644 --- a/subsys/bluetooth/host/classic/shell/goep.c +++ b/subsys/bluetooth/host/classic/shell/goep.c @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -34,6 +35,8 @@ static uint8_t add_head_buffer[GOEP_MOPL]; struct bt_goep_app { struct bt_goep goep; + struct bt_obex_client client; + struct bt_obex_server server; struct bt_conn *conn; struct net_buf *tx_buf; }; @@ -130,49 +133,50 @@ static int goep_parse_headers(struct net_buf *buf) return err; } -static void goep_server_connect(struct bt_obex *obex, uint8_t version, uint16_t mopl, +static void goep_server_connect(struct bt_obex_server *server, uint8_t version, uint16_t mopl, struct net_buf *buf) { - bt_shell_print("OBEX %p conn req, version %02x, mopl %04x", obex, version, mopl); + bt_shell_print("OBEX server %p conn req, version %02x, mopl %04x", server, version, mopl); goep_parse_headers(buf); } -static void goep_server_disconnect(struct bt_obex *obex, struct net_buf *buf) +static void goep_server_disconnect(struct bt_obex_server *server, struct net_buf *buf) { - bt_shell_print("OBEX %p disconn req", obex); + bt_shell_print("OBEX server %p disconn req", server); goep_parse_headers(buf); } -static void goep_server_put(struct bt_obex *obex, bool final, struct net_buf *buf) +static void goep_server_put(struct bt_obex_server *server, bool final, struct net_buf *buf) { - bt_shell_print("OBEX %p put req, final %s, data len %d", obex, final ? "true" : "false", - buf->len); + bt_shell_print("OBEX server %p put req, final %s, data len %d", server, + final ? "true" : "false", buf->len); goep_parse_headers(buf); } -static void goep_server_get(struct bt_obex *obex, bool final, struct net_buf *buf) +static void goep_server_get(struct bt_obex_server *server, bool final, struct net_buf *buf) { - bt_shell_print("OBEX %p get req, final %s, data len %d", obex, final ? "true" : "false", - buf->len); + bt_shell_print("OBEX server %p get req, final %s, data len %d", server, + final ? "true" : "false", buf->len); goep_parse_headers(buf); } -static void goep_server_abort(struct bt_obex *obex, struct net_buf *buf) +static void goep_server_abort(struct bt_obex_server *server, struct net_buf *buf) { - bt_shell_print("OBEX %p abort req", obex); + bt_shell_print("OBEX server %p abort req", server); goep_parse_headers(buf); } -static void goep_server_setpath(struct bt_obex *obex, uint8_t flags, struct net_buf *buf) +static void goep_server_setpath(struct bt_obex_server *server, uint8_t flags, struct net_buf *buf) { - bt_shell_print("OBEX %p setpath req, flags %02x, data len %d", obex, flags, buf->len); + bt_shell_print("OBEX server %p setpath req, flags %02x, data len %d", server, flags, + buf->len); goep_parse_headers(buf); } -static void goep_server_action(struct bt_obex *obex, bool final, struct net_buf *buf) +static void goep_server_action(struct bt_obex_server *server, bool final, struct net_buf *buf) { - bt_shell_print("OBEX %p action req, final %s, data len %d", obex, final ? "true" : "false", - buf->len); + bt_shell_print("OBEX server %p action req, final %s, data len %d", server, + final ? "true" : "false", buf->len); goep_parse_headers(buf); } @@ -186,49 +190,56 @@ struct bt_obex_server_ops goep_server_ops = { .action = goep_server_action, }; -static void goep_client_connect(struct bt_obex *obex, uint8_t rsp_code, uint8_t version, +static void goep_client_connect(struct bt_obex_client *client, uint8_t rsp_code, uint8_t version, uint16_t mopl, struct net_buf *buf) { - bt_shell_print("OBEX %p conn rsp, rsp_code %s, version %02x, mopl %04x", obex, + bt_shell_print("OBEX client %p conn rsp, rsp_code %s, version %02x, mopl %04x", client, bt_obex_rsp_code_to_str(rsp_code), version, mopl); goep_parse_headers(buf); } -static void goep_client_disconnect(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_disconnect(struct bt_obex_client *client, uint8_t rsp_code, + struct net_buf *buf) { - bt_shell_print("OBEX %p disconn rsp, rsp_code %s", obex, bt_obex_rsp_code_to_str(rsp_code)); + bt_shell_print("OBEX client %p disconn rsp, rsp_code %s", client, + bt_obex_rsp_code_to_str(rsp_code)); goep_parse_headers(buf); } -static void goep_client_put(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_put(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf) { - bt_shell_print("OBEX %p put rsp, rsp_code %s, data len %d", obex, + bt_shell_print("OBEX client %p put rsp, rsp_code %s, data len %d", client, bt_obex_rsp_code_to_str(rsp_code), buf->len); goep_parse_headers(buf); } -static void goep_client_get(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_get(struct bt_obex_client *client, uint8_t rsp_code, struct net_buf *buf) { - bt_shell_print("OBEX %p get rsp, rsp_code %s, data len %d", obex, + bt_shell_print("OBEX client %p get rsp, rsp_code %s, data len %d", client, bt_obex_rsp_code_to_str(rsp_code), buf->len); goep_parse_headers(buf); } -static void goep_client_abort(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_abort(struct bt_obex_client *client, uint8_t rsp_code, + struct net_buf *buf) { - bt_shell_print("OBEX %p abort rsp, rsp_code %s", obex, bt_obex_rsp_code_to_str(rsp_code)); + bt_shell_print("OBEX client %p abort rsp, rsp_code %s", client, + bt_obex_rsp_code_to_str(rsp_code)); goep_parse_headers(buf); } -static void goep_client_setpath(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_setpath(struct bt_obex_client *client, uint8_t rsp_code, + struct net_buf *buf) { - bt_shell_print("OBEX %p setpath rsp, rsp_code %s", obex, bt_obex_rsp_code_to_str(rsp_code)); + bt_shell_print("OBEX client %p setpath rsp, rsp_code %s", client, + bt_obex_rsp_code_to_str(rsp_code)); goep_parse_headers(buf); } -static void goep_client_action(struct bt_obex *obex, uint8_t rsp_code, struct net_buf *buf) +static void goep_client_action(struct bt_obex_client *client, uint8_t rsp_code, + struct net_buf *buf) { - bt_shell_print("OBEX %p action rsp, rsp_code %s, data len %d", obex, + bt_shell_print("OBEX client %p action rsp, rsp_code %s, data len %d", client, bt_obex_rsp_code_to_str(rsp_code), buf->len); goep_parse_headers(buf); } @@ -255,7 +266,6 @@ static int rfcomm_accept(struct bt_conn *conn, struct bt_goep_transport_rfcomm_s } g_app->goep.transport_ops = &goep_transport_ops; - g_app->goep.obex.server_ops = &goep_server_ops; *goep = &g_app->goep; return 0; } @@ -308,7 +318,6 @@ static int cmd_connect_rfcomm(const struct shell *sh, size_t argc, char *argv[]) } g_app->goep.transport_ops = &goep_transport_ops; - g_app->goep.obex.client_ops = &goep_client_ops; err = bt_goep_transport_rfcomm_connect(default_conn, &g_app->goep, channel); if (err) { @@ -356,7 +365,6 @@ static int l2cap_accept(struct bt_conn *conn, struct bt_goep_transport_l2cap_ser } g_app->goep.transport_ops = &goep_transport_ops; - g_app->goep.obex.server_ops = &goep_server_ops; *goep = &g_app->goep; return 0; } @@ -409,7 +417,6 @@ static int cmd_connect_l2cap(const struct shell *sh, size_t argc, char *argv[]) } g_app->goep.transport_ops = &goep_transport_ops; - g_app->goep.obex.client_ops = &goep_client_ops; err = bt_goep_transport_l2cap_connect(default_conn, &g_app->goep, psm); if (err) { @@ -1021,7 +1028,9 @@ static int cmd_goep_client_conn(const struct shell *sh, size_t argc, char *argv[ mopl = (uint16_t)strtoul(argv[1], NULL, 16); - err = bt_obex_connect(&goep_app.goep.obex, mopl, goep_app.tx_buf); + goep_app.client.ops = &goep_client_ops; + goep_app.client.obex = &goep_app.goep.obex; + err = bt_obex_connect(&goep_app.client, mopl, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send conn req %d", err); } else { @@ -1044,7 +1053,7 @@ static int cmd_goep_client_disconn(const struct shell *sh, size_t argc, char *ar return -ENOEXEC; } - err = bt_obex_disconnect(&goep_app.goep.obex, goep_app.tx_buf); + err = bt_obex_disconnect(&goep_app.client, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send disconn req %d", err); } else { @@ -1074,7 +1083,7 @@ static int cmd_goep_client_put(const struct shell *sh, size_t argc, char *argv[] return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_put(&goep_app.goep.obex, final, goep_app.tx_buf); + err = bt_obex_put(&goep_app.client, final, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send put req %d", err); } else { @@ -1104,7 +1113,7 @@ static int cmd_goep_client_get(const struct shell *sh, size_t argc, char *argv[] return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_get(&goep_app.goep.obex, final, goep_app.tx_buf); + err = bt_obex_get(&goep_app.client, final, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send get req %d", err); } else { @@ -1127,7 +1136,7 @@ static int cmd_goep_client_abort(const struct shell *sh, size_t argc, char *argv return -ENOEXEC; } - err = bt_obex_abort(&goep_app.goep.obex, goep_app.tx_buf); + err = bt_obex_abort(&goep_app.client, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send abort req %d", err); } else { @@ -1162,7 +1171,7 @@ static int cmd_goep_client_setpath(const struct shell *sh, size_t argc, char *ar } } - err = bt_obex_setpath(&goep_app.goep.obex, flags, goep_app.tx_buf); + err = bt_obex_setpath(&goep_app.client, flags, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send setpath req %d", err); } else { @@ -1192,7 +1201,7 @@ static int cmd_goep_client_action(const struct shell *sh, size_t argc, char *arg return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_action(&goep_app.goep.obex, final, goep_app.tx_buf); + err = bt_obex_action(&goep_app.client, final, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send action req %d", err); } else { @@ -1201,6 +1210,43 @@ static int cmd_goep_client_action(const struct shell *sh, size_t argc, char *arg return err; } +static int cmd_goep_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; + + static struct bt_uuid_128 uuid; + + if (argc > 1) { + hex2bin(argv[1], strlen(argv[1]), uuid128, sizeof(uuid128)); + uuid.uuid.type = BT_UUID_TYPE_128; + sys_memcpy_swap(uuid.val, uuid128, BT_UUID_SIZE_128); + u = &uuid; + } + + goep_app.server.ops = &goep_server_ops; + goep_app.server.obex = &goep_app.goep.obex; + err = bt_obex_server_register(&goep_app.server, u); + if (err != 0) { + shell_error(sh, "Fail to register obex server %d", err); + } + + return err; +} + +static int cmd_goep_server_unreg(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + + err = bt_obex_server_unregister(&goep_app.server); + if (err != 0) { + shell_error(sh, "Fail to unregister obex server %d", err); + } + + return err; +} + static int cmd_goep_server_conn(const struct shell *sh, size_t argc, char *argv[]) { uint8_t rsp_code; @@ -1237,7 +1283,7 @@ static int cmd_goep_server_conn(const struct shell *sh, size_t argc, char *argv[ mopl = (uint16_t)strtoul(argv[2], NULL, 16); - err = bt_obex_connect_rsp(&goep_app.goep.obex, rsp_code, mopl, goep_app.tx_buf); + err = bt_obex_connect_rsp(&goep_app.server, rsp_code, mopl, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send conn rsp %d", err); } else { @@ -1279,7 +1325,7 @@ static int cmd_goep_server_disconn(const struct shell *sh, size_t argc, char *ar return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_disconnect_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_disconnect_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send disconn rsp %d", err); } else { @@ -1321,7 +1367,7 @@ static int cmd_goep_server_put(const struct shell *sh, size_t argc, char *argv[] return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_put_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_put_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send put rsp %d", err); } else { @@ -1363,7 +1409,7 @@ static int cmd_goep_server_get(const struct shell *sh, size_t argc, char *argv[] return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_get_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_get_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send get rsp %d", err); } else { @@ -1405,7 +1451,7 @@ static int cmd_goep_server_abort(const struct shell *sh, size_t argc, char *argv return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_abort_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_abort_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send abort rsp %d", err); } else { @@ -1447,7 +1493,7 @@ static int cmd_goep_server_setpath(const struct shell *sh, size_t argc, char *ar return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_setpath_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_setpath_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send setpath rsp %d", err); } else { @@ -1489,7 +1535,7 @@ static int cmd_goep_server_action(const struct shell *sh, size_t argc, char *arg return SHELL_CMD_HELP_PRINTED; } - err = bt_obex_action_rsp(&goep_app.goep.obex, rsp_code, goep_app.tx_buf); + err = bt_obex_action_rsp(&goep_app.server, rsp_code, goep_app.tx_buf); if (err) { shell_error(sh, "Fail to send action rsp %d", err); } else { @@ -1570,6 +1616,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(obex_client_cmds, ); SHELL_STATIC_SUBCMD_SET_CREATE(obex_server_cmds, + SHELL_CMD_ARG(reg, NULL, "[UUID 128]", cmd_goep_server_reg, 1, 1), + SHELL_CMD_ARG(unreg, NULL, HELP_NONE, cmd_goep_server_unreg, 1, 0), SHELL_CMD_ARG(conn, NULL, " [rsp_code]", cmd_goep_server_conn, 3, 1), SHELL_CMD_ARG(disconn, NULL, " [rsp_code]",