RESOLUTION
To resolve this problem, the OEM can download a source file that is available later in this section. The USB Mass Storage Class client driver has been updated to support scatter/gather requests of an arbitrary size. Because the driver is public code, the OEM may download the updated source file to resolve the issue.
How to resolve the problem
To resolve the problem, update the public code of the sample driver, and then follow the instructions to update the Scsi2.c file. You can update the Scsi2.c source file with a new source file from the Microsoft Download Center.
Download information
The following file is available for download from the Microsoft Download Center:
Download the Scsi2.exe package now.
For additional information about how to download Microsoft Support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591 How to Obtain Microsoft Support Files from Online Services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help to prevent any unauthorized changes to the file.
Update the Scsi2.c file
Integrate the public code changes
by replacing the following source file with the Scsi2.c source file that is in the package from the Microsoft Download Center.
Public\Common\Oak\Drivers\USB\Class\Storage\class\Disk\Scsi2.c
To replace the Scsi2.c file, follow these steps:
- Extract the Scsi2.c file from the Scsi2.exe package that you downloaded from the Microsoft Download Center.
- Copy the Scsi2.c file to the following folder:
Windows_CE_.NET_installation\Public\Common\Oak\drivers\USB\Class\Storage\class\Disk\
- In Windows CE .NET Platform Builder, click Options on the Tools menu.
- On the Build tab, select the Enable Deptree Build check box.
- In Platform Builder, click Clean on the Build menu.
- After the Clean operation finishes, click Rebuild Platform on the Build menu.
Affected source code
The following functions in the Scsi2.c source file are affected:
- ScsiRWSG()
- InspectSgReq()
- CheckSegments()
MORE INFORMATION
Introduction
The sample USB Mass Storage Class client driver is a generic block driver that provides access to USB Mass Storage Class devices. The driver source code is located in the following folder of a Windows CE .NET installation:
Public\Common\Oak\Drivers\USB\class\Storage\
Because the USB Mass Storage Class driver is a block driver, the USB Mass Storage Class driver accepts a scatter/gather request to transfer data both to a storage device and from a storage device. A scatter/gather request is defined in the Include file that is located in the following folder:
Public\common\Oak\inc\Diskio.h
The following is a sample scatter/gather request:
#define MAX_SG_BUF 8 /* maximum scatter/gather buffers */
typedef struct _SG_BUF {
PUCHAR sb_buf; /* pointer to buffer */
DWORD sb_len; /* length of buffer */
} SG_BUF, *PSG_BUF;
typedef struct _SG_REQ {
DWORD sr_start; /* start sector number */
DWORD sr_num_sec; /* number of sectors */
DWORD sr_num_sg; /* number of scatter/gather buffers */
DWORD sr_status; /* request status */
PFN_REQDONE sr_callback; /* request completion callback function */
SG_BUF sr_sglist[1]; /* first scatter/gather buffer */
} SG_REQ, *PSG_REQ;
A scatter/gather request is a request to transfer a contiguous set of sectors. Therefore, a scatter/gather transfer is always sector-aligned. That is, a scatter/gather transfer begins and ends as follows:
- Always begins at the first byte of the first sector of a contiguous set of sectors.
- Ends at the last byte of the last sector of a contiguous set of sectors.
The transferred data is stored in a set of scatter/gather buffers that are contained in a
SG_REQ structure. Specifically, the transferred data is stored in the
sr_sglist field of the
SG_REQ structure. A scatter/gather request contains a minimum of one scatter/gather buffers and a maximum of eight scatter/gather buffers. Any scatter/gather buffers after the first scatter/gather buffer are dynamically allocated by the mounted FSD.
A scatter/gather request is typically created by the mounted FSD that uses the driver to access a storage device by means of a stream API that is accessed through the
DeviceIoControl function. The mounted FSD decides how to distribute transferred data across the scatter/gather buffers that are referenced by the
sr_sglist field.
How gather differs from scatter
A gather (or read) request contains scatter/gather buffers that are empty. The
sb_len field of the
SG_BUF structure specifies the size of the
sb_buf field in the
SG_BUF structure. This specification indicates the number of bytes that are to be read from the device.
A scatter (or write) request contains fully populated scatter/gather buffers. The
sb_len field of the contained
SG_BUF structure specifies the number of bytes contained in the sb_buf field to write to the device.
Important Transferred data does not have to be sector-aligned across scatter/gather buffers. For example, consider a scatter request to write three buffers to three sectors when the target storage device has a sector size of 2048 bytes. The three buffers are A, B, and C. The sectors are 43575 (A), 43576 (B), and 43577 (C).
The following are examples of valid scatter/gather buffers:
Example 1
sr_sglist[0].sb_len = 6144 bytes
sr_sglist[0].sb_buf = A[0], ., A[2047], B[0], ., B[2047], C[0], ., C[2047]
Example 2
sr_sglist[0].sb_len = 2048 bytes
sr_sglist[0].sb_buf = A[0], ., A[2047]
sr_sglist[1].sb_len = 2048 bytes
sr_sglist[1].sb_buf = B[0], ., B[2047]
sr_sglist[2].sb_len = 2048 bytes
sr_sglist[2].sb_buf = C[0], ., C[2047]
Example 3
sr_sglist[0].sb_len = 1011 bytes
sr_sglist[0].sb_buf = A[0], ., A[1010]
sr_sglist[1].sb_len = 5133 bytes
sr_sglist[1].sb_buf = A[1011], ., A[2047], B[0], ., B[2047], C[0], ., C[2047]
As described earlier, a scatter/gather request is a request to transfer a contiguous set of sectors. A scatter/gather buffer set that is contained in
sr_sglist is organized to form a set of buffers that occupy a contiguous set of sectors. The first byte of the transfer is the first byte of the first scatter/gather buffer, and the last byte of the transfer is the last byte of the last scatter/gather buffer.
Double buffers
Because storage devices accept only sector-aligned, block-based transfer requests, the driver cannot just iterate through a set of scatter/gather buffers and submit each scatter/gather buffer as a transfer request. Consider the earlier "Example 3" where a complete sector spans scatter/gather buffers. The scatter/gather buffers in
sr_sglist index 0 and index 1 are not sector-aligned. When the driver receives a scatter/gather request that contains scatter/gather buffers that are not sector-aligned, the driver uses a technique that is known as
double buffering to sector-align the unaligned scatter/gather buffers.
Double buffering uses an intermediate buffer that is also known as a
double buffer. The size of this double buffer is a multiple of the sector size of the target storage device. The double buffer collects sector-aligned data to transfer tothe target storage device or from the target storage device.
The double buffer behaves differently for gather than for scatter:
- Gather: The double buffer is filled to capacity (or the end of data) from the device and emptied into the scatter/gather buffer according to the scatter/gather buffer ordering. The amount of data to empty into a particular scatter/gather buffer is specified by the sb_len field of the scatter/gather buffer.
- Scatter: The double buffer is filled to capacity (or the end of data) from the respective scatter/gather buffers. The double buffer is filled according to the scatter/gather buffer ordering as it is sector-aligned in the buffer. It is then emptied into the target storage device.
The sample driver contains a statically allocated double buffer whose size is defined as follows:
#define FAT_SECTOR_SIZE 512
#define CDSECTOR_SIZE 2048
#define MAX_SECTOR_SIZE CDSECTOR_SIZE
#define MAX_SG_BUF 8
#define SG_BUFF_SIZE max((MAX_SG_BUF*FAT_SECTOR_SIZE),(3*CDSECTOR_SIZE))
Important The driver does not use the double buffer with
every transfer. Before the driver initiates a transfer with a storage device, the driver examines the scatter/gather request to determine whether the use of a double buffer is required. If the request contains any one buffer with an
sb_len field that is not a multiple of the sector size on the storage device, the use of a double buffer is required.
Size is important with double buffers
Because scatter/gather requests must be sector-aligned, the sum of all buffer
sb_len fields in the scatter/gather request must be a multiple of the sector size on the storage device.
Example of required double buffering
A gather request targets a storage device that has 2048-byte sectors, where the request contains the following three gather buffers:
SGBUF[0].sb_len = 1024
SGBUF[1].sb_len = 2048
SGBUF[2].sb_len = 3072
Note the following:
( ?{i = 0, ., 2} SGBUF[i].sb_len ) = 6144
6144 / 2048 = 3
The driver inspects the gather request and tests each gather buffer for sector-alignment by using the following calculation:
((SGBUF[i].sb_len % SECTOR_SIZE) == 0)
From this information, you know that SGBUF[0] is unaligned, SGBUF[1] is aligned, and SGBUF[2] is unaligned. Therefore, double buffering is required.
How the problem occurs
The sample driver does not correctly implement double buffering. If the driver is invoked to complete a gather request that contains gather buffers that are not sector-aligned, and if the total length of the gather request is larger than the double buffer on the driver, the gather request fails. That is, the driver assumes that the double buffer has sufficient space to hold the whole transfer request.
The problem is limited to storage devices that contain volumes that are formatted for the CDFS/UDFS. CDFS/UDFS supports a maximum scatter/gather transfer size of 64 kilobytes (KB). That maximum transfer size is a lot bigger than the 6144-byte statically allocated double buffer of the driver.