aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2013-11-25 23:44:13 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-08 20:56:37 -0500
commite5fc70d5ccc3d005bb038c0275ffdf57a34b1496 (patch)
treeb08f2262ac1d0cf4ca7101496b9833abe8b5a729
parent0d6077f8b48ed2dce8f2466a76c0d574a3b4dbe9 (diff)
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: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/storage/protocol.c80
1 files changed, 26 insertions, 54 deletions
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 5dfb4c36a1b0..f54e5fea9230 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -135,69 +135,41 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
135 unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr, 135 unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr,
136 unsigned int *offset, enum xfer_buf_dir dir) 136 unsigned int *offset, enum xfer_buf_dir dir)
137{ 137{
138 unsigned int cnt; 138 unsigned int cnt = 0;
139 struct scatterlist *sg = *sgptr; 139 struct scatterlist *sg = *sgptr;
140 struct sg_mapping_iter miter;
141 unsigned int nents = scsi_sg_count(srb);
140 142
141 /* We have to go through the list one entry 143 if (sg)
142 * at a time. Each s-g entry contains some number of pages, and 144 nents = sg_nents(sg);
143 * each page has to be kmap()'ed separately. If the page is already 145 else
144 * in kernel-addressable memory then kmap() will return its address.
145 * If the page is not directly accessible -- such as a user buffer
146 * located in high memory -- then kmap() will map it to a temporary
147 * position in the kernel's virtual address space.
148 */
149
150 if (!sg)
151 sg = scsi_sglist(srb); 146 sg = scsi_sglist(srb);
152 147
153 /* This loop handles a single s-g list entry, which may 148 sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ?
154 * include multiple pages. Find the initial page structure 149 SG_MITER_FROM_SG: SG_MITER_TO_SG);
155 * and the starting offset within the page, and update
156 * the *offset and **sgptr values for the next loop.
157 */
158 cnt = 0;
159 while (cnt < buflen && sg) {
160 struct page *page = sg_page(sg) +
161 ((sg->offset + *offset) >> PAGE_SHIFT);
162 unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE-1);
163 unsigned int sglen = sg->length - *offset;
164
165 if (sglen > buflen - cnt) {
166
167 /* Transfer ends within this s-g entry */
168 sglen = buflen - cnt;
169 *offset += sglen;
170 } else {
171 150
172 /* Transfer continues to next s-g entry */ 151 if (!sg_miter_skip(&miter, *offset))
173 *offset = 0; 152 return cnt;
174 sg = sg_next(sg); 153
175 } 154 while (sg_miter_next(&miter) && cnt < buflen) {
155 unsigned int len = min(miter.length, buflen - cnt);
156
157 if (dir == FROM_XFER_BUF)
158 memcpy(buffer + cnt, miter.addr, len);
159 else
160 memcpy(miter.addr, buffer + cnt, len);
176 161
177 /* Transfer the data for all the pages in this 162 if (*offset + len < miter.piter.sg->length) {
178 * s-g entry. For each page: call kmap(), do the 163 *offset += len;
179 * transfer, and call kunmap() immediately after. */ 164 *sgptr = miter.piter.sg;
180 while (sglen > 0) { 165 } else {
181 unsigned int plen = min(sglen, (unsigned int) 166 *offset = 0;
182 PAGE_SIZE - poff); 167 *sgptr = sg_next(miter.piter.sg);
183 unsigned char *ptr = kmap(page);
184
185 if (dir == TO_XFER_BUF)
186 memcpy(ptr + poff, buffer + cnt, plen);
187 else
188 memcpy(buffer + cnt, ptr + poff, plen);
189 kunmap(page);
190
191 /* Start at the beginning of the next page */
192 poff = 0;
193 ++page;
194 cnt += plen;
195 sglen -= plen;
196 } 168 }
169 cnt += len;
197 } 170 }
198 *sgptr = sg; 171 sg_miter_stop(&miter);
199 172
200 /* Return the amount actually transferred */
201 return cnt; 173 return cnt;
202} 174}
203EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); 175EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf);