Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5fc70d5 authored by Ming Lei's avatar Ming Lei Committed by Greg Kroah-Hartman
Browse files

USB: storage: use sg_miter_* APIs to access scsi buffer



We have sg_miter_* APIs for accessing scsi sg buffer, so
use them to make code clean and bug free.

Cc: Matthew Dharm <mdharm-usb@one-eyed-alien.net>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarMing Lei <ming.lei@canonical.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0d6077f8
Loading
Loading
Loading
Loading
+26 −54
Original line number Diff line number Diff line
@@ -135,69 +135,41 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
	unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr,
	unsigned int *offset, enum xfer_buf_dir dir)
{
	unsigned int cnt;
	unsigned int cnt = 0;
	struct scatterlist *sg = *sgptr;
	struct sg_mapping_iter miter;
	unsigned int nents = scsi_sg_count(srb);

	/* We have to go through the list one entry
	 * at a time.  Each s-g entry contains some number of pages, and
	 * each page has to be kmap()'ed separately.  If the page is already
	 * in kernel-addressable memory then kmap() will return its address.
	 * If the page is not directly accessible -- such as a user buffer
	 * located in high memory -- then kmap() will map it to a temporary
	 * position in the kernel's virtual address space.
	 */

	if (!sg)
	if (sg)
		nents = sg_nents(sg);
	else
		sg = scsi_sglist(srb);

	/* This loop handles a single s-g list entry, which may
	 * include multiple pages.  Find the initial page structure
	 * and the starting offset within the page, and update
	 * the *offset and **sgptr values for the next loop.
	 */
	cnt = 0;
	while (cnt < buflen && sg) {
		struct page *page = sg_page(sg) +
				((sg->offset + *offset) >> PAGE_SHIFT);
		unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE-1);
		unsigned int sglen = sg->length - *offset;

		if (sglen > buflen - cnt) {

			/* Transfer ends within this s-g entry */
			sglen = buflen - cnt;
			*offset += sglen;
		} else {
	sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ?
		SG_MITER_FROM_SG: SG_MITER_TO_SG);

			/* Transfer continues to next s-g entry */
			*offset = 0;
			sg = sg_next(sg);
		}
	if (!sg_miter_skip(&miter, *offset))
		return cnt;

		/* Transfer the data for all the pages in this
			* s-g entry.  For each page: call kmap(), do the
			* transfer, and call kunmap() immediately after. */
		while (sglen > 0) {
			unsigned int plen = min(sglen, (unsigned int)
					PAGE_SIZE - poff);
			unsigned char *ptr = kmap(page);
	while (sg_miter_next(&miter) && cnt < buflen) {
		unsigned int len = min(miter.length, buflen - cnt);

			if (dir == TO_XFER_BUF)
				memcpy(ptr + poff, buffer + cnt, plen);
		if (dir == FROM_XFER_BUF)
			memcpy(buffer + cnt, miter.addr, len);
		else
				memcpy(buffer + cnt, ptr + poff, plen);
			kunmap(page);

			/* Start at the beginning of the next page */
			poff = 0;
			++page;
			cnt += plen;
			sglen -= plen;
			memcpy(miter.addr, buffer + cnt, len);

		if (*offset + len < miter.piter.sg->length) {
			*offset += len;
			*sgptr = miter.piter.sg;
		} else {
			*offset = 0;
			*sgptr = sg_next(miter.piter.sg);
		}
		cnt += len;
	}
	*sgptr = sg;
	sg_miter_stop(&miter);

	/* Return the amount actually transferred */
	return cnt;
}
EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf);