OSDN Git Service

gobex: Add basic packet sending support
authorJohan Hedberg <johan.hedberg@intel.com>
Mon, 27 Jun 2011 11:08:33 +0000 (14:08 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 4 Dec 2012 21:21:56 +0000 (22:21 +0100)
gobex/gobex.c
unit/test-gobex.c

index 80a8bc4..485b91d 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <unistd.h>
 #include <string.h>
+#include <errno.h>
 
 #include "gobex.h"
 
@@ -77,6 +78,12 @@ struct _GObex {
        size_t rx_data;
        guint16 rx_pkt_len;
 
+       guint8 *tx_buf;
+       size_t tx_data;
+       size_t tx_sent;
+
+       guint write_source;
+
        guint16 rx_mtu;
        guint16 tx_mtu;
 
@@ -536,13 +543,108 @@ failed:
        return NULL;
 }
 
+static ssize_t g_obex_packet_encode(GObexPacket *pkt, uint8_t *buf, size_t len)
+{
+       size_t count;
+       guint16 pkt_len, u16;
+       GSList *l;
+
+       pkt_len = 3 + pkt->data_len + pkt->hlen;
+
+       if (pkt_len > len)
+               return -ENOBUFS;
+
+       buf[0] = pkt->opcode;
+       if (pkt->final)
+               buf[0] |= G_OBEX_FINAL;
+
+       u16 = g_htons(pkt_len);
+       memcpy(&buf[1], &u16, sizeof(u16));
+
+       if (pkt->data_len > 0) {
+               if (pkt->data_policy == G_OBEX_DATA_REF)
+                       memcpy(&buf[3], pkt->data.buf_ref, pkt->data_len);
+               else
+                       memcpy(&buf[3], pkt->data.buf, pkt->data_len);
+       }
+
+       count = 3 + pkt->data_len;
+
+       for (l = pkt->headers; l != NULL; l = g_slist_next(l)) {
+               GObexHeader *hdr = l->data;
+               count += g_obex_header_encode(hdr, buf + count, len - count);
+       }
+
+       g_assert_cmpuint(count, ==, pkt_len);
+
+       return count;
+}
+
+static gboolean write_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       GObex *obex = user_data;
+       GIOStatus status;
+       gsize bytes_written;
+       gchar *buf;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               goto done;
+
+       if (obex->tx_data == 0) {
+               GObexPacket *pkt = g_queue_pop_head(obex->req_queue);
+               ssize_t len;
+
+               if (pkt == NULL)
+                       goto done;
+
+               len = g_obex_packet_encode(pkt, obex->tx_buf, obex->tx_mtu);
+
+               g_obex_packet_free(pkt);
+
+               if (len < 0)
+                       goto done;
+
+               obex->tx_data = len;
+               obex->tx_sent = 0;
+       }
+
+       buf = (gchar *) &obex->tx_buf[obex->tx_sent];
+       status = g_io_channel_write_chars(io, buf, obex->tx_data,
+                                                       &bytes_written, NULL);
+       if (status != G_IO_STATUS_NORMAL)
+               goto done;
+
+       obex->tx_sent += bytes_written;
+       obex->tx_data -= bytes_written;
+
+       if (obex->tx_data > 0 || g_queue_get_length(obex->req_queue) > 0)
+               return TRUE;
+
+done:
+       obex->tx_data = 0;
+       obex->write_source = 0;
+       return FALSE;
+}
+
 gboolean g_obex_send(GObex *obex, GObexPacket *pkt)
 {
+       GIOCondition cond;
+
        if (obex == NULL || pkt == NULL)
                return FALSE;
 
        g_queue_push_tail(obex->req_queue, pkt);
 
+       if (g_queue_get_length(obex->req_queue) > 1)
+               return TRUE;
+
+       cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex);
+
        return TRUE;
 }
 
@@ -658,8 +760,10 @@ GObex *g_obex_new(GIOChannel *io)
        obex->tx_mtu = G_OBEX_MINIMUM_MTU;
        obex->req_queue = g_queue_new();
        obex->rx_buf = g_malloc(obex->rx_mtu);
+       obex->tx_buf = g_malloc(obex->tx_mtu);
 
        g_io_channel_set_encoding(io, NULL, NULL);
+       g_io_channel_set_buffered(io, FALSE);
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
        obex->io_source = g_io_add_watch(io, cond, incoming_data, obex);
 
