From: Luiz Augusto von Dentz <[email protected]>
Single Response Mode (SRM) is a 1-byte quantity containing a value to
enable or disable SRM, as well as to indicate support for SRM.
---
gobex/gobex-transfer.c | 54 ++++++++++++--
gobex/gobex.c | 194 +++++++++++++++++++++++++++++++++++++++++-------
gobex/gobex.h | 1 +
3 files changed, 216 insertions(+), 33 deletions(-)
diff --git a/gobex/gobex-transfer.c b/gobex/gobex-transfer.c
index 0a7a29f..614d2d1 100644
--- a/gobex/gobex-transfer.c
+++ b/gobex/gobex-transfer.c
@@ -33,6 +33,9 @@
static GSList *transfers = NULL;
+static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
+ gpointer user_data);
+
struct transfer {
guint id;
guint8 opcode;
@@ -130,16 +133,30 @@ static gssize put_get_data(void *buf, gsize len, gpointer user_data)
gssize ret;
ret = transfer->data_producer(buf, len, transfer->user_data);
- if (ret >= 0)
+ if (ret == 0 || ret == -EAGAIN)
return ret;
- if (ret == -EAGAIN)
- return ret;
+ if (ret > 0) {
+ /* Check if SRM is active */
+ if (!g_obex_srm_active(transfer->obex))
+ return ret;
+
+ /* Generate next packet */
+ req = g_obex_packet_new(transfer->opcode, FALSE,
+ G_OBEX_HDR_INVALID);
+ g_obex_packet_add_body(req, put_get_data, transfer);
+ transfer->req_id = g_obex_send_req(transfer->obex, req, -1,
+ transfer_response, transfer,
+ &err);
+ goto done;
+ }
req = g_obex_packet_new(G_OBEX_OP_ABORT, TRUE, G_OBEX_HDR_INVALID);
+
transfer->req_id = g_obex_send_req(transfer->obex, req, -1,
transfer_abort_response,
transfer, &err);
+done:
if (err != NULL) {
transfer_complete(transfer, err);
g_error_free(err);
@@ -209,10 +226,11 @@ static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
req = g_obex_packet_new(transfer->opcode, FALSE,
G_OBEX_HDR_INVALID);
g_obex_packet_add_body(req, put_get_data, transfer);
- } else {
+ } else if (!g_obex_srm_active(transfer->obex)) {
req = g_obex_packet_new(transfer->opcode, TRUE,
G_OBEX_HDR_INVALID);
- }
+ } else
+ return;
transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response,
transfer, &err);
@@ -350,6 +368,7 @@ static void transfer_put_req_first(struct transfer *transfer, GObexPacket *req,
rspcode = put_get_bytes(transfer, req);
rsp = g_obex_packet_new_valist(rspcode, TRUE, first_hdr_id, args);
+
if (!g_obex_send(transfer->obex, rsp, &err)) {
transfer_complete(transfer, err);
g_error_free(err);
@@ -370,12 +389,19 @@ static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data)
rspcode = put_get_bytes(transfer, req);
+ /* Don't send continue while in SRM */
+ if (g_obex_srm_active(transfer->obex) &&
+ rspcode == G_OBEX_RSP_CONTINUE)
+ goto done;
+
rsp = g_obex_packet_new(rspcode, TRUE, G_OBEX_HDR_INVALID);
+
if (!g_obex_send(obex, rsp, &err)) {
transfer_complete(transfer, err);
g_error_free(err);
}
+done:
if (rspcode != G_OBEX_RSP_CONTINUE)
transfer_complete(transfer, NULL);
}
@@ -472,15 +498,29 @@ guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
static gssize get_get_data(void *buf, gsize len, gpointer user_data)
{
struct transfer *transfer = user_data;
- GObexPacket *req;
+ GObexPacket *req, *rsp;
GError *err = NULL;
gssize ret;
g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
ret = transfer->data_producer(buf, len, transfer->user_data);
- if (ret > 0)
+ if (ret > 0) {
+ if (!g_obex_srm_active(transfer->obex))
+ return ret;
+
+ /* Generate next response */
+ rsp = g_obex_packet_new(G_OBEX_RSP_CONTINUE, TRUE,
+ G_OBEX_HDR_INVALID);
+ g_obex_packet_add_body(rsp, get_get_data, transfer);
+
+ if (!g_obex_send(transfer->obex, rsp, &err)) {
+ transfer_complete(transfer, err);
+ g_error_free(err);
+ }
+
return ret;
+ }
if (ret == -EAGAIN)
return ret;
diff --git a/gobex/gobex.c b/gobex/gobex.c
index 036a44a..e225a50 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -46,6 +46,12 @@
guint gobex_debug = 0;
+struct srm_config {
+ guint8 op;
+ gboolean enabled;
+ guint8 srm;
+};
+
struct _GObex {
gint ref_count;
GIOChannel *io;
@@ -65,6 +71,8 @@ struct _GObex {
gboolean suspended;
+ struct srm_config *srm;
+
guint write_source;
gssize io_rx_mtu;
@@ -308,12 +316,16 @@ static gboolean write_data(GIOChannel *io, GIOCondition cond,
if (p == NULL)
goto stop_tx;
+ if (g_obex_srm_active(obex))
+ goto encode;
+
/* Can't send a request while there's a pending one */
if (obex->pending_req && p->id > 0) {
g_queue_push_head(obex->tx_queue, p);
goto stop_tx;
}
+encode:
len = g_obex_packet_encode(p->pkt, obex->tx_buf, obex->tx_mtu);
if (len == -EAGAIN) {
g_queue_push_head(obex->tx_queue, p);
@@ -327,6 +339,8 @@ static gboolean write_data(GIOChannel *io, GIOCondition cond,
}
if (p->id > 0) {
+ if (obex->pending_req != NULL)
+ pending_pkt_free(obex->pending_req);
obex->pending_req = p;
p->timeout_id = g_timeout_add_seconds(p->timeout,
req_timeout, obex);
@@ -427,6 +441,77 @@ static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
g_obex_packet_prepend_header(rsp, connid);
}
+static void set_srm(GObex *obex, guint8 op, guint8 srm)
+{
+ struct srm_config *config = obex->srm;
+ gboolean enable;
+
+ if (config == NULL) {
+ if (srm == G_OBEX_SRM_DISABLE)
+ return;
+
+ config = g_new0(struct srm_config, 1);
+ config->op = op;
+ config->srm = srm;
+ obex->srm = config;
+ return;
+ }
+
+ /* Indicate response, treat it as request */
+ if (config->srm == G_OBEX_SRM_INDICATE) {
+ if (srm != G_OBEX_SRM_ENABLE)
+ goto done;
+ config->srm = srm;
+ return;
+ }
+
+ enable = (srm == G_OBEX_SRM_ENABLE);
+ if (config->enabled == enable)
+ goto done;
+
+ config->enabled = enable;
+
+ g_obex_debug(G_OBEX_DEBUG_COMMAND, "SRM %s", config->enabled ?
+ "Enabled" : "Disabled");
+
+done:
+ if (config->enabled)
+ return;
+
+ g_free(obex->srm);
+ obex->srm = NULL;
+}
+
+static void setup_srm(GObex *obex, GObexPacket *pkt)
+{
+ GObexHeader *hdr;
+ guint8 op;
+ gboolean final;
+
+ op = g_obex_packet_get_operation(pkt, &final);
+
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+ if (hdr != NULL) {
+ guint8 srm;
+ g_obex_header_get_uint8(hdr, &srm);
+ g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
+ set_srm(obex, op, srm);
+ }
+
+ if (obex->srm == NULL || !obex->srm->enabled || !final)
+ return;
+
+ switch (obex->srm->op) {
+ case G_OBEX_OP_CONNECT:
+ return;
+ default:
+ if (op <= G_OBEX_RSP_CONTINUE)
+ return;
+ }
+
+ set_srm(obex, op, G_OBEX_SRM_DISABLE);
+}
+
gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
{
struct pending_pkt *p;
@@ -444,6 +529,8 @@ gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
if (obex->rx_last_op == G_OBEX_OP_CONNECT)
prepare_connect_rsp(obex, pkt);
+ setup_srm(obex, pkt);
+
p = g_new0(struct pending_pkt, 1);
p->pkt = pkt;
@@ -458,25 +545,29 @@ guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
GObexResponseFunc func, gpointer user_data,
GError **err)
{
- GObexHeader *connid;
+ GObexHeader *hdr;
struct pending_pkt *p;
static guint id = 1;
g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+ setup_srm(obex, req);
+
if (obex->conn_id == CONNID_INVALID)
goto create_pending;
if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
goto create_pending;
- connid = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
- if (connid != NULL)
+ if (g_obex_srm_active(obex) && obex->pending_req != NULL)
goto create_pending;
- connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
- obex->conn_id);
- g_obex_packet_prepend_header(req, connid);
+ hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
+ if (hdr != NULL)
+ goto create_pending;
+
+ hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
+ g_obex_packet_prepend_header(req, hdr);
create_pending:
p = g_new0(struct pending_pkt, 1);
@@ -675,6 +766,19 @@ void g_obex_resume(GObex *obex)
enable_tx(obex);
}
+gboolean g_obex_srm_active(GObex *obex)
+{
+ gboolean ret = FALSE;
+
+ if (obex->srm == NULL || !obex->srm->enabled)
+ goto done;
+
+ ret = TRUE;
+done:
+ g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
+ return ret;
+}
+
static void parse_connect_data(GObex *obex, GObexPacket *pkt)
{
const struct connect_data *data;
@@ -698,21 +802,46 @@ static void parse_connect_data(GObex *obex, GObexPacket *pkt)
g_obex_header_get_uint32(connid, &obex->conn_id);
}
-static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
+static gboolean parse_response(GObex *obex, GObexPacket *rsp)
{
struct pending_pkt *p = obex->pending_req;
- gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
+ guint8 opcode, rspcode;
+ gboolean final;
- if (rsp != NULL) {
- guint8 opcode;
+ rspcode = g_obex_packet_get_operation(rsp, &final);
- g_obex_packet_get_operation(rsp, &final_rsp);
+ opcode = g_obex_packet_get_operation(p->pkt, NULL);
+ if (opcode == G_OBEX_OP_CONNECT)
+ parse_connect_data(obex, rsp);
- opcode = g_obex_packet_get_operation(p->pkt, NULL);
- if (opcode == G_OBEX_OP_CONNECT)
- parse_connect_data(obex, rsp);
+ setup_srm(obex, rsp);
+
+ if (!g_obex_srm_active(obex))
+ return final;
+
+ /*
+ * Resposes have final bit set but in case of GET with SRM
+ * we should not remove the request since the remote side will
+ * continue sending responses until the transfer is finished
+ */
+ if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
+ g_source_remove(p->timeout_id);
+ p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
+ obex);
+ return FALSE;
}
+ return final;
+}
+
+static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
+{
+ struct pending_pkt *p = obex->pending_req;
+ gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
+
+ if (rsp != NULL)
+ final_rsp = parse_response(obex, rsp);
+
if (p->cancelled)
err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
"The operation was cancelled");
@@ -720,7 +849,6 @@ static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
if (err)
g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", err->message);
-
if (p->rsp_func) {
p->rsp_func(obex, err, rsp, p->rsp_data);
@@ -758,13 +886,12 @@ static gboolean check_connid(GObex *obex, GObexPacket *pkt)
return obex->conn_id == id;
}
-static void handle_request(GObex *obex, GObexPacket *req)
+static int parse_request(GObex *obex, GObexPacket *req)
{
- GSList *match;
- guint op;
-
- op = g_obex_packet_get_operation(req, NULL);
+ guint8 op;
+ gboolean final;
+ op = g_obex_packet_get_operation(req, &final);
switch (op) {
case G_OBEX_OP_CONNECT:
parse_connect_data(obex, req);
@@ -775,12 +902,23 @@ static void handle_request(GObex *obex, GObexPacket *req)
if (check_connid(obex, req))
break;
- g_obex_debug(G_OBEX_DEBUG_ERROR, "Invalid Connection ID");
- g_obex_send_rsp(obex, G_OBEX_RSP_SERVICE_UNAVAILABLE, NULL,
- G_OBEX_HDR_INVALID);
- return;
+ return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
}
+ setup_srm(obex, req);
+
+ return op;
+}
+
+static void handle_request(GObex *obex, GObexPacket *req)
+{
+ GSList *match;
+ int op;
+
+ op = parse_request(obex, req);
+ if (op < 0)
+ goto fail;
+
match = g_slist_find_custom(obex->req_handlers, GUINT_TO_POINTER(op),
req_handler_cmpop);
if (match) {
@@ -789,8 +927,11 @@ static void handle_request(GObex *obex, GObexPacket *req)
return;
}
- g_obex_send_rsp(obex, G_OBEX_RSP_NOT_IMPLEMENTED, NULL,
- G_OBEX_HDR_INVALID);
+ op = -G_OBEX_RSP_NOT_IMPLEMENTED;
+
+fail:
+ g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", g_obex_strerror(-op));
+ g_obex_send_rsp(obex, -op, NULL, G_OBEX_HDR_INVALID);
}
static gboolean read_stream(GObex *obex, GError **err)
@@ -1111,6 +1252,7 @@ void g_obex_unref(GObex *obex)
g_free(obex->rx_buf);
g_free(obex->tx_buf);
+ g_free(obex->srm);
if (obex->pending_req)
pending_pkt_free(obex->pending_req);
diff --git a/gobex/gobex.h b/gobex/gobex.h
index 81981ea..f3a7f94 100644
--- a/gobex/gobex.h
+++ b/gobex/gobex.h
@@ -61,6 +61,7 @@ gboolean g_obex_remove_request_function(GObex *obex, guint id);
void g_obex_suspend(GObex *obex);
void g_obex_resume(GObex *obex);
+gboolean g_obex_srm_active(GObex *obex);
GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
gssize rx_mtu, gssize tx_mtu);
--
1.7.7.4
Hi Luiz,
On Tue, Jan 03, 2012, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> Single Response Mode (SRM) is a 1-byte quantity containing a value to
> enable or disable SRM, as well as to indicate support for SRM.
> ---
> gobex/gobex-transfer.c | 54 ++++++++++++--
> gobex/gobex.c | 194 +++++++++++++++++++++++++++++++++++++++++-------
> gobex/gobex.h | 1 +
> 3 files changed, 216 insertions(+), 33 deletions(-)
Since the rather long discussion with Hendrik doesn't seem to have
changed your mind about the correctness of these patches I've now
applied them upstream :)
Johan
Hi Hendrik,
On Fri, Jan 6, 2012 at 12:40 AM, Hendrik Sattler
<[email protected]> wrote:
>> And it continues stating that SRM can be enable outside of connection
>> scope: "If this mode is enabled outside the scope of an OBEX connection,
>> SRM is automatically disabled when the operation that enabled this mode
>> completes."
>
> I guess this is only specified if there is ever a transport that actually
> specifies this. GOEP v2.0 does not do that.
It does state SRM is automatically disabled when operations finishes,
that is specified by both OBEX 1.4 and GOEP 2.0.
>> I believe this is why GOEP 2.0 want to avoid using SRM per connection,
>> because multiple outstanding operations are not very easy to be
>> handled without an id/serial to identify them which OBEX doesn't have,
>> for instance there could be SRMP headers in responses which alters SRM
>> and breaks the assumption that only one response occurs per operation.
>
> No. Unless the server does not correctly handle SRM, this assumption is not
> broken. The main problem is actually when sending an ABORT after the GET that
> comes too late. Then you get responses: one SUCCESS for GET and one SUCCESS
> for ABORT instead of one SUCCESS for GET. This can break the synchronisation
> of client and server and the writers of the OBEX spec did not consider this.
In any case this is pretty bad.
>> IMO what the OBEX spec writer suggests is that the operations are
>> queued by the server, so it only starts responding once the previous
>> operation is finished, but this doesn't have any benefit over enabling
>> SRM per operation as GOEP 2.0 suggests.
>
> And there is the problem: "SRM per operation" is not specified by OBEX v1.4.
> There, it is always "SRM per connection", so this must be something new from
> OBEX v1.5.
It is specified, GET, PUT, SETPATH and DISCONNECT can all have SRM
headers, it is also stated in page 32 of OBEX 1.4:
"If no OBEX connection exists when SRM is enabled, SRM shall be
automatically disabled upon completion of the operation which enabled
it."
The question is, connection means CONNECT operation has been complete
or within CONNECT request/response, GOEP 2.0 interpret it as the
latter which might be what OBEX 1.5 specifies due to the issues of
multiple outstanding operations. Btw, GOEP 2.0 test specification used
for qualification has a test which strictly forbids the server to
respond with SRM headers in case of CONNECT:
"3.16 TP/SRM/BI-03-C Process an OBEX CONNECT request
(incorrectly) containing a SRM header
? Expected Outcome
Pass Verdict:
? On receiving the invalid SRM header in the OBEX_CONNECT request, the IUT
responds with a SUCCESS without a SRM header.
? OBEX/L2CAP channel is established.
Fail Verdict:
? On receiving the invalid SRM header in the OBEX_CONNECT request, the IUT does
not respond with a SUCCESS and/or includes a SRM header in the response.
? OBEX/L2CAP channel is not established or OBEX/RFCOMM channel is established."
>> OBEX spec says that if SRM is enabled during the connection context it
>> is valid to all subsequent operations, I guess GOEP 2.0 interpret
>> context as just within CONNECT request/response. Again I think this is
>> to avoid having to support multiple outstanding operations, but it is
>> not stated anywhere so it is just my guess.
>
> This must be from OBEX v1.5. Good guess, then :-)
> Strange enough they chose to support SRM but only a less efficient setup (you
> still have twice as much RTTs for each PUT and GET than with SRM enabled with
> CONNECT.
You mean +1 response for PUT to enable SRM, for GET it is exactly the
same since the first response should enable SRM and no other request
should be sent. I think this is pretty good considering that it
reduces the complexity compared to supporting SRM on CONNECT.
--
Luiz Augusto von Dentz
Am Donnerstag, 5. Januar 2012, 13:11:01 schrieb Luiz Augusto von Dentz:
> Hi Hendrik,
>
> On Wed, Jan 4, 2012 at 8:48 PM, Hendrik Sattler <[email protected]>
wrote:
> >> That is what the OBEX spec says, but GOEP 2.0 says otherwise, actually
> >> PUT and GET may also be used without CONNECT and the even the OBEX
> >> spec state both PUT, GET and SETPATH supporting SRM headers.
> >
> > Using PUT and GET without CONNECT is not very efficient as you limit
> > yourself to the lowest MTU and default inbox (OPP). This was only there
> > for some kind of stateless IrDA connection which does not qualify for
> > the requirements of SRM, anyway
>
> Efficiency was no much of the point here, but what operation do
> support as stated in 2.2.23 Single Response Mode (SRM):
>
> "This header shall be used during CONNECT, DISCONNECT, SETPATH, PUT
> and GET operations only (see the table below)."
>
> This is also restated in 3. Session Protocol:
> " 2) Single Response Mode (SRM) can be negotiated during CONNECT,
> PUT, GET, or SETPATH, which will remain enabled until a DISCONNECT or
> OBEX transport disconnect occur."
>
> And it continues stating that SRM can be enable outside of connection
> scope: "If this mode is enabled outside the scope of an OBEX connection,
> SRM is automatically disabled when the operation that enabled this mode
> completes."
I guess this is only specified if there is ever a transport that actually
specifies this. GOEP v2.0 does not do that.
> So I can only interpret this statements as PUT and GET can be used to
> enable SRM at any point like GOEP 2.0 is doing.
Yes.
> Now after that comes an interesting part, and that one I think makes
> GOEP 2.0 to not adopt SRM headers on CONNECT is the following:
> "This mode will allow multiple outstanding request or response packets
> during the context of an OBEX connection (or OBEX operation). Since
> SRM allows multiple outstanding packets, this mode also makes
> available the ability for multiple outstanding OBEX operations to
> exist at a time."
>
> Now look at the example 7.14 PUT and GET during Single Response Mode (SRM):
This example is the core of the SRM mess.
> "Start GET operation without waiting for the Server to respond to the
> outstanding PUT operation. Since only one response occurs per
> operation, it is known that the first Server response packet will
> pertain to the PUT operation."
>
> I believe this is why GOEP 2.0 want to avoid using SRM per connection,
> because multiple outstanding operations are not very easy to be
> handled without an id/serial to identify them which OBEX doesn't have,
> for instance there could be SRMP headers in responses which alters SRM
> and breaks the assumption that only one response occurs per operation.
No. Unless the server does not correctly handle SRM, this assumption is not
broken. The main problem is actually when sending an ABORT after the GET that
comes too late. Then you get responses: one SUCCESS for GET and one SUCCESS
for ABORT instead of one SUCCESS for GET. This can break the synchronisation
of client and server and the writers of the OBEX spec did not consider this.
> IMO what the OBEX spec writer suggests is that the operations are
> queued by the server, so it only starts responding once the previous
> operation is finished, but this doesn't have any benefit over enabling
> SRM per operation as GOEP 2.0 suggests.
And there is the problem: "SRM per operation" is not specified by OBEX v1.4.
There, it is always "SRM per connection", so this must be something new from
OBEX v1.5.
> >> > Additionally, the spec says:
> >> > "Client devices supporting SRM must issue a Single Response Mode (SRM)
> >> > header [...] during all CONNECT requests [...]".
> >>
> >> That is still possible, by adding the headers manually, but
> >
> > How should the application ever know better than the part that implements
> > the low-level details of OBEX? That is a very unlikely case.
>
> I mean we have an API to add headers to a packet which the application
> can use to modify the behavior in e.g. CONNECT, but we won't be adding
> this headers automatically because there are not always recommended by
> specification using OBEX as GOEP 2.0. The impact is minimal since we
> still enable SRM per operation.
>
> >> automatically configuration will only be done for PUT/GET because of
> >>
> >> the way GOEP 2.0 is using SRM restrict that:
> >> > "SRM headers shall not be sent in the Connect request or response
> >> > packets (note, this is to preserve backwards compatibility). SRM
> >> > shall be enabled through Put and Get operations only."
> >>
> >> You can get GOEP 2.0 from bluetooth.org, you might find it interesting
> >> specially the examples are completely different from the ones OBEX
> >> spec has and we have to support them both.
> >
> > I have read that. BTW, it should be noticed that GOEP v2.0 references
> > OBEX v1.5, not v1.4. Maybe those differences come from the latest OBEX
> > specification?
>
> Good point, I don't have 1.5 yet, hope to get some clarification about
> it during the next UPF.
>
> > Also note that the first example (page 14) is _wrong_ as the "SRM
> > disabled" in the last line of the table is wrong. See all following
> > examples that do not have the entry. Also, this would directly
> > contradict the OBEX specification. The Bluetooth spec should get an
> > errata for this, ASAP, as this will most likely cause interoperability
> > issues.
>
> The way the author appears to interpret is that if SRM was enable in
> PUT/GET it will remain active only during that operation:
>
> GOEP 2.0 Page 1.4 - 4.6 Using Single Response Mode:
>
> "SRM will remain in effect for the duration of the operation that
> enabled it (PUT or GET), at which time it will automatically be
> disabled and revert back to the normal GOEP request/response model."
>
> OBEX spec says that if SRM is enabled during the connection context it
> is valid to all subsequent operations, I guess GOEP 2.0 interpret
> context as just within CONNECT request/response. Again I think this is
> to avoid having to support multiple outstanding operations, but it is
> not stated anywhere so it is just my guess.
This must be from OBEX v1.5. Good guess, then :-)
Strange enough they chose to support SRM but only a less efficient setup (you
still have twice as much RTTs for each PUT and GET than with SRM enabled with
CONNECT.
Unrelated to this new information about SRM, I saw this in GOEP v2.0:
"The non-Body headers such as Name or Description that may exceed the allowed
OBEX packet size may be issued multiple times in consecutive packets within a
single PUT/GET operation."
Does this mean that the value of a second NAME header gets appended to the
value of the first NAME header? Ouch.
HS
Hi Hendrik,
On Wed, Jan 4, 2012 at 8:48 PM, Hendrik Sattler <[email protected]> wrote:
>> That is what the OBEX spec says, but GOEP 2.0 says otherwise, actually
>> PUT and GET may also be used without CONNECT and the even the OBEX
>> spec state both PUT, GET and SETPATH supporting SRM headers.
>
> Using PUT and GET without CONNECT is not very efficient as you limit yourself
> to the lowest MTU and default inbox (OPP). This was only there for some kind
> of stateless IrDA connection which does not qualify for the requirements of
> SRM, anyway
Efficiency was no much of the point here, but what operation do
support as stated in 2.2.23 Single Response Mode (SRM):
"This header shall be used during CONNECT, DISCONNECT, SETPATH, PUT
and GET operations only (see the table below)."
This is also restated in 3. Session Protocol:
" 2) Single Response Mode (SRM) can be negotiated during CONNECT,
PUT, GET, or SETPATH, which will remain enabled until a DISCONNECT or
OBEX transport disconnect occur."
And it continues stating that SRM can be enable outside of connection scope:
"If this mode is enabled outside the scope of an OBEX connection,
SRM is automatically disabled when the operation that enabled this
mode completes."
So I can only interpret this statements as PUT and GET can be used to
enable SRM at any point like GOEP 2.0 is doing.
Now after that comes an interesting part, and that one I think makes
GOEP 2.0 to not adopt SRM headers on CONNECT is the following:
"This mode will allow multiple outstanding request or response packets
during the context of an OBEX connection (or OBEX operation). Since
SRM allows multiple outstanding packets, this mode also makes
available the ability for multiple outstanding OBEX operations to
exist at a time."
Now look at the example 7.14 PUT and GET during Single Response Mode (SRM):
"Start GET operation without waiting for the Server to respond to the
outstanding PUT operation. Since only one response occurs per
operation, it is known that the first Server response packet will
pertain to the PUT operation."
I believe this is why GOEP 2.0 want to avoid using SRM per connection,
because multiple outstanding operations are not very easy to be
handled without an id/serial to identify them which OBEX doesn't have,
for instance there could be SRMP headers in responses which alters SRM
and breaks the assumption that only one response occurs per operation.
IMO what the OBEX spec writer suggests is that the operations are
queued by the server, so it only starts responding once the previous
operation is finished, but this doesn't have any benefit over enabling
SRM per operation as GOEP 2.0 suggests.
>> > Additionally, the spec says:
>> > "Client devices supporting SRM must issue a Single Response Mode (SRM)
>> > header [...] during all CONNECT requests [...]".
>>
>> That is still possible, by adding the headers manually, but
>
> How should the application ever know better than the part that implements the
> low-level details of OBEX? That is a very unlikely case.
I mean we have an API to add headers to a packet which the application
can use to modify the behavior in e.g. CONNECT, but we won't be adding
this headers automatically because there are not always recommended by
specification using OBEX as GOEP 2.0. The impact is minimal since we
still enable SRM per operation.
>> automatically configuration will only be done for PUT/GET because of
>> the way GOEP 2.0 is using SRM restrict that:
>> > ? "SRM headers shall not be sent in the Connect request or response
>> > ? packets (note, this is to preserve backwards compatibility). SRM shall
>> > ? be enabled through Put and Get operations only."
>>
>> You can get GOEP 2.0 from bluetooth.org, you might find it interesting
>> specially the examples are completely different from the ones OBEX
>> spec has and we have to support them both.
>
> I have read that. BTW, it should be noticed that GOEP v2.0 references OBEX
> v1.5, not v1.4. Maybe those differences come from the latest OBEX
> specification?
Good point, I don't have 1.5 yet, hope to get some clarification about
it during the next UPF.
> Also note that the first example (page 14) is _wrong_ as the "SRM disabled" in
> the last line of the table is wrong. See all following examples that do not
> have the entry. Also, this would directly contradict the OBEX specification.
> The Bluetooth spec should get an errata for this, ASAP, as this will most
> likely cause interoperability issues.
The way the author appears to interpret is that if SRM was enable in
PUT/GET it will remain active only during that operation:
GOEP 2.0 Page 1.4 - 4.6 Using Single Response Mode:
"SRM will remain in effect for the duration of the operation that
enabled it (PUT or GET), at which time it will automatically be
disabled and revert back to the normal GOEP request/response model."
OBEX spec says that if SRM is enabled during the connection context it
is valid to all subsequent operations, I guess GOEP 2.0 interpret
context as just within CONNECT request/response. Again I think this is
to avoid having to support multiple outstanding operations, but it is
not stated anywhere so it is just my guess.
> SRM is a complete mess, anyway.
Indeed.
--
Luiz Augusto von Dentz
Hi Luiz,
Am Dienstag, 3. Januar 2012, 23:31:13 schrieb Luiz Augusto von Dentz:
> On Tue, Jan 3, 2012 at 5:58 PM, Hendrik Sattler <[email protected]>
wrote:
> >> So only in case of PUT or GET requests SRM is automatically configured,
> >> applications can still enable it manually for other operations by adding
> >> the headers like before but it is not recommended.
> >>
> >> Note that it would be a good practice to indicate SRM support by using
> >> value 0x02, but since that should happens during CONNECT command it is
> >> not done automatically for requests when acting as a client, server
> >> responding to indicate requests will automatically add SRM headers
> >> though.
> >
> > I think you misread the specs here.
> >
> > SRM is not enabled for "requests" but for sessions and SRM is valid until
> > disabled again. This includes _all_ requests and responses, not only GET
> > and PUT but the authors of the OBEX spec have a strange way to write
> > things. The SRMP headers only affect PUT and PUT, though.
s/PUT and PUT/PUT and GET/
> That is what the OBEX spec says, but GOEP 2.0 says otherwise, actually
> PUT and GET may also be used without CONNECT and the even the OBEX
> spec state both PUT, GET and SETPATH supporting SRM headers.
Using PUT and GET without CONNECT is not very efficient as you limit yourself
to the lowest MTU and default inbox (OPP). This was only there for some kind
of stateless IrDA connection which does not qualify for the requirements of
SRM, anyway
> > Additionally, the spec says:
> > "Client devices supporting SRM must issue a Single Response Mode (SRM)
> > header [...] during all CONNECT requests [...]".
>
> That is still possible, by adding the headers manually, but
How should the application ever know better than the part that implements the
low-level details of OBEX? That is a very unlikely case.
> automatically configuration will only be done for PUT/GET because of
> the way GOEP 2.0 is using SRM restrict that:
> > "SRM headers shall not be sent in the Connect request or response
> > packets (note, this is to preserve backwards compatibility). SRM shall
> > be enabled through Put and Get operations only."
>
> You can get GOEP 2.0 from bluetooth.org, you might find it interesting
> specially the examples are completely different from the ones OBEX
> spec has and we have to support them both.
I have read that. BTW, it should be noticed that GOEP v2.0 references OBEX
v1.5, not v1.4. Maybe those differences come from the latest OBEX
specification?
Also note that the first example (page 14) is _wrong_ as the "SRM disabled" in
the last line of the table is wrong. See all following examples that do not
have the entry. Also, this would directly contradict the OBEX specification.
The Bluetooth spec should get an errata for this, ASAP, as this will most
likely cause interoperability issues.
SRM is a complete mess, anyway.
HS
Hi Hendrik,
On Tue, Jan 3, 2012 at 5:58 PM, Hendrik Sattler <[email protected]> wrote:
>>
>> So only in case of PUT or GET requests SRM is automatically configured,
>> applications can still enable it manually for other operations by adding
>> the headers like before but it is not recommended.
>>
>> Note that it would be a good practice to indicate SRM support by using
>> value 0x02, but since that should happens during CONNECT command it is
>> not done automatically for requests when acting as a client, server
>> responding to indicate requests will automatically add SRM headers though.
>
> I think you misread the specs here.
>
> SRM is not enabled for "requests" but for sessions and SRM is valid until
> disabled again. This includes _all_ requests and responses, not only GET and
> PUT but the authors of the OBEX spec have a strange way to write things.
> The SRMP headers only affect PUT and PUT, though.
That is what the OBEX spec says, but GOEP 2.0 says otherwise, actually
PUT and GET may also be used without CONNECT and the even the OBEX
spec state both PUT, GET and SETPATH supporting SRM headers.
> Additionally, the spec says:
> "Client devices supporting SRM must issue a Single Response Mode (SRM) header
> [...] during all CONNECT requests [...]".
That is still possible, by adding the headers manually, but
automatically configuration will only be done for PUT/GET because of
the way GOEP 2.0 is using SRM restrict that:
> ? "SRM headers shall not be sent in the Connect request or response
> ? packets (note, this is to preserve backwards compatibility). SRM shall
> ? be enabled through Put and Get operations only."
You can get GOEP 2.0 from bluetooth.org, you might find it interesting
specially the examples are completely different from the ones OBEX
spec has and we have to support them both.
--
Luiz Augusto von Dentz
Am Dienstag, 3. Januar 2012, 14:52:23 schrieb Luiz Augusto von Dentz:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This simplifies the applications so SRM setup phase became transparent
> while using SOCK_SEQPACKET which is useful for GOEP 2.0 since in that
> case we can only use SRM if the transport is L2CAP.
>
> This also follows GOEP 2.0 Page 14 - 4.6 Using Single Response Mode:
>
> "The Server cannot issue an enable request, but can only issue a
> response to an enable request from the Client. SRM will remain in
> effect for the duration of the operation that enabled it (PUT or GET)
> ...
> SRM headers shall not be sent in CONNECT request or response packets."
>
> and Page 22 - 5.4 Establishment of OBEX Connection:
>
> "SRM headers shall not be sent in the Connect request or response
> packets (note, this is to preserve backwards compatibility). SRM shall
> be enabled through Put and Get operations only."
>
> So only in case of PUT or GET requests SRM is automatically configured,
> applications can still enable it manually for other operations by adding
> the headers like before but it is not recommended.
>
> Note that it would be a good practice to indicate SRM support by using
> value 0x02, but since that should happens during CONNECT command it is
> not done automatically for requests when acting as a client, server
> responding to indicate requests will automatically add SRM headers though.
I think you misread the specs here.
SRM is not enabled for "requests" but for sessions and SRM is valid until
disabled again. This includes _all_ requests and responses, not only GET and
PUT but the authors of the OBEX spec have a strange way to write things.
The SRMP headers only affect PUT and PUT, though.
Additionally, the spec says:
"Client devices supporting SRM must issue a Single Response Mode (SRM) header
[...] during all CONNECT requests [...]".
HS
From: Luiz Augusto von Dentz <[email protected]>
Now that SRM is automatically configured when the transport type is
SOCK_SEQPACKET all tests that uses this transport are already testing
SRM so there is no need to keep adding the headers manually.
In addition to that remove the tests for SRM using SOCK_STREAM since
those are currently not supported without including the headers.
---
unit/test-gobex-transfer.c | 313 +++++++-------------------------------------
1 files changed, 48 insertions(+), 265 deletions(-)
diff --git a/unit/test-gobex-transfer.c b/unit/test-gobex-transfer.c
index eb6a55e..d4cf181 100644
--- a/unit/test-gobex-transfer.c
+++ b/unit/test-gobex-transfer.c
@@ -85,18 +85,18 @@ static guint8 get_req_first_app[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x2a,
0, 1, 2, 3 };
static guint8 get_req_first_srm[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x25,
+ G_OBEX_HDR_SRM, 0x01,
G_OBEX_HDR_TYPE, 0x00, 0x0b,
'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
G_OBEX_HDR_NAME, 0x00, 0x15,
- 0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
- G_OBEX_HDR_SRM, 0x01 };
+ 0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0 };
static guint8 get_req_first_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x27,
+ G_OBEX_HDR_SRM, 0x01,
G_OBEX_HDR_TYPE, 0x00, 0x0b,
'f', 'o', 'o', '/', 'b', 'a', 'r', '\0',
G_OBEX_HDR_NAME, 0x00, 0x15,
0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
- G_OBEX_HDR_SRM, 0x01,
G_OBEX_HDR_SRMP, 0x01 };
static guint8 get_req_last[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x03, };
@@ -407,7 +407,7 @@ static void handle_put_seq(GObex *obex, GObexPacket *req,
g_main_loop_quit(d->mainloop);
}
-static void test_put_rsp_seq(int sock_type)
+static void test_stream_put_rsp(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -453,16 +453,6 @@ static void test_put_rsp_seq(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_rsp(void)
-{
- test_put_rsp_seq(SOCK_STREAM);
-}
-
-static void test_packet_put_rsp(void)
-{
- test_put_rsp_seq(SOCK_SEQPACKET);
-}
-
static void handle_put_seq_wait(GObex *obex, GObexPacket *req,
gpointer user_data)
{
@@ -479,14 +469,13 @@ static void handle_put_seq_wait(GObex *obex, GObexPacket *req,
id = g_obex_put_rsp(obex, req, rcv_seq, transfer_complete, d,
&d->err,
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
G_OBEX_HDR_INVALID);
if (id == 0)
g_main_loop_quit(d->mainloop);
}
-static void test_put_rsp_seq_srm_wait(int sock_type)
+static void test_packet_put_rsp_wait(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -502,7 +491,7 @@ static void test_put_rsp_seq_srm_wait(int sock_type)
{ put_req_last, sizeof(put_req_last) },
{ NULL, 0 } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -533,39 +522,7 @@ static void test_put_rsp_seq_srm_wait(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_rsp_srm_wait(void)
-{
- test_put_rsp_seq_srm_wait(SOCK_STREAM);
-}
-
-static void test_packet_put_rsp_srm_wait(void)
-{
- test_put_rsp_seq_srm_wait(SOCK_SEQPACKET);
-}
-
-static void handle_put_seq_srm(GObex *obex, GObexPacket *req,
- gpointer user_data)
-{
- struct test_data *d = user_data;
- guint8 op = g_obex_packet_get_operation(req, NULL);
- guint id;
-
- if (op != G_OBEX_OP_PUT) {
- d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
- "Unexpected opcode 0x%02x", op);
- g_main_loop_quit(d->mainloop);
- return;
- }
-
- id = g_obex_put_rsp(obex, req, rcv_seq, transfer_complete, d,
- &d->err,
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
- G_OBEX_HDR_INVALID);
- if (id == 0)
- g_main_loop_quit(d->mainloop);
-}
-
-static void test_put_rsp_seq_srm(int sock_type)
+static void test_packet_put_rsp(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -581,7 +538,7 @@ static void test_put_rsp_seq_srm(int sock_type)
{ put_req_last, sizeof(put_req_last) },
{ NULL, 0 } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -590,8 +547,7 @@ static void test_put_rsp_seq_srm(int sock_type)
timer_id = g_timeout_add_seconds(1, test_timeout, &d);
- g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_seq_srm,
- &d);
+ g_obex_add_request_function(obex, G_OBEX_OP_PUT, handle_put_seq, &d);
g_io_channel_write_chars(io, (char *) put_req_first_srm,
sizeof(put_req_first_srm), NULL,
@@ -612,16 +568,6 @@ static void test_put_rsp_seq_srm(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_rsp_srm(void)
-{
- test_put_rsp_seq_srm(SOCK_STREAM);
-}
-
-static void test_packet_put_rsp_srm(void)
-{
- test_put_rsp_seq_srm(SOCK_SEQPACKET);
-}
-
static void test_get_req(void)
{
GIOChannel *io;
@@ -663,7 +609,7 @@ static void test_get_req(void)
g_assert_no_error(d.err);
}
-static void test_get_req_seq(int sock_type)
+static void test_stream_get_req(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -679,7 +625,7 @@ static void test_get_req_seq(int sock_type)
{ get_rsp_zero, sizeof(get_rsp_zero) },
{ get_rsp_last, sizeof(get_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -708,18 +654,8 @@ static void test_get_req_seq(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_req(void)
-{
- test_get_req_seq(SOCK_STREAM);
-}
-
static void test_packet_get_req(void)
{
- test_get_req_seq(SOCK_SEQPACKET);
-}
-
-static void test_get_req_seq_srm(int sock_type)
-{
GIOChannel *io;
GIOCondition cond;
guint io_id, timer_id;
@@ -734,7 +670,7 @@ static void test_get_req_seq_srm(int sock_type)
{ get_rsp_zero, sizeof(get_rsp_zero) },
{ get_rsp_last, sizeof(get_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -746,7 +682,6 @@ static void test_get_req_seq_srm(int sock_type)
g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "file.txt",
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
@@ -764,17 +699,7 @@ static void test_get_req_seq_srm(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_req_srm(void)
-{
- test_get_req_seq_srm(SOCK_STREAM);
-}
-
-static void test_packet_get_req_srm(void)
-{
- test_get_req_seq_srm(SOCK_SEQPACKET);
-}
-
-static void test_get_req_seq_srm_wait(int sock_type)
+static void test_packet_get_req_wait(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -790,7 +715,7 @@ static void test_get_req_seq_srm_wait(int sock_type)
{ get_rsp_zero, sizeof(get_rsp_zero) },
{ get_rsp_last, sizeof(get_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -802,7 +727,6 @@ static void test_get_req_seq_srm_wait(int sock_type)
g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "file.txt",
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
@@ -821,17 +745,7 @@ static void test_get_req_seq_srm_wait(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_req_srm_wait(void)
-{
- test_get_req_seq_srm_wait(SOCK_STREAM);
-}
-
-static void test_packet_get_req_srm_wait(void)
-{
- test_get_req_seq_srm_wait(SOCK_SEQPACKET);
-}
-
-static void test_get_req_seq_srm_wait_next(int sock_type)
+static void test_packet_get_req_wait_next(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -848,7 +762,7 @@ static void test_get_req_seq_srm_wait_next(int sock_type)
{ get_rsp_zero_wait_next, sizeof(get_rsp_zero_wait_next) },
{ get_rsp_last, sizeof(get_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -860,7 +774,6 @@ static void test_get_req_seq_srm_wait_next(int sock_type)
g_obex_get_req(obex, rcv_seq, transfer_complete, &d, &d.err,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "file.txt",
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
@@ -878,16 +791,6 @@ static void test_get_req_seq_srm_wait_next(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_req_srm_wait_next(void)
-{
- test_get_req_seq_srm_wait_next(SOCK_STREAM);
-}
-
-static void test_packet_get_req_srm_wait_next(void)
-{
- test_get_req_seq_srm_wait_next(SOCK_SEQPACKET);
-}
-
static void test_get_req_app(void)
{
GIOChannel *io;
@@ -971,7 +874,7 @@ static void handle_get(GObex *obex, GObexPacket *req, gpointer user_data)
g_main_loop_quit(d->mainloop);
}
-static void test_put_req_seq(int sock_type)
+static void test_stream_put_req(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -987,7 +890,7 @@ static void test_put_req_seq(int sock_type)
{ put_rsp_first, sizeof(put_rsp_first) },
{ put_rsp_last, sizeof(put_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_STREAM);
d.obex = obex;
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
@@ -1017,17 +920,7 @@ static void test_put_req_seq(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_req(void)
-{
- test_put_req_seq(SOCK_STREAM);
-}
-
-static void test_packet_put_req(void)
-{
- test_put_req_seq(SOCK_SEQPACKET);
-}
-
-static void test_put_req_seq_srm_wait(int sock_type)
+static void test_packet_put_req_wait(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -1043,7 +936,7 @@ static void test_put_req_seq_srm_wait(int sock_type)
{ NULL, 0 },
{ put_rsp_last, sizeof(put_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
d.obex = obex;
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
@@ -1056,7 +949,6 @@ static void test_put_req_seq_srm_wait(int sock_type)
g_obex_put_req(obex, provide_seq, transfer_complete, &d, &d.err,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "random.bin",
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
@@ -1074,17 +966,7 @@ static void test_put_req_seq_srm_wait(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_req_srm_wait(void)
-{
- test_put_req_seq_srm_wait(SOCK_STREAM);
-}
-
-static void test_packet_put_req_srm_wait(void)
-{
- test_put_req_seq_srm_wait(SOCK_SEQPACKET);
-}
-
-static void test_put_req_seq_srm(int sock_type)
+static void test_packet_put_req(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -1100,7 +982,7 @@ static void test_put_req_seq_srm(int sock_type)
{ NULL, 0 },
{ put_rsp_last, sizeof(put_rsp_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
d.obex = obex;
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
@@ -1113,7 +995,6 @@ static void test_put_req_seq_srm(int sock_type)
g_obex_put_req(obex, provide_seq, transfer_complete, &d, &d.err,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "random.bin",
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_INVALID);
g_assert_no_error(d.err);
@@ -1131,16 +1012,6 @@ static void test_put_req_seq_srm(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_put_req_srm(void)
-{
- test_put_req_seq_srm(SOCK_STREAM);
-}
-
-static void test_packet_put_req_srm(void)
-{
- test_put_req_seq_srm(SOCK_SEQPACKET);
-}
-
static void test_put_req_eagain(void)
{
GIOChannel *io;
@@ -1245,7 +1116,7 @@ static void handle_get_seq(GObex *obex, GObexPacket *req,
g_main_loop_quit(d->mainloop);
}
-static void test_get_rsp_seq(int sock_type)
+static void test_stream_get_rsp(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -1260,7 +1131,7 @@ static void test_get_rsp_seq(int sock_type)
{ get_req_last, sizeof(get_req_last) },
{ get_req_last, sizeof(get_req_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_STREAM);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -1269,8 +1140,7 @@ static void test_get_rsp_seq(int sock_type)
timer_id = g_timeout_add_seconds(1, test_timeout, &d);
- g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq,
- &d);
+ g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq, &d);
g_io_channel_write_chars(io, (char *) get_req_first,
sizeof(get_req_first), NULL, &d.err);
@@ -1290,40 +1160,8 @@ static void test_get_rsp_seq(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_rsp(void)
-{
- test_get_rsp_seq(SOCK_STREAM);
-}
-
static void test_packet_get_rsp(void)
{
- test_get_rsp_seq(SOCK_SEQPACKET);
-}
-
-static void handle_get_seq_srm(GObex *obex, GObexPacket *req,
- gpointer user_data)
-{
- struct test_data *d = user_data;
- guint8 op = g_obex_packet_get_operation(req, NULL);
- guint id;
-
- if (op != G_OBEX_OP_GET) {
- d->err = g_error_new(TEST_ERROR, TEST_ERROR_UNEXPECTED,
- "Unexpected opcode 0x%02x", op);
- g_main_loop_quit(d->mainloop);
- return;
- }
-
- id = g_obex_get_rsp(obex, provide_seq, transfer_complete, d,
- &d->err,
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
- G_OBEX_HDR_INVALID);
- if (id == 0)
- g_main_loop_quit(d->mainloop);
-}
-
-static void test_get_rsp_seq_srm(int sock_type)
-{
GIOChannel *io;
GIOCondition cond;
guint io_id, timer_id;
@@ -1337,7 +1175,7 @@ static void test_get_rsp_seq_srm(int sock_type)
{ NULL, 0 },
{ get_req_last, sizeof(get_req_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -1346,8 +1184,7 @@ static void test_get_rsp_seq_srm(int sock_type)
timer_id = g_timeout_add_seconds(1, test_timeout, &d);
- g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq_srm,
- &d);
+ g_obex_add_request_function(obex, G_OBEX_OP_GET, handle_get_seq, &d);
g_io_channel_write_chars(io, (char *) get_req_first_srm,
sizeof(get_req_first_srm), NULL,
@@ -1368,16 +1205,6 @@ static void test_get_rsp_seq_srm(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_rsp_srm(void)
-{
- test_get_rsp_seq_srm(SOCK_STREAM);
-}
-
-static void test_packet_get_rsp_srm(void)
-{
- test_get_rsp_seq_srm(SOCK_SEQPACKET);
-}
-
static void handle_get_seq_srm_wait(GObex *obex, GObexPacket *req,
gpointer user_data)
{
@@ -1394,14 +1221,13 @@ static void handle_get_seq_srm_wait(GObex *obex, GObexPacket *req,
id = g_obex_get_rsp(obex, provide_seq, transfer_complete, d,
&d->err,
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
G_OBEX_HDR_INVALID);
if (id == 0)
g_main_loop_quit(d->mainloop);
}
-static void test_get_rsp_seq_srm_wait(int sock_type)
+static void test_packet_get_rsp_wait(void)
{
GIOChannel *io;
GIOCondition cond;
@@ -1416,7 +1242,7 @@ static void test_get_rsp_seq_srm_wait(int sock_type)
{ NULL, 0 },
{ get_req_last, sizeof(get_req_last) } } };
- create_endpoints(&obex, &io, sock_type);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
io_id = g_io_add_watch(io, cond, test_io_cb, &d);
@@ -1447,16 +1273,6 @@ static void test_get_rsp_seq_srm_wait(int sock_type)
g_assert_no_error(d.err);
}
-static void test_stream_get_rsp_srm_wait(void)
-{
- test_get_rsp_seq_srm_wait(SOCK_STREAM);
-}
-
-static void test_packet_get_rsp_srm_wait(void)
-{
- test_get_rsp_seq_srm_wait(SOCK_SEQPACKET);
-}
-
static void handle_get_app(GObex *obex, GObexPacket *req, gpointer user_data)
{
struct test_data *d = user_data;
@@ -2216,7 +2032,6 @@ static void conn_complete_put_req_seq_srm(GObex *obex, GError *err,
}
g_obex_put_req(obex, provide_seq, transfer_complete, d, &d->err,
- G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE,
G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
G_OBEX_HDR_NAME, "random.bin",
G_OBEX_HDR_INVALID);
@@ -2238,7 +2053,7 @@ static void test_conn_put_req_seq_srm(void)
{ NULL, 0 },
{ put_rsp_last, sizeof(put_rsp_last) } } };
- create_endpoints(&obex, &io, SOCK_STREAM);
+ create_endpoints(&obex, &io, SOCK_SEQPACKET);
d.obex = obex;
cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
@@ -2293,16 +2108,10 @@ int main(int argc, char *argv[])
g_test_add_func("/gobex/test_get_req_eagain", test_get_rsp_eagain);
g_test_add_func("/gobex/test_stream_put_req", test_stream_put_req);
- g_test_add_func("/gobex/test_packet_put_req", test_packet_put_req);
-
g_test_add_func("/gobex/test_stream_put_rsp", test_stream_put_rsp);
- g_test_add_func("/gobex/test_packet_put_rsp", test_packet_put_rsp);
g_test_add_func("/gobex/test_stream_get_req", test_stream_get_req);
- g_test_add_func("/gobex/test_packet_get_req", test_packet_get_req);
-
g_test_add_func("/gobex/test_stream_get_rsp", test_stream_get_rsp);
- g_test_add_func("/gobex/test_packet_get_rsp", test_packet_get_rsp);
g_test_add_func("/gobex/test_conn_get_req", test_conn_get_req);
g_test_add_func("/gobex/test_conn_get_rsp", test_conn_get_rsp);
@@ -2315,50 +2124,24 @@ int main(int argc, char *argv[])
g_test_add_func("/gobex/test_conn_put_req_seq",
test_conn_put_req_seq);
- g_test_add_func("/gobex/test_stream_put_req_srm",
- test_stream_put_req_srm);
- g_test_add_func("/gobex/test_packet_put_req_srm",
- test_packet_put_req_srm);
-
- g_test_add_func("/gobex/test_stream_put_req_srm_wait",
- test_stream_put_req_srm_wait);
- g_test_add_func("/gobex/test_packet_put_req_srm_wait",
- test_packet_put_req_srm_wait);
-
- g_test_add_func("/gobex/test_stream_put_rsp_srm",
- test_stream_put_rsp_srm);
- g_test_add_func("/gobex/test_packet_put_rsp_srm",
- test_packet_put_rsp_srm);
-
- g_test_add_func("/gobex/test_stream_put_rsp_srm_wait",
- test_stream_put_rsp_srm_wait);
- g_test_add_func("/gobex/test_packet_put_rsp_srm_wait",
- test_packet_put_rsp_srm_wait);
-
- g_test_add_func("/gobex/test_stream_get_rsp_srm",
- test_stream_get_rsp_srm);
- g_test_add_func("/gobex/test_packet_get_rsp_srm",
- test_packet_get_rsp_srm);
-
- g_test_add_func("/gobex/test_stream_get_rsp_srm_wait",
- test_stream_get_rsp_srm_wait);
- g_test_add_func("/gobex/test_packet_get_rsp_srm_wait",
- test_packet_get_rsp_srm_wait);
-
- g_test_add_func("/gobex/test_stream_get_req_srm",
- test_stream_get_req_srm);
- g_test_add_func("/gobex/test_packet_get_req_srm",
- test_packet_get_req_srm);
-
- g_test_add_func("/gobex/test_stream_get_req_srm_wait",
- test_stream_get_req_srm_wait);
- g_test_add_func("/gobex/test_packet_get_req_srm_wait",
- test_packet_get_req_srm_wait);
-
- g_test_add_func("/gobex/test_stream_get_req_srm_wait_next",
- test_stream_get_req_srm_wait_next);
- g_test_add_func("/gobex/test_packet_get_req_srm_wait_next",
- test_packet_get_req_srm_wait_next);
+ g_test_add_func("/gobex/test_packet_put_req", test_packet_put_req);
+ g_test_add_func("/gobex/test_packet_put_req_wait",
+ test_packet_put_req_wait);
+
+ g_test_add_func("/gobex/test_packet_put_rsp", test_packet_put_rsp);
+ g_test_add_func("/gobex/test_packet_put_rsp_wait",
+ test_packet_put_rsp_wait);
+
+ g_test_add_func("/gobex/test_packet_get_rsp", test_packet_get_rsp);
+ g_test_add_func("/gobex/test_packet_get_rsp_wait",
+ test_packet_get_rsp_wait);
+
+ g_test_add_func("/gobex/test_packet_get_req", test_packet_get_req);
+ g_test_add_func("/gobex/test_packet_get_req_wait",
+ test_packet_get_req_wait);
+
+ g_test_add_func("/gobex/test_packet_get_req_wait_next",
+ test_packet_get_req_wait_next);
g_test_add_func("/gobex/test_conn_put_req_seq_srm",
test_conn_put_req_seq_srm);
--
1.7.7.4
From: Luiz Augusto von Dentz <[email protected]>
This simplifies the applications so SRM setup phase became transparent
while using SOCK_SEQPACKET which is useful for GOEP 2.0 since in that
case we can only use SRM if the transport is L2CAP.
This also follows GOEP 2.0 Page 14 - 4.6 Using Single Response Mode:
"The Server cannot issue an enable request, but can only issue a
response to an enable request from the Client. SRM will remain in
effect for the duration of the operation that enabled it (PUT or GET)
...
SRM headers shall not be sent in CONNECT request or response packets."
and Page 22 - 5.4 Establishment of OBEX Connection:
"SRM headers shall not be sent in the Connect request or response
packets (note, this is to preserve backwards compatibility). SRM shall
be enabled through Put and Get operations only."
So only in case of PUT or GET requests SRM is automatically configured,
applications can still enable it manually for other operations by adding
the headers like before but it is not recommended.
Note that it would be a good practice to indicate SRM support by using
value 0x02, but since that should happens during CONNECT command it is
not done automatically for requests when acting as a client, server
responding to indicate requests will automatically add SRM headers though.
---
gobex/gobex.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 55 insertions(+), 1 deletions(-)
diff --git a/gobex/gobex.c b/gobex/gobex.c
index ed051ee..0db4567 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -72,6 +72,7 @@ struct _GObex {
size_t tx_sent;
gboolean suspended;
+ gboolean use_srm;
struct srm_config *srm;
@@ -505,6 +506,9 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
guint8 op;
gboolean final;
+ if (!obex->use_srm)
+ return;
+
op = g_obex_packet_get_operation(pkt, &final);
hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
@@ -538,6 +542,24 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
set_srm(obex, op, G_OBEX_SRM_DISABLE);
}
+static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
+{
+ GObexHeader *hdr;
+
+ if (!obex->use_srm || obex->srm == NULL)
+ return;
+
+ if (obex->srm->enabled)
+ return;
+
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+ if (hdr != NULL)
+ return;
+
+ hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
+ g_obex_packet_prepend_header(pkt, hdr);
+}
+
gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
{
struct pending_pkt *p;
@@ -552,8 +574,14 @@ gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
return FALSE;
}
- if (obex->rx_last_op == G_OBEX_OP_CONNECT)
+ switch (obex->rx_last_op) {
+ case G_OBEX_OP_CONNECT:
prepare_connect_rsp(obex, pkt);
+ case G_OBEX_OP_GET:
+ case G_OBEX_OP_PUT:
+ prepare_srm_rsp(obex, pkt);
+ break;
+ }
setup_srm(obex, pkt, TRUE);
@@ -567,6 +595,24 @@ gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
return ret;
}
+static void prepare_srm_req(GObex *obex, GObexPacket *pkt)
+{
+ GObexHeader *hdr;
+
+ if (!obex->use_srm)
+ return;
+
+ if (obex->srm != NULL && obex->srm->enabled)
+ return;
+
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRM);
+ if (hdr != NULL)
+ return;
+
+ hdr = g_obex_header_new_uint8(G_OBEX_HDR_SRM, G_OBEX_SRM_ENABLE);
+ g_obex_packet_prepend_header(pkt, hdr);
+}
+
guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
GObexResponseFunc func, gpointer user_data,
GError **err)
@@ -574,9 +620,16 @@ guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
GObexHeader *hdr;
struct pending_pkt *p;
static guint id = 1;
+ guint8 op;
g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
+ op = g_obex_packet_get_operation(req, NULL);
+ if (op == G_OBEX_OP_PUT || op == G_OBEX_OP_GET) {
+ /* Only enable SRM automatically for GET and PUT */
+ prepare_srm_req(obex, req);
+ }
+
setup_srm(obex, req, TRUE);
if (obex->conn_id == CONNID_INVALID)
@@ -1226,6 +1279,7 @@ GObex *g_obex_new(GIOChannel *io, GObexTransportType transport_type,
obex->write = write_stream;
break;
case G_OBEX_TRANSPORT_PACKET:
+ obex->use_srm = TRUE;
obex->read = read_packet;
obex->write = write_packet;
break;
--
1.7.7.4
From: Luiz Augusto von Dentz <[email protected]>
Single Response Mode Parameters is a 1-byte quantity containing the
value for the parameters used for SRM.
---
gobex/gobex.c | 39 ++++++++++++++++++++++++++++++++++-----
1 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/gobex/gobex.c b/gobex/gobex.c
index e225a50..ed051ee 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -50,6 +50,8 @@ struct srm_config {
guint8 op;
gboolean enabled;
guint8 srm;
+ guint8 srmp;
+ gboolean outgoing;
};
struct _GObex {
@@ -441,6 +443,21 @@ static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
g_obex_packet_prepend_header(rsp, connid);
}
+static void set_srmp(GObex *obex, guint8 srmp, gboolean outgoing)
+{
+ struct srm_config *config = obex->srm;
+
+ if (config == NULL)
+ return;
+
+ /* Dont't reset if direction doesn't match */
+ if (srmp > G_OBEX_SRMP_NEXT_WAIT && config->outgoing != outgoing)
+ return;
+
+ config->srmp = srmp;
+ config->outgoing = outgoing;
+}
+
static void set_srm(GObex *obex, guint8 op, guint8 srm)
{
struct srm_config *config = obex->srm;
@@ -482,7 +499,7 @@ done:
obex->srm = NULL;
}
-static void setup_srm(GObex *obex, GObexPacket *pkt)
+static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
{
GObexHeader *hdr;
guint8 op;
@@ -498,6 +515,15 @@ static void setup_srm(GObex *obex, GObexPacket *pkt)
set_srm(obex, op, srm);
}
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
+ if (hdr != NULL) {
+ guint8 srmp;
+ g_obex_header_get_uint8(hdr, &srmp);
+ g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
+ set_srmp(obex, srmp, outgoing);
+ } else
+ set_srmp(obex, -1, outgoing);
+
if (obex->srm == NULL || !obex->srm->enabled || !final)
return;
@@ -529,7 +555,7 @@ gboolean g_obex_send(GObex *obex, GObexPacket *pkt, GError **err)
if (obex->rx_last_op == G_OBEX_OP_CONNECT)
prepare_connect_rsp(obex, pkt);
- setup_srm(obex, pkt);
+ setup_srm(obex, pkt, TRUE);
p = g_new0(struct pending_pkt, 1);
p->pkt = pkt;
@@ -551,7 +577,7 @@ guint g_obex_send_req(GObex *obex, GObexPacket *req, gint timeout,
g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
- setup_srm(obex, req);
+ setup_srm(obex, req, TRUE);
if (obex->conn_id == CONNID_INVALID)
goto create_pending;
@@ -773,6 +799,9 @@ gboolean g_obex_srm_active(GObex *obex)
if (obex->srm == NULL || !obex->srm->enabled)
goto done;
+ if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
+ goto done;
+
ret = TRUE;
done:
g_obex_debug(G_OBEX_DEBUG_COMMAND, "%s", ret ? "yes" : "no");
@@ -814,7 +843,7 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
if (opcode == G_OBEX_OP_CONNECT)
parse_connect_data(obex, rsp);
- setup_srm(obex, rsp);
+ setup_srm(obex, rsp, FALSE);
if (!g_obex_srm_active(obex))
return final;
@@ -905,7 +934,7 @@ static int parse_request(GObex *obex, GObexPacket *req)
return -G_OBEX_RSP_SERVICE_UNAVAILABLE;
}
- setup_srm(obex, req);
+ setup_srm(obex, req, FALSE);
return op;
}
--
1.7.7.4