diff options
Diffstat (limited to 'drivers/usb/storage/protocol.c')
| -rw-r--r-- | drivers/usb/storage/protocol.c | 95 | 
1 files changed, 34 insertions, 61 deletions
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index fc310f75ead..12e3c2fac64 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -43,6 +43,7 @@   */  #include <linux/highmem.h> +#include <linux/export.h>  #include <scsi/scsi.h>  #include <scsi/scsi_cmnd.h> @@ -58,17 +59,16 @@  void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us)  { -	/* Pad the SCSI command with zeros out to 12 bytes +	/* +	 * Pad the SCSI command with zeros out to 12 bytes.  If the +	 * command already is 12 bytes or longer, leave it alone.  	 *  	 * NOTE: This only works because a scsi_cmnd struct field contains  	 * a unsigned char cmnd[16], so we know we have storage available  	 */ -	for (; srb->cmd_len<12; srb->cmd_len++) +	for (; srb->cmd_len < 12; srb->cmd_len++)  		srb->cmnd[srb->cmd_len] = 0; -	/* set command length to 12 bytes */ -	srb->cmd_len = 12; -  	/* send the command to the transport layer */  	usb_stor_invoke_transport(srb, us);  } @@ -76,14 +76,14 @@ void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us)  void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)  {  	/* fix some commands -- this is a form of mode translation -	 * UFI devices only accept 12 byte long commands  +	 * UFI devices only accept 12 byte long commands  	 *  	 * NOTE: This only works because a scsi_cmnd struct field contains  	 * a unsigned char cmnd[16], so we know we have storage available  	 */  	/* Pad the ATAPI command with zeros */ -	for (; srb->cmd_len<12; srb->cmd_len++) +	for (; srb->cmd_len < 12; srb->cmd_len++)  		srb->cmnd[srb->cmd_len] = 0;  	/* set command length to 12 bytes (this affects the transport layer) */ @@ -135,69 +135,42 @@ 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; + +	while (sg_miter_next(&miter) && cnt < buflen) { +		unsigned int len = min_t(unsigned int, miter.length, +				buflen - 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); - -			if (dir == TO_XFER_BUF) -				memcpy(ptr + poff, buffer + cnt, plen); -			else -				memcpy(buffer + cnt, ptr + poff, plen); -			kunmap(page); - -			/* Start at the beginning of the next page */ -			poff = 0; -			++page; -			cnt += plen; -			sglen -= plen; +		if (dir == FROM_XFER_BUF) +			memcpy(buffer + cnt, miter.addr, len); +		else +			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);  | 
