For the case where use_sg is 0, let's look at the second row. offset is the parameter passed in by the function call. it is clear in the comment that it is used to mark the offset, each time you copy several bytes, it adds several bytes, and the maximum value cannot exceed request_bufflen. this is clear. Usb...
For the case where use_sg is 0, let's look at the second row. offset is the parameter passed in by the function call. it is clear in the comment that it is used to mark the offset, each time you copy several bytes, it adds several bytes, and the maximum value cannot exceed request_bufflen. this is clear. The function usb_stor_access_xfer_buf () is used to copy data from the server load balancer-> request_buffer to the buffer, or, in turn, from the buffer to the server load balancer-> request_buffer, and then return how many bytes have been copied. If the offset value is greater than or equal to request_bufflen, of course, 0 is returned directly because request_buffer is full.
The enumxfer_buf_dir mark indicates the transmission direction. this data type is defined in drivers/usb/storage/protocol. h:
51/* struct scsi_cmnd transfer buffer accessutilities */
52 enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF };
This parameter is actually a simple enumerated data type, and its meaning is also very simple: one indicates copying to the server load balancer> request_buffer, TO_XFER_BUF; the other indicates copying from the server load balancer> request_buffer, FROM_XFER_BUF. (Digression: XFER refers to TRANSFER, which is short for foreigners. When I first entered Intel, my boss gave me an Excel file, which is an abbreviation widely used in Intel. I cannot understand it after some time .) It is also necessary to define the data type as enumeration, because data transmission must have only one direction. In this case, we pass in the former, so after the judgment of the first row, 171 rows will be executed, and the cnt bytes will be copied from the buffer to (unsignedchar *) -> request_buffer + * offset. Cnt is determined in row 3. the min function does not need to be known as the minimum value. However, the linux kernel does define this function in include/linux/kernel. h.
Row 3 compares a buflen with a port B and a port B> request_bufflen-* offset. the length of the data to be transmitted this time is buflen, but it cannot exceed the value of the latter, therefore, take the small and medium values, call memcpy replication, and then add the copied byte cnt to the offset of the first row. for this transmission without using scatter gather, so here we can return the code directly to the 240 rows and return cnt.
However, the situation is certainly different for transmission using scattergather. Starting from row 186, it is obvious that we have to define a pointer to the struct scatterlist struct, because struct scatterlist is related to the architecture, taking i386 as an example, include/asm-i386/scatterlist. h:
6 struct scatterlist {
7 struct page * page;
8 unsigned int offset;
9 dma_addr_t dma_address;
10 unsigned int length;
11 };
This structure is not complex, where the page pointer usually points to the buffer for the scatter gather operation. Length indicates the length of the buffer, and offset indicates the offset of the buffer in the page. Line 3 defines a structscatterlist pointer sg, and points it to (structscatterlist *) corner stone-> request_buffer + * index. you can find out the kernel code by searching it, every time * index is initialized to 0, then usb_stor_access_xfer_buf () is called. Row 3: When row 3 is set to row 3, cnt enters the cycle. the condition of the cycle is that cnt is smaller than buflen, and * index is smaller than or equal to the number of characters (> use_sg,> use_sg, as long as it is not 0, the value in it represents the number of group elements during the scatter gather transmission.
Please note that in line 1, let sg be equal to the port number of the server load balancer-> request_buffer. (of course, add * index. if the index is not 0, then what is request_buffer? When scatter/gather is used for transmission, request_buffer is actually an array, and each element is a structscatterlist pointer, the page of each scatterlist pointer contains some pages (rather than a page), and the offset contains the total offset of each DMA buffer, which is composed of two parts, the high part indicates the page number, and the low part indicates the offset of a specific page. the high part is divided by the PAGE_SHIFT macro. The PAGE_SHIFT values of different hardware platforms are different because the page sizes of different hardware platforms are different, that is, the PAGE_SIZE here is different. Currently, the relatively cutting-edge hardware platform has a page size of 4 kB or 8 KB, while PAGE_SHIFT is 12 or 13. In other words, sg-> offset removes the 12-bit lower or 13-bit lower, which is the page number, while the 12-bit lower or 13-bit lower is the offset in the page. The reason why one sg-> offset can be used is because page size only needs 12 or 13 bits, or the offset itself only has 12 or 13 bits, an int variable can obviously contain more information than 12-bit or 13-bit. We finally need to copy the information in the buffer (that is, the 36 Bytes standard INQUIRY data buffer mentioned above) to the DMAbuffer. we already know the buffer, which is usb_stor_access_xfer_buf () where is the DMA buffer? As long as we know which page it is in and know its offset, there is a function that can help us get its corresponding kernel virtual address, which is exactly what we need, with it, we can call the memcpy function to copy data.
Therefore, rows 197 to 200 are used to calculate the page and the offset value. The latter is assigned to the unsigned int variable poff. * The offset value is usb_stor_access_xfer_buf () the parameter passed by the function, which can also control the offset of the DMA buffer for data transmission, but here we pass 0. So don't pay attention to it.
Row 3 assigns a value to unsigned int sglen. sg-> length is actually the length of the DMA buffer. Obviously, the data we copied cannot exceed this length. If * offset is also specified, it indicates that the DMA buffer is loaded from * offset, so it cannot exceed sg-> length-* offset.
Row 203, because we are still in the while loop, so first execute to row 203 for the first time, then cnt is equal to 0, buflen is the length of data buffer, passed in is 36. Sglen indicates the amount of data that can be loaded in the DMA buffer. if sglen is larger than buflen, it is very good and can be filled at once. Because it is like buflen is a ton of sand, while sglen indicates that the loading car weighs two or more tons, such as three tons. In this way, lines 206 and 207 are used to make a mark. for example, sglen is used to record the actual loads of sand, and * offset indicates the amount of sand loaded, if you have not unloaded the goods yet and want to install it again next time, you can install it if it is not full. If sglen is smaller than buflen or is just large enough, * offset must be set to 0, because the car will be filled with sand and only the next car can be called, therefore, sg and * index are also automatically added at the same time.
Next, let's look at the 219 rows. sglen must be greater than 0 at the beginning, which indicates how much data is actually installed. However, the memory management mechanism has a limit that memcpy transmission cannot span pages, that is to say, it cannot span pages. a maximum of one page is to copy the content of this page. if your data exceeds one page, you have to copy it again or multiple times because a loop is used here.
The length of each real copy is expressed by plen. Therefore, for 233 rows and 234 rows, cnt is counted. Therefore, a plen is added, and sglen is also counted, but it is an inverse calculation, so it will lose a plen each time. Poff and page are set to 0 and auto-increment. this principle is very simple. copy from the beginning of the next page. The second line calls the powerful min () function again to ensure that each copy cannot span pages.
The 222 rows and 228 rows of kmap () and kunmap () appear, and the truth is also very simple. This is an important function provided by the memory management department in the kernel. the kmap () function is used to pass it a struct page pointer, it can return the kernel virtual address of the page referred to by the page pointer, and the virtual address corresponding to the page, plus the offset known above, you can call the memcpy function to copy data. As for kunmap, the good relationships established by kmap () must be destroyed by kunmap.
Then, there is no need to talk about the two memcpy lines from 224 to 227.
Row 3: The number of bytes actually transmitted.
So far, the function usb_stor_access_xfer_buf () has been completed. (Note: it should be said that it is difficult to understand the code. to understand this function, you must understand this dual loop. the external loop is cycled by sg entry, that is to say, a sg entry loops once, while an internal loop loops by page. as we have said, an sgentry can have multiple pages, because there is no way to map pages across pages, therefore, only one page can be mapped, so loops are required .)
Return to usb_stor_set_xfer_buf (), and there is only one sentence left. if the 36 bytes we want to pass are not enough to fill the sandcar, so let's record how much sand the car can hold. the "server-> resid" is the role of the recorder.
Then we will return to fill_inquiry_response (), and the function is over. We will return to usb_stor_control_thread () again. This function will always appear after a while. For the INQUIRY command, after fill_inquiry_response (), we set the result to SAM_STAT_GOOD, which is a macro defined in the scsi system, include/scsi. h:
129 # define SAM_STAT_GOOD 0x00
In fact, it is 0. after the work is completed, set the result to 0. then, the scsi function will detect the result, so we don't have to worry about it.
Then, we enter the 384 rows. more specifically, the 388 rows are called by the server load balancer-> scsi_done () function. > Scsi_done () is actually a function pointer. in queuecommand, make it equal to scsi_done. we are required to call this function after we have done what we should do, the remaining issues will be handled by the SCSI core layer.
When the queuecommand is passed in to the INQUIRY command, all the work should be done.
Finally, I will explain it separately:
First of all, why should we distribute the transmitted data on the good end into multiple scatter gather lists? The reason why the SCSI layer includes usb-storage uses scatter gather is that this feature allows the system to transmit multiple physically discontinuous buffers in a SCSI command.
What are kmap () and kunmap () functions? Why is ING required? In this world, there is an address called a physical address and an address called a kernel address. Struct page represents the physical address. the kernel uses this struct to represent each physical page, or physical page. Obviously, we cannot directly operate the physical address in the code, the memcpy function does not know the physical address at all. it can only use the kernel address. Therefore, we need to map the physical address to the kernel address space. Kmap () is engaged in this great job. However, people who have written code know that kmap () is more familiar with high memory.