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; }