Ejemplo n.º 1
0
static int
usbpf_xfer_frame_is_read(struct usb_xfer *xfer, uint32_t frame)
{
	int isread;

	if ((frame == 0) && (xfer->flags_int.control_xfr != 0) &&
	    (xfer->flags_int.control_hdr != 0)) {
		/* special case */
		if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
			/* The device controller writes to memory */
			isread = 1;
		} else {
			/* The host controller reads from memory */
			isread = 0;
		}
	} else {
		isread = USB_GET_DATA_ISREAD(xfer);
	}
	return (isread);
}
Ejemplo n.º 2
0
static int
ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
{
	struct usb_device_request *req;
	struct usb_xfer *xfer;
	struct usb_fs_endpoint fs_ep;
	struct usb_fs_endpoint *fs_ep_uptr;	/* userland ptr */
	void *uaddr;			/* userland ptr */
	void *kaddr;
	usb_frlength_t offset;
	usb_frlength_t rem;
	usb_frcount_t n;
	uint32_t length;
	uint32_t temp;
	int error;
	uint8_t isread;

	if (ep_index >= f->fs_ep_max)
		return (EINVAL);

	xfer = f->fs_xfer[ep_index];
	if (xfer == NULL)
		return (EINVAL);

	mtx_lock(f->priv_mtx);
	if (usbd_transfer_pending(xfer)) {
		mtx_unlock(f->priv_mtx);
		return (EBUSY);		/* should not happen */
	}
	mtx_unlock(f->priv_mtx);

	fs_ep_uptr = f->fs_ep_ptr + ep_index;
	error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
	if (error) {
		return (error);
	}
	fs_ep.status = xfer->error;
	fs_ep.aFrames = xfer->aframes;
	fs_ep.isoc_time_complete = xfer->isoc_time_complete;
	if (xfer->error) {
		goto complete;
	}
	if (xfer->flags_int.control_xfr) {
		req = xfer->frbuffers[0].buffer;

		/* Host mode only ! */
		if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) {
			isread = 1;
		} else {
			isread = 0;
		}
		if (xfer->nframes == 0)
			n = 0;		/* should never happen */
		else
			n = 1;
	} else {
		/* Device and Host mode */
		if (USB_GET_DATA_ISREAD(xfer)) {
			isread = 1;
		} else {
			isread = 0;
		}
		n = 0;
	}

	/* Update lengths and copy out data */

	rem = usbd_xfer_max_len(xfer);
	offset = 0;

	for (; n != xfer->nframes; n++) {

		/* get initial length into "temp" */
		error = copyin(fs_ep.pLength + n,
		    &temp, sizeof(temp));
		if (error) {
			return (error);
		}
		if (temp > rem) {
			/* the userland length has been corrupted */
			DPRINTF("corrupt userland length "
			    "%u > %u\n", temp, rem);
			fs_ep.status = USB_ERR_INVAL;
			goto complete;
		}
		rem -= temp;

		/* get actual transfer length */
		length = xfer->frlengths[n];
		if (length > temp) {
			/* data overflow */
			fs_ep.status = USB_ERR_INVAL;
			DPRINTF("data overflow %u > %u\n",
			    length, temp);
			goto complete;
		}
		if (isread) {

			/* we need to know the destination buffer */
			error = copyin(fs_ep.ppBuffer + n,
			    &uaddr, sizeof(uaddr));
			if (error) {
				return (error);
			}
			if (xfer->flags_int.isochronous_xfr) {
				/* only one frame buffer */
				kaddr = USB_ADD_BYTES(
				    xfer->frbuffers[0].buffer, offset);
			} else {
				/* multiple frame buffers */
				kaddr = xfer->frbuffers[n].buffer;
			}

			/* move data */
			error = copyout(kaddr, uaddr, length);
			if (error) {
				return (error);
			}
		}
		/*
		 * Update offset according to initial length, which is
		 * needed by isochronous transfers!
		 */
		offset += temp;

		/* update length */
		error = copyout(&length,
		    fs_ep.pLength + n, sizeof(length));
		if (error) {
			return (error);
		}
	}

