summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/usb_dev.c')
-rw-r--r--sys/dev/usb/usb_dev.c83
1 files changed, 67 insertions, 16 deletions
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
index de2e156..cba8919 100644
--- a/sys/dev/usb/usb_dev.c
+++ b/sys/dev/usb/usb_dev.c
@@ -740,6 +740,8 @@ usb_fifo_reset(struct usb_fifo *f)
break;
}
}
+ /* reset have fragment flag */
+ f->flag_have_fragment = 0;
}
/*------------------------------------------------------------------------*
@@ -783,6 +785,16 @@ usb_fifo_close(struct usb_fifo *f, int fflags)
/* set flushing flag */
f->flag_flushing = 1;
+ /* get the last packet in */
+ if (f->flag_have_fragment) {
+ struct usb_mbuf *m;
+ f->flag_have_fragment = 0;
+ USB_IF_DEQUEUE(&f->free_q, m);
+ if (m) {
+ USB_IF_ENQUEUE(&f->used_q, m);
+ }
+ }
+
/* start write transfer, if not already started */
(f->methods->f_start_write) (f);
@@ -1303,6 +1315,7 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
struct usb_cdev_privdata* cpd;
struct usb_fifo *f;
struct usb_mbuf *m;
+ uint8_t *pdata;
int fflags;
int resid;
int io_len;
@@ -1373,33 +1386,59 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
}
tr_data = 1;
- USB_MBUF_RESET(m);
-
- io_len = MIN(m->cur_data_len, uio->uio_resid);
-
- m->cur_data_len = io_len;
+ if (f->flag_have_fragment == 0) {
+ USB_MBUF_RESET(m);
+ io_len = m->cur_data_len;
+ pdata = m->cur_data_ptr;
+ if (io_len > uio->uio_resid)
+ io_len = uio->uio_resid;
+ m->cur_data_len = io_len;
+ } else {
+ io_len = m->max_data_len - m->cur_data_len;
+ pdata = m->cur_data_ptr + m->cur_data_len;
+ if (io_len > uio->uio_resid)
+ io_len = uio->uio_resid;
+ m->cur_data_len += io_len;
+ }
DPRINTFN(2, "transfer %d bytes to %p\n",
- io_len, m->cur_data_ptr);
+ io_len, pdata);
- err = usb_fifo_uiomove(f,
- m->cur_data_ptr, io_len, uio);
+ err = usb_fifo_uiomove(f, pdata, io_len, uio);
if (err) {
+ f->flag_have_fragment = 0;
USB_IF_ENQUEUE(&f->free_q, m);
break;
}
- if (f->methods->f_filter_write) {
+
+ /* check if the buffer is ready to be transmitted */
+
+ if ((f->flag_write_defrag == 0) ||
+ (m->cur_data_len == m->max_data_len)) {
+ f->flag_have_fragment = 0;
+
/*
- * Sometimes it is convenient to process data at the
- * expense of a userland process instead of a kernel
- * process.
+ * Check for write filter:
+ *
+ * Sometimes it is convenient to process data
+ * at the expense of a userland process
+ * instead of a kernel process.
*/
- (f->methods->f_filter_write) (f, m);
- }
- USB_IF_ENQUEUE(&f->used_q, m);
+ if (f->methods->f_filter_write) {
+ (f->methods->f_filter_write) (f, m);
+ }
- (f->methods->f_start_write) (f);
+ /* Put USB mbuf in the used queue */
+ USB_IF_ENQUEUE(&f->used_q, m);
+
+ /* Start writing data, if not already started */
+ (f->methods->f_start_write) (f);
+ } else {
+ /* Wait for more data or close */
+ f->flag_have_fragment = 1;
+ USB_IF_PREPEND(&f->free_q, m);
+ }
} while (uio->uio_resid > 0);
done:
@@ -2220,6 +2259,18 @@ usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
f->flag_short = onoff;
}
+void
+usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
+{
+ if (f == NULL)
+ return;
+
+ /* defrag written data */
+ f->flag_write_defrag = onoff;
+ /* reset defrag state */
+ f->flag_have_fragment = 0;
+}
+
void *
usb_fifo_softc(struct usb_fifo *f)
{
OpenPOWER on IntegriCloud