u_serial ring buffer bug

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Dave,

I think I ran into a bug in your ring buffer handling on u_serial.c so
far I couldn't find a proper fix but the bug is really easy to reproduce
with the applications attached.

The problem is then we we try to transfer files of over 31k over an obex
interface (probably acm and g_serial suffer the same) we get a short
transfer (511) where we shouldn't get.

It seems that the problem lies on the gs_buf_space_available(), more
specifically related to that "-1" you added, I believe, to report
"correctly" when you have an empty buffer, since:

(buf_size + buf_get - buf_put) % buf_size

would return 0 on that situation.

The following didn't help:

diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 53d5928..7a62d3e 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -198,7 +198,10 @@ static unsigned gs_buf_data_avail(struct gs_buf *gb)
  */
 static unsigned gs_buf_space_avail(struct gs_buf *gb)
 {
-       return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+       if (gb->buf_put == gb->buf_get)
+               return gb->buf_size;
+
+       return (gb->buf_size + gb->buf_get - gb->buf_put) % gb->buf_size;
 }
 
 /*

as after applying this, the available space and data are correct (as it
seems from the logs) but there's no data reaching the other end of the
pipe. I tried with my test application and another setup with full obex
server [1] and full obex client.

I believe the problem is there, because I can see gs_buf_space_available
always one less byte and if we fill the buffer faster than drain it,
then the host might see a premature short packet, am I right ?

Hope you can help me with this issue and have any better ideas for
fixing it.

[1] http://git.kernel.org/?p=bluetooth/obexd.git;a=summary

ps: the file.bin I created with dd if=/dev/zero of=/home/user/file.bin bs=1k count=59

-- 
balbi
/**
 * gcc -lusb-1.0 -Wall -O2 -g -o obexc obexc.c
 *
 * obexc.c - f_obex test application, host side
 *
 * Copyright (c) 2009 - Felipe Balbi <felipe.balbi@xxxxxxxxx>
 *
 * This file is licensed under the GPLv2.
 */

#include <libusb-1.0/libusb.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#define TIMEOUT 5000
#define BUFLEN (64 * 1024)

static void hexdump(void *mem, unsigned length)
{
	char  line[80];
	char *src = (char*)mem;
	unsigned i;

	printf("dumping %u bytes from %p\r\n"
			"       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F    0123456789ABCDEF\r\n",
			length, src);

	for (i = 0; i < length; i += 16, src += 16) {
		char *t = line;
		int j;

		t += sprintf(t, "%04x:  ", i);
		for (j = 0; j < 16; j++) {
			if (i+j < length)
				t += sprintf(t, "%02x", src[j] & 0xff);
			else
				t += sprintf(t, "  ");
			t += sprintf(t, j%2 ? " " : "-");
		}

		t += sprintf(t, "  ");
		for (j = 0; j < 16; j++) {
			if (i+j < length) {
				if (isprint((unsigned char)src[j]))
					t += sprintf(t, "%c", src[j]);
				else
					t += sprintf(t, ".");
			} else {
				t += sprintf(t, " ");
			}
		}

		t += sprintf(t, "\r\n");
		printf("%s", line);
	}
}

int main(int argc, char *argv[])
{
	libusb_context		*context;
	libusb_device_handle	*udevh;

	int			transferred;

	unsigned char		buf[BUFLEN];

	int			ret;

	memset(buf, 0x00, BUFLEN);

	/* initialize libusb */
	libusb_init(&context);

	udevh = libusb_open_device_with_vid_pid(context, 0x0525, 0xa4a9);
	libusb_claim_interface(udevh, 1);
	libusb_set_interface_alt_setting(udevh, 1, 1);

	ret = libusb_bulk_transfer(udevh, 0x82, buf, BUFLEN, &transferred, TIMEOUT);
	if (ret < 0) {
		printf("failed to receive data, err %d\n", ret);
		goto out;
	}

	printf("received %d bytes\n", transferred);

	hexdump(buf, 1024);

out:
	/* kill the session */
	libusb_release_interface(udevh, 2);
	libusb_close(udevh);
	libusb_exit(context);

	return 0;
}