@@ -694,6 +798,7 @@ void g_obex_unref(GObex *obex)
                g_source_remove(obex->io_source);
 
        g_free(obex->rx_buf);
+       g_free(obex->tx_buf);
 
        g_free(obex);
 }
index 99170af..368a602 100644 (file)
@@ -76,15 +76,20 @@ static void dump_bytes(uint8_t *buf, size_t buf_len)
        g_printerr("\n");
 }
 
-static void assert_memequal(void *mem1, size_t len1, void *mem2, size_t len2)
+static void dump_bufs(void *mem1, size_t len1, void *mem2, size_t len2)
 {
-       if (len1 == len2 && memcmp(mem1, mem2, len1) == 0)
-               return;
-
        g_printerr("\nExpected: ");
        dump_bytes(mem1, len1);
        g_printerr("Got:      ");
        dump_bytes(mem2, len2);
+}
+
+static void assert_memequal(void *mem1, size_t len1, void *mem2, size_t len2)
+{
+       if (len1 == len2 && memcmp(mem1, mem2, len1) == 0)
+               return;
+
+       dump_bufs(mem1, len1, mem2, len2);
 
        g_assert(0);
 }
@@ -103,6 +108,98 @@ static gboolean test_timeout(gpointer user_data)
        return FALSE;
 }
 
+static gboolean handle_connect_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       GError **err = user_data;
+       GIOStatus status;
+       gsize rbytes;
+       char buf[255];
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Unexpected condition %d on socket", cond);
+               goto done;
+       }
+
+       status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Reading data failed with status %d", status);
+               goto done;
+       }
+
+       if (rbytes != sizeof(pkt_connect_req)) {
+               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Got %zu bytes instead of %zu",
+                               rbytes, sizeof(pkt_connect_req));
+               dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
+                                                               buf, rbytes);
+               goto done;
+       }
+
+       if (memcmp(buf, pkt_connect_req, rbytes) != 0) {
+               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Mismatch with received data");
+               dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
+                                                               buf, rbytes);
+               goto done;
+       }
+
+done:
+       g_main_loop_quit(mainloop);
+       return FALSE;
+}
+
+static void test_send_connect_stream(void)
+{
+       guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+       GError *gerr = NULL;
+       GIOChannel *io;
+       GIOCondition cond;
+       GObexPacket *req;
+       guint io_id, timer_id;
+       GObex *obex;
+       int sv[2];
+
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sv) < 0) {
+               g_printerr("socketpair: %s", strerror(errno));
+               abort();
+       }
+
+       obex = create_gobex(sv[0]);
+       g_assert(obex != NULL);
+
+       req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
+       g_assert(req != NULL);
+
+       g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
+                                                       G_OBEX_DATA_REF);
+       g_obex_send(obex, req);
+
+       io = g_io_channel_unix_new(sv[1]);
+       g_io_channel_set_encoding(io, NULL, NULL);
+       g_io_channel_set_buffered(io, FALSE);
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, handle_connect_data, &gerr);
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &gerr);
+
+       g_main_loop_run(mainloop);
+
+       g_main_loop_unref(mainloop);
+       mainloop = NULL;
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(gerr);
+}
+
 static void handle_connect_request(GObex *obex, GObexPacket *pkt,
                                                        gpointer user_data)
 {
@@ -123,6 +220,7 @@ static void handle_connect_request(GObex *obex, GObexPacket *pkt,
 static void test_recv_connect_stream(void)
 {
        GError *gerr = NULL;
+       guint timer_id;
        GObex *obex;
        ssize_t err;
        int sv[2];
@@ -142,10 +240,17 @@ static void test_recv_connect_stream(void)
 
        mainloop = g_main_loop_new(NULL, FALSE);
 
-       g_timeout_add_seconds(1, test_timeout, &gerr);
+       timer_id = g_timeout_add_seconds(1, test_timeout, &gerr);
 
        g_main_loop_run(mainloop);
 
+       g_source_remove(timer_id);
+       g_obex_unref(obex);
+
+       g_main_loop_unref(mainloop);
+       mainloop = NULL;
+
+
        g_assert_no_error(gerr);
 }
 
@@ -510,6 +615,8 @@ int main(int argc, char *argv[])
 
        g_test_add_func("/gobex/test_recv_connect_stream",
                                                test_recv_connect_stream);
+       g_test_add_func("/gobex/test_send_connect_stream",
+                                               test_send_connect_stream);
 
        g_test_run();