complete:
	/* update "aFrames" */
	error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
	    sizeof(fs_ep.aFrames));
	if (error)
		goto done;

	/* update "isoc_time_complete" */
	error = copyout(&fs_ep.isoc_time_complete,
	    &fs_ep_uptr->isoc_time_complete,
	    sizeof(fs_ep.isoc_time_complete));
	if (error)
		goto done;
	/* update "status" */
	error = copyout(&fs_ep.status, &fs_ep_uptr->status,
	    sizeof(fs_ep.status));
done:
	return (error);
}
Ejemplo n.º 3
0
static int
ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
{
	struct usb_device_request *req;
	struct usb_xfer *xfer;
	struct usb_fs_endpoint fs_ep;
	void *uaddr;			/* userland pointer */
	void *kaddr;
	usb_frlength_t offset;
	usb_frlength_t rem;
	usb_frcount_t n;
	uint32_t length;
	int error;
	uint8_t isread;

	if (ep_index >= f->fs_ep_max) {
		return (EINVAL);
	}
	xfer = f->fs_xfer[ep_index];
	if (xfer == NULL) {
		return (EINVAL);
	}
	mtx_lock(f->priv_mtx);
	if (usbd_transfer_pending(xfer)) {
		mtx_unlock(f->priv_mtx);
		return (EBUSY);		/* should not happen */
	}
	mtx_unlock(f->priv_mtx);

	error = copyin(f->fs_ep_ptr +
	    ep_index, &fs_ep, sizeof(fs_ep));
	if (error) {
		return (error);
	}
	/* security checks */

	if (fs_ep.nFrames > xfer->max_frame_count) {
		xfer->error = USB_ERR_INVAL;
		goto complete;
	}
	if (fs_ep.nFrames == 0) {
		xfer->error = USB_ERR_INVAL;
		goto complete;
	}
	error = copyin(fs_ep.ppBuffer,
	    &uaddr, sizeof(uaddr));
	if (error) {
		return (error);
	}
	/* reset first frame */
	usbd_xfer_set_frame_offset(xfer, 0, 0);

	if (xfer->flags_int.control_xfr) {

		req = xfer->frbuffers[0].buffer;

		error = copyin(fs_ep.pLength,
		    &length, sizeof(length));
		if (error) {
			return (error);
		}
		if (length != sizeof(*req)) {
			xfer->error = USB_ERR_INVAL;
			goto complete;
		}
		if (length != 0) {
			error = copyin(uaddr, req, length);
			if (error) {
				return (error);
			}
		}
		if (ugen_check_request(f->udev, req)) {
			xfer->error = USB_ERR_INVAL;
			goto complete;
		}
		usbd_xfer_set_frame_len(xfer, 0, length);

		/* Host mode only ! */
		if ((req->bmRequestType &
		    (UT_READ | UT_WRITE)) == UT_READ) {
			isread = 1;
		} else {
			isread = 0;
		}
		n = 1;
		offset = sizeof(*req);

	} else {
		/* Device and Host mode */
		if (USB_GET_DATA_ISREAD(xfer)) {
			isread = 1;
		} else {
			isread = 0;
		}
		n = 0;
		offset = 0;
	}

	rem = usbd_xfer_max_len(xfer);
	xfer->nframes = fs_ep.nFrames;
	xfer->timeout = fs_ep.timeout;
	if (xfer->timeout > 65535) {
		xfer->timeout = 65535;
	}
	if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK)
		xfer->flags.short_xfer_ok = 1;
	else
		xfer->flags.short_xfer_ok = 0;

	if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK)
		xfer->flags.short_frames_ok = 1;
	else
		xfer->flags.short_frames_ok = 0;

	if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT)
		xfer->flags.force_short_xfer = 1;
	else
		xfer->flags.force_short_xfer = 0;

	if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL)
		usbd_xfer_set_stall(xfer);
	else
		xfer->flags.stall_pipe = 0;

	for (; n != xfer->nframes; n++) {

		error = copyin(fs_ep.pLength + n,
		    &length, sizeof(length));
		if (error) {
			break;
		}
		usbd_xfer_set_frame_len(xfer, n, length);

		if (length > rem) {
			xfer->error = USB_ERR_INVAL;
			goto complete;
		}
		rem -= length;

		if (!isread) {

			/* we need to know the source buffer */
			error = copyin(fs_ep.ppBuffer + n,
			    &uaddr, sizeof(uaddr));
			if (error) {
				break;
			}
			if (xfer->flags_int.isochronous_xfr) {
				/* get kernel buffer address */
				kaddr = xfer->frbuffers[0].buffer;
				kaddr = USB_ADD_BYTES(kaddr, offset);
			} else {
				/* set current frame offset */
				usbd_xfer_set_frame_offset(xfer, offset, n);

				/* get kernel buffer address */
				kaddr = xfer->frbuffers[n].buffer;
			}

			/* move data */
			error = copyin(uaddr, kaddr, length);
			if (error) {
				break;
			}
		}
		offset += length;
	}
	return (error);

