Skip to content

Commit c4bd693

Browse files
piruncarlescufi
authored andcommitted
Bluetooth: OTS - Client APIs add Object Write sub-procedure
OTS client: Add object write function. Also add write object callback. Signed-off-by: Pirun Lee <[email protected]>
1 parent 8644b33 commit c4bd693

File tree

2 files changed

+214
-5
lines changed

2 files changed

+214
-5
lines changed

include/zephyr/bluetooth/services/ots.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,15 @@ enum {
250250
BT_OTS_OACP_FEAT_ABORT = 9,
251251
};
252252

253+
/*
254+
* @enum bt_ots_oacp_write_op_mode
255+
* @brief Mode Parameter for OACP Write Op Code.
256+
*/
257+
enum bt_ots_oacp_write_op_mode {
258+
BT_OTS_OACP_WRITE_OP_MODE_NONE = 0,
259+
BT_OTS_OACP_WRITE_OP_MODE_TRUNCATE = BIT(1),
260+
};
261+
253262
/** @brief Set @ref BT_OTS_OACP_SET_FEAT_CREATE feature.
254263
*
255264
* @param feat OTS features.
@@ -867,6 +876,18 @@ struct bt_ots_client_cb {
867876
void (*obj_metadata_read)(struct bt_ots_client *ots_inst,
868877
struct bt_conn *conn, int err,
869878
uint8_t metadata_read);
879+
880+
/** @brief Callback function for the data of the write object.
881+
*
882+
* Called when the data of the selected object is written using
883+
* bt_ots_client_write_object_data().
884+
*
885+
* @param ots_inst Pointer to the OTC instance.
886+
* @param conn The connection to the peer device.
887+
* @param len Length of the written data.
888+
*/
889+
void (*obj_data_written)(struct bt_ots_client *ots_inst,
890+
struct bt_conn *conn, size_t len);
870891
};
871892

872893
/** @brief Register an Object Transfer Service Instance.
@@ -988,6 +1009,26 @@ int bt_ots_client_read_object_metadata(struct bt_ots_client *otc_inst,
9881009
int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,
9891010
struct bt_conn *conn);
9901011

1012+
/** @brief Write the data of the current selected object.
1013+
*
1014+
* This will trigger an OACP write operation for the current size of the object
1015+
* with a specified offset and then expect transferring the content via the L2CAP CoC.
1016+
*
1017+
* The length of the data written to object is returned in the obj_data_written() callback.
1018+
*
1019+
* @param otc_inst Pointer to the OTC instance.
1020+
* @param conn Pointer to the connection object.
1021+
* @param buf Pointer to the data buffer to be written.
1022+
* @param len Size of data.
1023+
* @param offset Offset to write, usually 0.
1024+
* @param mode Mode Parameter for OACP Write Op Code. See @ref bt_ots_oacp_write_op_mode.
1025+
*
1026+
* @return int 0 if success, ERRNO on failure.
1027+
*/
1028+
int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst, struct bt_conn *conn,
1029+
const void *buf, size_t len, off_t offset,
1030+
enum bt_ots_oacp_write_op_mode mode);
1031+
9911032
/** @brief Directory listing object metadata callback
9921033
*
9931034
* If a directory listing is decoded using bt_ots_client_decode_dirlisting(),

subsys/bluetooth/services/ots/ots_client.c

Lines changed: 173 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <zephyr/device.h>
1313
#include <zephyr/init.h>
1414
#include <zephyr/sys/byteorder.h>
15+
#include <zephyr/sys/check.h>
1516

1617
#include <zephyr/bluetooth/bluetooth.h>
1718
#include <zephyr/bluetooth/conn.h>
@@ -109,6 +110,7 @@ struct bt_otc_internal_instance_t {
109110
uint8_t metadata_read;
110111
int metadata_err;
111112
uint32_t rcvd_size;
113+
uint32_t sent_size;
112114
};
113115

114116
/* The profile clients that uses the OTS are responsible for discovery and
@@ -120,11 +122,14 @@ static struct bt_otc_internal_instance_t *cur_inst;
120122

121123
static int oacp_read(struct bt_conn *conn,
122124
struct bt_otc_internal_instance_t *inst);
125+
static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
126+
const void *buf, uint32_t len, uint32_t offset,
127+
enum bt_ots_oacp_write_op_mode mode);
123128
static void read_next_metadata(struct bt_conn *conn,
124129
struct bt_otc_internal_instance_t *inst);
125130
static int read_attr(struct bt_conn *conn,
126-
struct bt_otc_internal_instance_t *inst,
127-
uint16_t handle, bt_gatt_read_func_t cb);
131+
struct bt_otc_internal_instance_t *inst,
132+
uint16_t handle, bt_gatt_read_func_t cb);
128133

129134
/* L2CAP callbacks */
130135
static void tx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
@@ -134,6 +139,35 @@ static void tx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
134139
BT_ERR("Unexpected call, context: %p, conn: %p", l2cap_ctx, (void *)conn);
135140
}
136141

