aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/storage/protocol.c126
1 files changed, 53 insertions, 73 deletions
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 889622baac20..a41ce21c0697 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -149,11 +149,7 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
149 ***********************************************************************/ 149 ***********************************************************************/
150 150
151/* Copy a buffer of length buflen to/from the srb's transfer buffer. 151/* Copy a buffer of length buflen to/from the srb's transfer buffer.
152 * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer 152 * Update the **sgptr and *offset variables so that the next copy will
153 * points to a list of s-g entries and we ignore srb->request_bufflen.
154 * For non-scatter-gather transfers, srb->request_buffer points to the
155 * transfer buffer itself and srb->request_bufflen is the buffer's length.)
156 * Update the *index and *offset variables so that the next copy will
157 * pick up from where this one left off. */ 153 * pick up from where this one left off. */
158 154
159unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, 155unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
@@ -162,80 +158,64 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
162{ 158{
163 unsigned int cnt; 159 unsigned int cnt;
164 160
165 /* If not using scatter-gather, just transfer the data directly. 161 /* We have to go through the list one entry
166 * Make certain it will fit in the available buffer space. */
167 if (srb->use_sg == 0) {
168 if (*offset >= srb->request_bufflen)
169 return 0;
170 cnt = min(buflen, srb->request_bufflen - *offset);
171 if (dir == TO_XFER_BUF)
172 memcpy((unsigned char *) srb->request_buffer + *offset,
173 buffer, cnt);
174 else
175 memcpy(buffer, (unsigned char *) srb->request_buffer +
176 *offset, cnt);
177 *offset += cnt;
178
179 /* Using scatter-gather. We have to go through the list one entry
180 * at a time. Each s-g entry contains some number of pages, and 162 * at a time. Each s-g entry contains some number of pages, and
181 * each page has to be kmap()'ed separately. If the page is already 163 * each page has to be kmap()'ed separately. If the page is already
182 * in kernel-addressable memory then kmap() will return its address. 164 * in kernel-addressable memory then kmap() will return its address.
183 * If the page is not directly accessible -- such as a user buffer 165 * If the page is not directly accessible -- such as a user buffer
184 * located in high memory -- then kmap() will map it to a temporary 166 * located in high memory -- then kmap() will map it to a temporary
185 * position in the kernel's virtual address space. */ 167 * position in the kernel's virtual address space. */
186 } else { 168 struct scatterlist *sg = *sgptr;
187 struct scatterlist *sg = *sgptr; 169
188 170 if (!sg)
189 if (!sg) 171 sg = scsi_sglist(srb);
190 sg = (struct scatterlist *) srb->request_buffer; 172
191 173 /* This loop handles a single s-g list entry, which may
192 /* This loop handles a single s-g list entry, which may 174 * include multiple pages. Find the initial page structure
193 * include multiple pages. Find the initial page structure 175 * and the starting offset within the page, and update
194 * and the starting offset within the page, and update 176 * the *offset and **sgptr values for the next loop. */
195 * the *offset and *index values for the next loop. */ 177 cnt = 0;
196 cnt = 0; 178 while (cnt < buflen) {
197 while (cnt < buflen) { 179 struct page *page = sg_page(sg) +
198 struct page *page = sg_page(sg) + 180 ((sg->offset + *offset) >> PAGE_SHIFT);
199 ((sg->offset + *offset) >> PAGE_SHIFT); 181 unsigned int poff =
200 unsigned int poff = 182 (sg->offset + *offset) & (PAGE_SIZE-1);
201 (sg->offset + *offset) & (PAGE_SIZE-1); 183 unsigned int sglen = sg->length - *offset;
202 unsigned int sglen = sg->length - *offset; 184
203 185 if (sglen > buflen - cnt) {
204 if (sglen > buflen - cnt) { 186
205 187 /* Transfer ends within this s-g entry */
206 /* Transfer ends within this s-g entry */ 188 sglen = buflen - cnt;
207 sglen = buflen - cnt; 189 *offset += sglen;
208 *offset += sglen; 190 } else {
209 } else { 191
210 192 /* Transfer continues to next s-g entry */
211 /* Transfer continues to next s-g entry */ 193 *offset = 0;
212 *offset = 0; 194 sg = sg_next(sg);
213 sg = sg_next(sg); 195 }
214 } 196
215 197 /* Transfer the data for all the pages in this
216 /* Transfer the data for all the pages in this 198 * s-g entry. For each page: call kmap(), do the
217 * s-g entry. For each page: call kmap(), do the 199 * transfer, and call kunmap() immediately after. */
218 * transfer, and call kunmap() immediately after. */ 200 while (sglen > 0) {
219 while (sglen > 0) { 201 unsigned int plen = min(sglen, (unsigned int)
220 unsigned int plen = min(sglen, (unsigned int) 202 PAGE_SIZE - poff);
221 PAGE_SIZE - poff); 203 unsigned char *ptr = kmap(page);
222 unsigned char *ptr = kmap(page); 204
223 205 if (dir == TO_XFER_BUF)
224 if (dir == TO_XFER_BUF) 206 memcpy(ptr + poff, buffer + cnt, plen);
225 memcpy(ptr + poff, buffer + cnt, plen); 207 else
226 else 208 memcpy(buffer + cnt, ptr + poff, plen);
227 memcpy(buffer + cnt, ptr + poff, plen); 209 kunmap(page);
228 kunmap(page); 210
229 211 /* Start at the beginning of the next page */
230 /* Start at the beginning of the next page */ 212 poff = 0;
231 poff = 0; 213 ++page;
232 ++page; 214 cnt += plen;
233 cnt += plen; 215 sglen -= plen;
234 sglen -= plen;
235 }
236 } 216 }
237 *sgptr = sg;
238 } 217 }
218 *sgptr = sg;
239 219
240 /* Return the amount actually transferred */ 220 /* Return the amount actually transferred */
241 return cnt; 221 return cnt;
@@ -251,6 +231,6 @@ void usb_stor_set_xfer_buf(unsigned char *buffer,
251 231
252 usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset, 232 usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset,
253 TO_XFER_BUF); 233 TO_XFER_BUF);
254 if (buflen < srb->request_bufflen) 234 if (buflen < scsi_bufflen(srb))
255 srb->resid = srb->request_bufflen - buflen; 235 scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
256} 236}