complete:
	mtx_lock(f->priv_mtx);
	ugen_fs_set_complete(f, ep_index);
	mtx_unlock(f->priv_mtx);
	return (0);
}
Ejemplo n.º 4
0
/*------------------------------------------------------------------------*
 *	usb_bdma_work_loop
 *
 * This function handles loading of virtual buffers into DMA and is
 * only called when "dma_refcount" is zero.
 *------------------------------------------------------------------------*/
void
usb_bdma_work_loop(struct usb_xfer_queue *pq)
{
	struct usb_xfer_root *info;
	struct usb_xfer *xfer;
	usb_frcount_t nframes;

	xfer = pq->curr;
	info = xfer->xroot;

	mtx_assert(info->xfer_mtx, MA_OWNED);

	if (xfer->error) {
		/* some error happened */
		USB_BUS_LOCK(info->bus);
		usbd_transfer_done(xfer, 0);
		USB_BUS_UNLOCK(info->bus);
		return;
	}
	if (!xfer->flags_int.bdma_setup) {
		struct usb_page *pg;
		usb_frlength_t frlength_0;
		uint8_t isread;

		xfer->flags_int.bdma_setup = 1;

		/* reset BUS-DMA load state */

		info->dma_error = 0;

		if (xfer->flags_int.isochronous_xfr) {
			/* only one frame buffer */
			nframes = 1;
			frlength_0 = xfer->sumlen;
		} else {
			/* can be multiple frame buffers */
			nframes = xfer->nframes;
			frlength_0 = xfer->frlengths[0];
		}

		/*
		 * Set DMA direction first. This is needed to
		 * select the correct cache invalidate and cache
		 * flush operations.
		 */
		isread = USB_GET_DATA_ISREAD(xfer);
		pg = xfer->dma_page_ptr;

		if (xfer->flags_int.control_xfr &&
		    xfer->flags_int.control_hdr) {
			/* special case */
			if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
				/* The device controller writes to memory */
				xfer->frbuffers[0].isread = 1;
			} else {
				/* The host controller reads from memory */
				xfer->frbuffers[0].isread = 0;
			}
		} else {
			/* default case */
			xfer->frbuffers[0].isread = isread;
		}

		/*
		 * Setup the "page_start" pointer which points to an array of
		 * USB pages where information about the physical address of a
		 * page will be stored. Also initialise the "isread" field of
		 * the USB page caches.
		 */
		xfer->frbuffers[0].page_start = pg;

		info->dma_nframes = nframes;
		info->dma_currframe = 0;
		info->dma_frlength_0 = frlength_0;

		pg += (frlength_0 / USB_PAGE_SIZE);
		pg += 2;

		while (--nframes > 0) {
			xfer->frbuffers[nframes].isread = isread;
			xfer->frbuffers[nframes].page_start = pg;

			pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE);
			pg += 2;
		}

	}
	if (info->dma_error) {
		USB_BUS_LOCK(info->bus);
		usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED);
		USB_BUS_UNLOCK(info->bus);
		return;
	}
	if (info->dma_currframe != info->dma_nframes) {

		if (info->dma_currframe == 0) {
			/* special case */
			usb_pc_load_mem(xfer->frbuffers,
			    info->dma_frlength_0, 0);
		} else {
			/* default case */
			nframes = info->dma_currframe;
			usb_pc_load_mem(xfer->frbuffers + nframes,
			    xfer->frlengths[nframes], 0);
		}

		/* advance frame index */
		info->dma_currframe++;

		return;
	}
	/* go ahead */
	usb_bdma_pre_sync(xfer);

	/* start loading next USB transfer, if any */
	usb_command_wrapper(pq, NULL);

	/* finally start the hardware */
	usbd_pipe_enter(xfer);
}