|
1 <?xml version="1.0" encoding="utf-8"?> |
|
2 <!-- Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies) All rights reserved. --> |
|
3 <!-- This component and the accompanying materials are made available under the terms of the License |
|
4 "Eclipse Public License v1.0" which accompanies this distribution, |
|
5 and is available at the URL "http://www.eclipse.org/legal/epl-v10.html". --> |
|
6 <!-- Initial Contributors: |
|
7 Nokia Corporation - initial contribution. |
|
8 Contributors: |
|
9 --> |
|
10 <!DOCTYPE concept |
|
11 PUBLIC "-//OASIS//DTD DITA Concept//EN" "concept.dtd"> |
|
12 <concept id="GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2" xml:lang="en"><title>Migration |
|
13 Tutorial: Direct Memory Addressing</title><shortdesc>To handle direct memory addressing, you must to make the following |
|
14 changes to your code. </shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody> |
|
15 <p>If a media driver uses a data transfer mechanism like <xref href="GUID-E5576D91-BC5C-588E-BF90-5A1E3C445133.dita">DMA</xref>, |
|
16 data transfer can be faster if the media driver knows that an address passed |
|
17 in an I/O request is a physical address. This is known as direct memory addressing. </p> |
|
18 <ul> |
|
19 <li id="GUID-E2C6E9D0-0150-529C-953E-32DFDEA68E77"><p> <xref href="GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2.dita#GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2/GUID-621EB44F-25BB-5B71-BF74-ED0304B7B591">Changes to registration</xref> </p> </li> |
|
20 <li id="GUID-62701989-76A6-52D5-A713-226343BED4EE"><p> <xref href="GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2.dita#GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2/GUID-49992CF9-404A-585C-8770-E9034434A344">Changes to request handling</xref> </p> </li> |
|
21 <li id="GUID-79FF128E-98B3-5613-B02A-08A81FC5B82D"><p> <xref href="GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2.dita#GUID-2380FDDE-5489-5B1C-87BB-1FD882E385D2/GUID-5F4A392F-0EAF-5E69-B902-D987B8FFD610">Issues about physical addresses</xref> </p> </li> |
|
22 </ul> |
|
23 <section id="GUID-621EB44F-25BB-5B71-BF74-ED0304B7B591"><title>Changes to |
|
24 registration</title> <p>If the media driver code can handle physical addresses, |
|
25 it must tell the local media subsystem. This process is called registration. |
|
26 The media driver calls <xref href="GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF.dita#GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF/GUID-57B3380F-3CA6-3FF4-9D79-05B718E0743A"><apiname>LocDrv::RegisterDmaDevice()</apiname></xref> to register |
|
27 as part of the <xref href="GUID-A70A01D2-467E-5BA8-A01D-6182558F3F52.dita">media |
|
28 driver initialisation</xref> process. The media driver calls <codeph>RegisterDmaDevice()</codeph> after |
|
29 the media driver has <xref href="GUID-A70A01D2-467E-5BA8-A01D-6182558F3F52.dita#GUID-A70A01D2-467E-5BA8-A01D-6182558F3F52/GUID-4A8DEEAB-32C4-5431-8226-5623E2BD9098">registered |
|
30 with the local media subsystem</xref>. </p> <p>After the call to <xref href="GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF.dita#GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF/GUID-57B3380F-3CA6-3FF4-9D79-05B718E0743A"><apiname>LocDrv::RegisterDmaDevice()</apiname></xref>, |
|
31 the local media subsystem will test if the address in an I/O request is a |
|
32 physical address or a virtual address. The local media subsystem extracts |
|
33 the information that the media driver requires. The media driver gets this |
|
34 information through calls to the functions: </p> <ul> |
|
35 <li id="GUID-93AEF5CB-CB70-5369-BC33-9F2B51903FFE"><p> <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-DD6773B4-9EF5-322F-B53D-29174DF3B3BF"><apiname>TLocDrvRequest::IsPhysicalAddress()</apiname></xref>. </p> </li> |
|
36 <li id="GUID-33328F10-BE3D-5ADA-8FFE-5E19764E43F1"><p> <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-9879145B-D996-327E-87F6-3B8337A86A56"><apiname>TLocDrvRequest::GetNextPhysicalAddress()</apiname></xref>. </p> </li> |
|
37 </ul> <p>A <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita"><apiname>TLocDrvRequest</apiname></xref> object represents an I/O request |
|
38 and is passed to the media driver. </p> <p>There are three pieces of information |
|
39 that the local media subsystem needs from the media driver when the media |
|
40 driver registers: </p> <ul> |
|
41 <li id="GUID-7A889D6A-52E0-5FBB-BC2F-A1E10A0F0D4D"><p>The <i>minimum number |
|
42 of bytes</i> that the media device can transfer. For example, the architecture |
|
43 of some memory card types requires data transfer in blocks of 512 bytes. The |
|
44 local media subsystem can not support I/O requests for physical addresses |
|
45 where the length of the data is smaller than this minimum number. This limit |
|
46 prevents accidental access of the data outside the limits of the request. </p> </li> |
|
47 <li id="GUID-C778CC22-699B-5B01-B217-C6567CBE01E3"><p>The <i>maximum number |
|
48 of bytes</i> that the media driver can transfer in one burst. This value depends |
|
49 on the hardware. For eaxample,<xref href="GUID-0D2F811C-81C3-526F-8EA4-98E50261BF4B.dita">DMA |
|
50 Framework</xref> has limits that depend on the DMA controller. </p> </li> |
|
51 <li id="GUID-49B448A8-B8ED-5EF6-9AB2-BA43FD6ED34F"><p>The <i>alignment of |
|
52 memory</i> that the DMA controller requires. For example: a DMA controller |
|
53 might require 2 byte (word or 16 bit) alignment or 4 byte (double word or |
|
54 32 bit) alignment. For 2 byte alignment, specify 2; for 4 byte alignment specify |
|
55 4 etc. The local media subsystem can not support I/O requests for physical |
|
56 addresses that are not aligned according to this value. </p> </li> |
|
57 </ul> <p>You get all of this information from the documentation for the platform. </p> <p>This |
|
58 example code extends the code shown in the section <xref href="GUID-A70A01D2-467E-5BA8-A01D-6182558F3F52.dita">Media |
|
59 driver initialisation before the system is initialised</xref>. It adds a call |
|
60 to <xref href="GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF.dita#GUID-9E60E8D9-619E-3A76-BAC8-93A60D62C7DF/GUID-57B3380F-3CA6-3FF4-9D79-05B718E0743A"><apiname>LocDrv::RegisterDmaDevice()</apiname></xref>. This call follows registration |
|
61 of the media driver with the local media subsystem. </p> <codeblock id="GUID-A56AFE4C-729A-57DA-B232-60A29CE7F735" xml:space="preserve">DECLARE_STANDARD_EXTENSION() |
|
62 { |
|
63 TInt r=KErrNoMemory; |
|
64 DPrimaryMediaBase* pM=new DPrimaryMediaBase; |
|
65 if (pM) |
|
66 {//…Required here for Asynchronous creation (if supported) |
|
67 pM->iDfcQ = &MyDfcQ; |
|
68 |
|
69 // Perform registration here |
|
70 r = LocDrv::RegisterMediaDevice(MEDIA_DEVICE_TYPE, |
|
71 MEDIA_DRIVECOUNT, |
|
72 &IMediaDriveNumbers[0], |
|
73 pM,MEDIA_NUMMEDIA,KMediaDriveName |
|
74 ); |
|
75 if ® != KErrNone) |
|
76 { |
|
77 return r; |
|
78 } |
|
79 |
|
80 // Register support for physical addressing. |
|
81 // |
|
82 // Note : in practice the values passed to RegisterDmaDevice() would be |
|
83 // either symbolic constants or functions that return values. If the |
|
84 // media driver is split into platform independent and platform dependent |
|
85 // layers, and this code is in the independent layer, then you will need |
|
86 // functions in the dependent layer to provide these values. |
|
87 r = LocDrv::RegisterDmaDevice(pM, |
|
88 512, // Block Addressing 512 Bytes |
|
89 1024, // 1024 Byte address range |
|
90 2 ); // 2 Byte alignment |
|
91 if ® != KErrNone) |
|
92 { |
|
93 return r; |
|
94 } |
|
95 ... |
|
96 } |
|
97 return®); |
|
98 } |
|
99 </codeblock> </section> |
|
100 <section id="GUID-49992CF9-404A-585C-8770-E9034434A344"><title> Changes to |
|
101 request handling</title> <p>To use physical addreses, you need to make changes |
|
102 to the code that deals with <codeph>ERead</codeph> and <codeph>EWrite</codeph> requests |
|
103 in your implementation of the <xref href="GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita#GUID-EBF025DB-1552-5E99-8C07-09932DB60552/GUID-EC193360-31C2-5012-8ED2-19F1C48C8FC5">DMediaDriver::Request()</xref> function. This section discusses the issues with <codeph>ERead</codeph> requests, |
|
104 but the principles apply to <codeph>EWrite</codeph> requests. </p> <p>There |
|
105 are a number of points to consider: </p> <p><b>Check if the address is physical </b> </p> <p>Call <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-DD6773B4-9EF5-322F-B53D-29174DF3B3BF"><apiname>TLocDrvRequest::IsPhysicalAddress()</apiname></xref> to |
|
106 test if the address passed is a physical address. For example, for a <codeph>ERead</codeph> request: </p> <codeblock id="GUID-90F1EE30-DAE5-5DE5-B42E-9EF48EB12509" xml:space="preserve">... |
|
107 // iReadReq points to a TLocDrvRequest object |
|
108 ... |
|
109 iMediaStartPos = iReadReq->Pos(); |
|
110 iTotalLength = I64LOW(iReadReq->Length()); |
|
111 iDoPhysicalAddress = iReadReq->IsPhysicalAddress(); |
|
112 if(iDoPhysicalAddress) |
|
113 { |
|
114 ..< Physical address memory code >.. |
|
115 } |
|
116 else |
|
117 { |
|
118 ...< Virtual address memory code >...</codeblock> <p><b>Physical address code </b> </p> <p>If you want to use the physical address, |
|
119 you need to get the physical address and the length of contiguous memory at |
|
120 this location. Call <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-9879145B-D996-327E-87F6-3B8337A86A56"><apiname>TLocDrvRequest::GetNextPhysicalAddress()</apiname></xref> to |
|
121 get the physical address and the length of physically contiguous memory. The |
|
122 length of physically contiguous memory can be smaller than the length supplied |
|
123 in the read request, because physical memory can be fragmented into a number |
|
124 of small blocks. If the length is smaller than the length supplied in the |
|
125 read or write request, you call <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-9879145B-D996-327E-87F6-3B8337A86A56"><apiname>TLocDrvRequest::GetNextPhysicalAddress()</apiname></xref> again |
|
126 to get the address of the next physically contiguous block of memory. You |
|
127 repeat this operation until the read request is complete. For example, for |
|
128 a <codeph>ERead</codeph> request: </p> <codeblock id="GUID-2311A626-38BF-582F-B310-8E30D73AA1A7" xml:space="preserve">... |
|
129 // iReadReq points to a TLocDrvRequest object |
|
130 ... |
|
131 TPhysAddr physAddr; |
|
132 TInt physLength; |
|
133 TInt err = KErrNone; |
|
134 err = iReadReq->GetNextPhysicalAddress(physAddr, physLength); |
|
135 if(err == KErrNone) |
|
136 { |
|
137 if (physLength < iTotalLength) |
|
138 { |
|
139 // Memory is fragmented, note remainder. You will need |
|
140 // to use this code again using the remainder value. |
|
141 iRemaining = iTotalLength – physLength; |
|
142 iTotalLength -= physLength; |
|
143 } |
|
144 |
|
145 // Start data transfer into the current physically |
|
146 // contiguous block of physical memory. |
|
147 DoDataTransfer(iMediaStartPos, physLength, physAddr); |
|
148 ... |
|
149 } |
|
150 </codeblock> <p>If you do not want to deal with fragmented physical memory, |
|
151 you can use your original code. </p> <p><b>Virtual to physical address translation </b> </p> <p>Your code must not |
|
152 perform a virtual to physical address translation when it deals with physical |
|
153 memory. For example: </p> <codeblock id="GUID-4377A0D5-8E41-59AF-B26E-C468ABAFCF47" xml:space="preserve">void DMMCRxDmaHelp::ChannelTransfer(const SDmaPseudoDes& aDes) |
|
154 { |
|
155 … |
|
156 TPhysAddr dest; |
|
157 |
|
158 if (iCurrentReq->IsPhysicalAddress()) |
|
159 dest = (TPhysAddr) aDes.iDest; |
|
160 else |
|
161 dest = Epoc::LinearToPhysical(aDes.iDest); |
|
162 TPlatDma::SetDMARegister(KHoDMA_CDSA(iChno), dest); |
|
163 … |
|
164 }</codeblock> <p><b>Eliminate inter-process copy </b> </p> <p>You must change your code to |
|
165 remove calls to <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-842F91FF-C780-3E2A-8860-8F34815452FC"><apiname>TLocDrvRequest::WriteRemote()</apiname></xref>. For <codeph>ERead</codeph> requests, |
|
166 data for transfer is already at the physical address. For example: </p> <codeblock id="GUID-1B14C66A-476C-5E3F-B0D7-502B54324D74" xml:space="preserve">if (!iCurrentReq->IsPhysicalAddress()) |
|
167 { |
|
168 if( (id == DMediaPagingDevice::ERomPageInRequest)|| |
|
169 (id == DMediaPagingDevice::ECodePageInRequest) ) |
|
170 { |
|
171 r = iCurrentReq->WriteToPageHandler((TUint8 *)(& iBufPtr [0]), len, usrOfst); |
|
172 } |
|
173 else if(id==DLocalDrive::ERead) |
|
174 { |
|
175 r = iCurrentReq->WriteRemote(&iBufPtr,usrOfst); |
|
176 } |
|
177 }</codeblock> <p>The same logic applies to <codeph>EWrite</codeph> requests. |
|
178 You need to remove calls to <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-96B80631-AD1C-35E8-8613-0A630BD7D9E9"><apiname>TLocDrvRequest::ReadRemote()</apiname></xref>. </p> <p><b>Test your changes </b> </p> <p>You are recommended to run regression tests |
|
179 on your changed code to makes sure that the media driver operates correctly. </p> </section> |
|
180 <section id="GUID-5F4A392F-0EAF-5E69-B902-D987B8FFD610"><title>Issues about |
|
181 physical addresses</title> <p>If the media driver can use physical addresses, |
|
182 you need to be aware of a number of issues. </p> <p><b>The address scheme used by the hardware </b> </p> <p>All media devices |
|
183 have a minimum number of bytes that they can transfer. For example, the architecture |
|
184 of some memory card types requires data transfer in blocks of 512 bytes. To |
|
185 read one byte from this type of media device, the media driver must read a |
|
186 block of 512 bytes and extract the byte from the block. To write one byte |
|
187 to a media device, the media driver must read a block of 512 bytes, change |
|
188 the content of the byte, and write the block to the media device. </p> <p><b>Data transfer smaller than the minimum size </b> </p> <p>If the local |
|
189 media subsystem receives a request to transfer data with a length smaller |
|
190 than the minimum transfer size, the local media subsystem does not make a |
|
191 physical address available to the media driver. A call to <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-DD6773B4-9EF5-322F-B53D-29174DF3B3BF"><apiname>TLocDrvRequest::IsPhysicalAddress()</apiname></xref> returns |
|
192 false. It is considered unsafe to give access to the physical data surrounding |
|
193 the requested memory location. </p> <p><b>Data transfer not aligned to the media device block boundary </b> </p> <p>If |
|
194 the local media subsystem receives a request to transfer data, and the address |
|
195 on the media device is <i>not aligned</i> to the media device block boundary, |
|
196 you need to adopt the technique suggested below. The local media subsystem |
|
197 will make the physical address available to the media driver. A call to <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-DD6773B4-9EF5-322F-B53D-29174DF3B3BF"><apiname>TLocDrvRequest::IsPhysicalAddress()</apiname></xref> returns |
|
198 true. </p> <p>Consider the following case. A request has been made to read |
|
199 1024 bytes from a media device that has a block size of 512 bytes. The 1024 |
|
200 bytes start at offset +256 on the media device. </p> <fig id="GUID-2689C022-180B-51EF-A02D-E63ACA832EB2"> |
|
201 <image href="GUID-647ADEDA-AB5A-548F-93C3-D099EAE6A030_d0e17089_href.png" placement="inline"/> |
|
202 </fig> <p>To get the first 256 bytes, you must read the first block of 512 |
|
203 bytes from the media device. This can corrupt the physical memory passed in |
|
204 the I/O request. The solution is to read the first block from the media device |
|
205 into an intermediate buffer. Copy the 256 bytes from that buffer into the |
|
206 physical memory passed in the I/O request. </p> <p>To get the last 256 bytes, |
|
207 you must read the third block of 512 bytes from the media device into the |
|
208 intermediate buffer. Copy the 256 bytes from that buffer into the correct |
|
209 position in the physical memory passed in the I/O request. </p> <p>The middle |
|
210 512 bytes are aligned on the media device block boundary. The media driver |
|
211 can read this data into the correct position in the physical memory passed |
|
212 in the I/O request. </p> <p><b>Scatter/Gather DMA controllers </b> </p> <p>DMA controllers can support |
|
213 the Scatter/Gather mode of operation. Each request in this mode of operation |
|
214 consists of a set of smaller requests chained together. This chain of requests |
|
215 is called the Scatter/Gather list. Each item in the list consists of a physical |
|
216 address and a length. </p> <p>Use <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-9879145B-D996-327E-87F6-3B8337A86A56"><apiname>TLocDrvRequest::GetNextPhysicalAddress()</apiname></xref> to |
|
217 help you populate the Scatter/Gather list. </p> <p>The following code fragment |
|
218 shows how you do this. The example assumes that the DMA controller supports |
|
219 a Scatter/Gather list with an unlimited number of entries. In practice, the |
|
220 number of entries in the list is finite. </p> <codeblock id="GUID-EC3A441C-080F-5EB8-B0E8-E658F80687D4" xml:space="preserve">TPhysAddr physAddr; |
|
221 TInt physLength; |
|
222 TInt err = KErrNone; |
|
223 |
|
224 while (iRemaining > 0) |
|
225 { |
|
226 err = iCurrentReq->GetNextPhysicalAddress(physAddr, physLength); |
|
227 if(err != KErrNone) |
|
228 return err; |
|
229 |
|
230 iRemaining -= physLength; |
|
231 PopulateScatterGatherList(physAddr, physLength); |
|
232 } |
|
233 |
|
234 return DoDataTransfer(pos, length);</codeblock> </section> |
|
235 </conbody><related-links> |
|
236 <link href="GUID-86082C0C-B0EE-5E7C-85B4-4A509066012F.dita"><linktext>MMC Porting |
|
237 Implementation Tutorial</linktext></link> |
|
238 </related-links></concept> |