/**
 * arm-linux-gcc -Wall -O2 -g -o obexd obexd.c
 *
 * obexd.c - f_obex test application
 *
 * Copyright (c) 2009 - Felipe Balbi <felipe.balbi@xxxxxxxxx>
 *
 * This file is licensed under the GPLv2.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <termios.h>
#include <string.h>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#define DEVNODE		"/dev/ttyGS0"
#define DATAFILE	"/home/user/file.bin"
#define BUFLEN		(64 * 1024)

static struct termios *term;

static void hexdump(void *mem, unsigned length)
{
	char  line[80];
	char *src = (char*)mem;
	unsigned i;

	printf("dumping %u bytes from %p\r\n"
			"       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F    0123456789ABCDEF\r\n",
			length, src);

	for (i = 0; i < length; i += 16, src += 16) {
		char *t = line;
		int j;

		t += sprintf(t, "%04x:  ", i);
		for (j = 0; j < 16; j++) {
			if (i+j < length)
				t += sprintf(t, "%02x", src[j] & 0xff);
			else
				t += sprintf(t, "  ");
			t += sprintf(t, j%2 ? " " : "-");
		}

		t += sprintf(t, "  ");
		for (j = 0; j < 16; j++) {
			if (i+j < length) {
				if (isprint((unsigned char)src[j]))
					t += sprintf(t, "%c", src[j]);
				else
					t += sprintf(t, ".");
			} else {
				t += sprintf(t, " ");
			}
		}

		t += sprintf(t, "\r\n");
		printf("%s", line);
	}
}

int main(int argc, char *argv[])
{
	char	*buf;
	int	devfd;
	int	icofd;
	int	arg;

	ssize_t	len;
	ssize_t	r;

	long	flags;

	term = malloc(sizeof(*term));
	if (!term) {
		printf("failed to allocated memory\n");
		goto fail0;
	}

	icofd = open(DATAFILE, O_RDONLY);
	if (icofd < 0) {
		printf("failed to open %s, err %d\n", DATAFILE, errno);
		goto fail1;
	}

	buf = malloc(BUFLEN);
	if (!buf) {
		printf("failed to allocate memory\n");
		goto fail2;
	}

	len = read(icofd, buf, BUFLEN);
	if (len < 0) {
		printf("failed to read %s, err %d\n", DATAFILE, errno);
		goto fail3;
	}

	printf("read %zd bytes\n", len);

	devfd = open(DEVNODE, O_WRONLY | O_NOCTTY);
	if (devfd < 0) {
		printf("failed to open %s, err %d\n", DEVNODE, errno);
		goto fail4;
	}

	flags = fcntl(devfd, F_GETFL);
	fcntl(devfd, flags & ~O_NONBLOCK);

	tcgetattr(devfd, term);

	cfmakeraw(term);
	term->c_oflag |= ONLCR;

	if (tcflush(devfd, TCIOFLUSH) < 0 ) {
		printf("flush failed, err %d: %s\n", errno, strerror(errno));
		goto fail4;
	}

	if (tcsetattr(devfd, TCSANOW, term) < 0) {
		printf("set attribute failed, err %d: %s\n", errno, strerror(errno));
		goto fail4;
	}

	arg = fcntl(devfd, F_GETFL);
	if (arg < 0) {
		printf("could not get arg\n");
		goto fail4;
	}

	arg |= O_NONBLOCK;
	if (fcntl(devfd, F_SETFL, arg) < 0) {
		printf("failed to set arguments\n");
		goto fail4;
	}

	r = write(devfd, buf, len);
	if (r < 0) {
		printf("failed to write to %s, err %d\n", DEVNODE, errno);
		goto fail5;
	}

	hexdump(buf, 1024);

	printf("wrote %d bytes\n", r);

	free(buf);
	close(icofd);
	close(devfd);

	return 0;

fail5:
	close(devfd);

fail4:
fail3:
	free(buf);

fail2:
	close(icofd);

fail1:
	free(term);

fail0:
	return -1;
}


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux