diff options
-rw-r--r-- | drivers/usb/storage/protocol.c | 126 |
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 | ||
159 | unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, | 155 | unsigned 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 | } |