diff options
author | Ming Lei <ming.lei@canonical.com> | 2013-11-25 23:44:13 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-08 20:56:37 -0500 |
commit | e5fc70d5ccc3d005bb038c0275ffdf57a34b1496 (patch) | |
tree | b08f2262ac1d0cf4ca7101496b9833abe8b5a729 /drivers/usb/storage | |
parent | 0d6077f8b48ed2dce8f2466a76c0d574a3b4dbe9 (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>
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/protocol.c | 80 |
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 | } |
203 | EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); | 175 | EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf); |