diff options
Diffstat (limited to 'drivers/usb/storage/protocol.c')
-rw-r--r-- | drivers/usb/storage/protocol.c | 81 |
1 files changed, 27 insertions, 54 deletions
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 5dfb4c36a1b0..12e3c2fac642 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c | |||
@@ -135,69 +135,42 @@ 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_t(unsigned int, miter.length, | ||
156 | buflen - cnt); | ||
157 | |||
158 | if (dir == FROM_XFER_BUF) | ||
159 | memcpy(buffer + cnt, miter.addr, len); | ||
160 | else | ||
161 | memcpy(miter.addr, buffer + cnt, len); | ||
176 | 162 | ||
177 | /* Transfer the data for all the pages in this | 163 | if (*offset + len < miter.piter.sg->length) { |
178 | * s-g entry. For each page: call kmap(), do the | 164 | *offset += len; |
179 | * transfer, and call kunmap() immediately after. */ | 165 | *sgptr = miter.piter.sg; |
180 | while (sglen > 0) { | 166 | } else { |
181 | unsigned int plen = min(sglen, (unsigned int) | 167 | *offset = 0; |
182 | PAGE_SIZE - poff); | 168 | *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 | } | 169 | } |
170 | cnt += len; | ||
197 | } | 171 | } |
198 | *sgptr = sg; | 172 | sg_miter_stop(&miter); |
199 | 173 | ||
200 | /* Return the amount actually transferred */ | ||
201 | return cnt; | 174 | return cnt; |
202 | } | 175 | } |
203 | EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); | 176 | EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); |