142+
static void write_obj_tx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
143+
struct bt_conn *conn)
144+
{
145+
int err;
146+
size_t written;
147+
148+
if (cur_inst == NULL) {
149+
BT_ERR("OTS instance invalid\n");
150+
return;
151+
}
152+
153+
written = cur_inst->sent_size;
154+
BT_DBG("ctx: %p, conn: %p, written: %d",
155+
l2cap_ctx, (void *)conn, written);
156+
157+
err = bt_gatt_ots_l2cap_disconnect(l2cap_ctx);
158+
if (err < 0) {
159+
BT_WARN("Disconnecting L2CAP returned error %d", err);
160+
}
161+
162+
if ((cur_inst->otc_inst != NULL) && (cur_inst->otc_inst->cb != NULL)) {
163+
if (cur_inst->otc_inst->cb->obj_data_written) {
164+
cur_inst->otc_inst->cb->obj_data_written(0, conn, written);
165+
}
166+
}
167+
168+
cur_inst = NULL;
169+
}
170+
137171
static ssize_t rx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
138172
struct bt_conn *conn, struct net_buf *buf)
139173
{
@@ -192,7 +226,7 @@ static ssize_t rx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
192226
}
193227

194228
static void chan_closed(struct bt_gatt_ots_l2cap *l2cap_ctx,
195-
struct bt_conn *conn)
229+
struct bt_conn *conn)
196230
{
197231
BT_DBG("L2CAP closed, context: %p, conn: %p", l2cap_ctx, (void *)conn);
198232
}
@@ -508,6 +542,7 @@ static void write_olcp_cb(struct bt_conn *conn, uint8_t err,
508542
BT_ERR("Instance not found");
509543
return;
510544
}
545+
511546
inst->busy = false;
512547
}
513548

@@ -1008,8 +1043,8 @@ static uint8_t read_obj_modified_cb(struct bt_conn *conn, uint8_t err,
10081043
}
10091044

10101045
static int read_attr(struct bt_conn *conn,
1011-
struct bt_otc_internal_instance_t *inst,
1012-
uint16_t handle, bt_gatt_read_func_t cb)
1046+
struct bt_otc_internal_instance_t *inst,
1047+
uint16_t handle, bt_gatt_read_func_t cb)
10131048
{
10141049
if (!handle) {
10151050
BT_DBG("Handle not set");
@@ -1096,6 +1131,30 @@ static void write_oacp_cp_cb(struct bt_conn *conn, uint8_t err,
10961131
inst->busy = false;
10971132
}
10981133

1134+
static void write_oacp_cp_write_req_cb(struct bt_conn *conn, uint8_t err,
1135+
struct bt_gatt_write_params *params)
1136+
{
1137+
struct bt_otc_internal_instance_t *inst =
1138+
lookup_inst_by_handle(params->handle);
1139+
uint32_t len;
1140+
1141+
BT_DBG("Write Object request %s (0x%02X)", err ? "failed" : "successful", err);
1142+
if (!inst) {
1143+
BT_ERR("Instance not found");
1144+
return;
1145+
}
1146+
1147+
len = inst->l2cap_ctx.tx.len;
1148+
inst->l2cap_ctx.tx.len = 0;
1149+
err = bt_gatt_ots_l2cap_send(&inst->l2cap_ctx, inst->l2cap_ctx.tx.data, len);
1150+
if (err) {
1151+
BT_WARN("L2CAP CoC error: %d while trying to execute OACP "
1152+
"Read procedure", err);
1153+
}
1154+
1155+
inst->busy = false;
1156+
}
1157+
10991158
static int oacp_read(struct bt_conn *conn,
11001159
struct bt_otc_internal_instance_t *inst)
11011160
{
@@ -1157,6 +1216,64 @@ static int oacp_read(struct bt_conn *conn,
11571216
return err;
11581217
}
11591218

1219+
static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
1220+
const void *buf, uint32_t len, uint32_t offset,
1221+
enum bt_ots_oacp_write_op_mode mode)
1222+
{
1223+
int err;
1224+
struct bt_gatt_ots_l2cap *l2cap;
1225+
1226+
if (!inst->otc_inst->oacp_handle) {
1227+
BT_DBG("Handle not set");
1228+
return -EINVAL;
1229+
} else if (inst->busy) {
1230+
return -EBUSY;
1231+
} else if (cur_inst) {
1232+
return -EBUSY;
1233+
}
1234+
1235+
err = bt_gatt_ots_l2cap_connect(conn, &l2cap);
1236+
if (err) {
1237+
BT_DBG("Could not connect l2cap: %d", err);
1238+
return err;
1239+
}
1240+
1241+
l2cap->tx_done = write_obj_tx_done;
1242+
l2cap->rx_done = rx_done;
1243+
l2cap->closed = chan_closed;
1244+
l2cap->tx.data = (uint8_t *)buf;
1245+
l2cap->tx.len = len;
1246+
net_buf_simple_reset(&otc_tx_buf);
1247+
1248+
/* OP Code */
1249+
net_buf_simple_add_u8(&otc_tx_buf, BT_GATT_OTS_OACP_PROC_WRITE);
1250+
1251+
/* Offset */
1252+
net_buf_simple_add_le32(&otc_tx_buf, offset);
1253+
1254+
/* Len */
1255+
net_buf_simple_add_le32(&otc_tx_buf, len);
1256+
1257+
/* Mode, truncate or not */
1258+
net_buf_simple_add_u8(&otc_tx_buf, mode);
1259+
1260+
inst->otc_inst->write_params.offset = 0;
1261+
inst->otc_inst->write_params.data = otc_tx_buf.data;
1262+
inst->otc_inst->write_params.length = otc_tx_buf.len;
1263+
inst->otc_inst->write_params.handle = inst->otc_inst->oacp_handle;
1264+
inst->otc_inst->write_params.func = write_oacp_cp_write_req_cb;
1265+
inst->sent_size = len;
1266+
err = bt_gatt_write(conn, &inst->otc_inst->write_params);
1267+
1268+
if (!err) {
1269+
inst->busy = true;
1270+
cur_inst = inst;
1271+
}
1272+
1273+
inst->rcvd_size = 0;
1274+
1275+
return err;
1276+
}
11601277
int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,
11611278
struct bt_conn *conn)
11621279
{
@@ -1185,6 +1302,57 @@ int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,
11851302
return oacp_read(conn, inst);
11861303
}
11871304

1305+
int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst,
1306+
struct bt_conn *conn, const void *buf, size_t len,
1307+
off_t offset, enum bt_ots_oacp_write_op_mode mode)
1308+
{
1309+
struct bt_otc_internal_instance_t *inst;
1310+
1311+
CHECKIF(!conn) {
1312+
BT_WARN("Invalid Connection");
1313+
return -ENOTCONN;
1314+
}
1315+
1316+
CHECKIF(!otc_inst) {
1317+
BT_ERR("Invalid OTC instance");
1318+
return -EINVAL;
1319+
}
1320+
1321+
CHECKIF((mode != BT_OTS_OACP_WRITE_OP_MODE_NONE) &&
1322+
(mode != BT_OTS_OACP_WRITE_OP_MODE_TRUNCATE)) {
1323+
BT_ERR("Invalid write object mode parameter %d", mode);
1324+
return -EINVAL;
1325+
}
1326+
1327+
/* OTS_v10.pdf Table 3.9: Object Action Control Point Procedure Requirements
1328+
* Offset and Length field are UINT32 Length
1329+
*/
1330+
CHECKIF(len > UINT32_MAX) {
1331+
BT_ERR("length exceed UINT32");
1332+
return -EINVAL;
1333+
}
1334+
1335+
CHECKIF((offset > UINT32_MAX) || (offset < 0)) {
1336+
BT_ERR("offset exceeds UINT32");
1337+
return -EINVAL;
1338+
}
1339+
1340+
inst = lookup_inst_by_handle(otc_inst->start_handle);
1341+
1342+
if (!inst) {
1343+
BT_ERR("Invalid OTC instance");
1344+
return -EINVAL;
1345+
}
1346+
1347+
if ((len > (otc_inst->cur_object.size.alloc - otc_inst->cur_object.size.cur)) ||
1348+
len == 0) {
1349+
BT_ERR("Invalid write len: %zu", len);
1350+
return -EINVAL;
1351+
}
1352+
1353+
return oacp_write(conn, inst, buf, (uint32_t)len, (uint32_t)offset, mode);
1354+
}
1355+
11881356
static void read_next_metadata(struct bt_conn *conn,
11891357
struct bt_otc_internal_instance_t *inst)
11901358
{

0 commit comments

Comments
 (0)