|
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-EBF025DB-1552-5E99-8C07-09932DB60552" xml:lang="en"><title>Physical |
|
13 Channel Implementation</title><shortdesc>A media driver must implement a physical channel class derived |
|
14 from the <apiname>DMediaDriver</apiname> base class. </shortdesc><prolog><metadata><keywords/></metadata></prolog><conbody> |
|
15 <p/> |
|
16 <p>This includes those drivers associated with fixed media, such as the internal |
|
17 drive, or removable media, such as a PC Card or MultiMediaCard. </p> |
|
18 <p> <codeph>DMediaDriver</codeph> is an abstract class that has virtual functions |
|
19 that must be implemented by your derived class. The following class definition |
|
20 is typical: </p> |
|
21 <codeblock id="GUID-B39837DA-6037-58D2-A1BE-3E7F1E218F7B" xml:space="preserve">class DMyMediaDriver : public DMediaDriver |
|
22 { |
|
23 public: |
|
24 DMyMediaDriver (TInt aMediaId); |
|
25 ~DMmcMediaDriver (); |
|
26 public: |
|
27 virtual void Close(); |
|
28 public: |
|
29 virtual void Disconnect(DLocalDrive* aLocalDrive, TThreadMessage*); |
|
30 virtual TInt Request(TLocDrvRequest& aRequest); |
|
31 virtual TInt PartitionInfo(TPartitionInfo& anInfo); |
|
32 virtual void NotifyPowerDown(); |
|
33 virtual void NotifyEmergencyPowerDown(); |
|
34 public: |
|
35 TInt DoCreate(TInt aMediaId); |
|
36 }; |
|
37 </codeblock> |
|
38 <p>All the functions except the constructor and <codeph>DoCreate()</codeph> either |
|
39 implement or re-implement virtual functions defined by <codeph>DMediaDriver</codeph>. </p> |
|
40 <p>The framework does not require the <codeph>DoCreate()</codeph> function, |
|
41 but it is useful to implement such a function to act as a second-phase constructor |
|
42 in the creation of the media driver. In the example code fragments, we call <codeph>DoCreate()</codeph> from |
|
43 the <xref href="GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930.dita#GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930/GUID-32B157E9-0F71-5C1B-A0FA-08D5B1ACA700">PDD |
|
44 factory object's Create()</xref> function that is responsible for creating |
|
45 the media driver. </p> |
|
46 <p>There is, of course, nothing to stop you from adding your own functions |
|
47 and data members, if this is appropriate for your implementation. In addition, |
|
48 your are also free to add other classes, functions and enums to your media |
|
49 driver implementation. </p> |
|
50 <ul> |
|
51 <li id="GUID-E17276B8-4595-5A45-89B0-D053A659A17E"><p> <xref href="GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita#GUID-EBF025DB-1552-5E99-8C07-09932DB60552/GUID-FE5BB479-7AC4-5DB8-87A3-55E855E1A176">Constructor</xref> </p> </li> |
|
52 <li id="GUID-927ED2FA-9A58-5C99-85B6-44BAD9F2F47A"><p> <xref href="GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita#GUID-EBF025DB-1552-5E99-8C07-09932DB60552/GUID-D308D7F7-25B9-5A3A-BC19-A01C5948B949">DoCreate() - second phase constructor</xref> </p> </li> |
|
53 <li id="GUID-380702AD-FBC8-584A-B875-B7C23AC841DE"><p> <xref href="GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita#GUID-EBF025DB-1552-5E99-8C07-09932DB60552/GUID-4ABC799E-33EE-5260-9CD0-71F0A5E7F980">PartitionInfo() - return the partition information</xref> </p> </li> |
|
54 <li id="GUID-D0EC11D5-CC9C-5A45-BF5B-06CDBEB8738B"><p> <xref href="GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita#GUID-EBF025DB-1552-5E99-8C07-09932DB60552/GUID-EC193360-31C2-5012-8ED2-19F1C48C8FC5">Request() - handling requests</xref> </p> </li> |
|
55 </ul> |
|
56 <section id="GUID-FE5BB479-7AC4-5DB8-87A3-55E855E1A176"><title>Constructor</title> <p>The |
|
57 media driver object is created by your PDD factory object's implementation |
|
58 of the <xref href="GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930.dita#GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930/GUID-32B157E9-0F71-5C1B-A0FA-08D5B1ACA700">Create()</xref> function. |
|
59 The following is the relevant line of code: </p> <codeblock id="GUID-012D3CB1-7C35-590E-AA5D-FBD0F21F957F" xml:space="preserve">... |
|
60 //Create my DMediaDriver derived object |
|
61 DMyMediaDriver* pD=new DMyMediaDriver (aMediaId); |
|
62 ...</codeblock> <p>Your constructor, prototyped as: </p> <codeblock id="GUID-66FD6B05-D32B-5CD6-A546-B1B8E7733EE5" xml:space="preserve">DMyMediaDriver (TInt aMediaId);</codeblock> <p>gives you the chance to do any initialisation that is safe, i.e. that |
|
63 cannot fail. Typically, this is the kind of initialisation that does not need |
|
64 to acquire resources. This is the first phase of the typical Symbian platform |
|
65 two-phase construction process. </p> <codeblock id="GUID-A1C95B09-C03B-531A-9E38-0E77F9E574F5" xml:space="preserve">DMyMediaDriver::DMyMediaDriver (TInt aMediaId) |
|
66 :DMediaDriver(aMediaId) |
|
67 { |
|
68 //…do safe initialisation here |
|
69 } |
|
70 </codeblock> <p>As this code fragment shows, you need to call the |
|
71 base class constructor first, forwarding the <codeph>TInt aMediaId</codeph> value. |
|
72 You do not need to do anything else with this value. Note that this value |
|
73 is the unique media ID used when the media driver was registered. </p> </section> |
|
74 <section id="GUID-D308D7F7-25B9-5A3A-BC19-A01C5948B949"><title>DoCreate() |
|
75 - second phase constructor</title> <p>The media driver object is created by |
|
76 your PDD factory object's implementation of the <xref href="GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930.dita#GUID-A6D14A03-ADBF-570D-8AC7-E8BC2700F930/GUID-32B157E9-0F71-5C1B-A0FA-08D5B1ACA700">Create()</xref> function. The following is the relevant line of code, which is called after |
|
77 successful creation of the media driver object: </p> <codeblock id="GUID-003B4A82-853B-5ABF-A33F-69ED7AC520F9" xml:space="preserve">... |
|
78 // Call my media driver’s second-stage constructor |
|
79 Tint r = KErrNoMemory; |
|
80 if(pD) |
|
81 { |
|
82 r = pD->DoCreate(aMediaId); |
|
83 } |
|
84 ... |
|
85 </codeblock> <p>This is a second-phase constructor that allows you |
|
86 to do more complex initialisation, and initialisation that might fail. Typically, |
|
87 this is initialisation that acquires resources (including memory). The outline |
|
88 implementation of <codeph>DoCreate()</codeph> is: </p> <codeblock id="GUID-4380C850-B722-52E5-A849-602312C2441D" xml:space="preserve">TInt DMyMediaDriver::DoCreate(TInt aMediaId) |
|
89 { |
|
90 TInt r = KErrNone; |
|
91 //…do complex initialisation here |
|
92 return r; |
|
93 } |
|
94 </codeblock> <p>Depending on the complexity of your initialisation, |
|
95 you can either do all your initialisation here, and complete immediately, |
|
96 or you can do the initialisation as an asynchronous operation, in which case |
|
97 initialisation will complete at some later time. </p> <p>If you do this <i>synchronously</i>, |
|
98 then the return code should reflect the success or failure of the operation. |
|
99 In practice, this will almost always be <codeph>KErrNone</codeph>. </p> <p>If |
|
100 you do this <i>asynchronously</i>, then, on completion of the initialisation |
|
101 processing, a call should be made to: <codeph>DMediaDriver::OpenMediaDriverComplete()</codeph> passing |
|
102 either <codeph>KErrNone</codeph> or one of the other system-wide codes as |
|
103 appropriate. </p> </section> |
|
104 <section id="GUID-4ABC799E-33EE-5260-9CD0-71F0A5E7F980"><title>PartitionInfo() |
|
105 - return the partition information</title> <p>Once the media driver has been |
|
106 successfully created and initialised, and has informed the media driver subsystem |
|
107 of this fact by a call to <codeph>DMediaDriver::OpenMediaDriverComplete()</codeph>, |
|
108 then the subsystem makes a call to the media driver's <codeph>PartitionInfo()</codeph> function |
|
109 to get partition information for the media device. </p> <p>The prototype function |
|
110 is: </p> <codeblock id="GUID-C22CA159-CDD5-5900-B753-64E1A481B6F1" xml:space="preserve">TInt PartitionInfo(TPartitionInfo& anInfo);</codeblock> <p>A <xref href="GUID-00D1DAB7-C23A-30F4-B5A3-B47EED5A5DD3.dita"><apiname>TPartitionInfo</apiname></xref> object is passed to the function, which the media driver must fill in. </p> <p>Decoding |
|
111 of partition information may require media access, and as such may be a long |
|
112 running activity. Support is provided that allows this to be done asynchronously. |
|
113 You use the <i>return code</i> from <codeph>PartitionInfo()</codeph> to tell |
|
114 the media driver subsystem which operational mode you are using: </p> <ul> |
|
115 <li id="GUID-6A5496C7-3896-5EA4-AF92-72BFAEAEED55"><p>return <b>KErrNone</b>, |
|
116 if the decoding operation is to be done <b>asynchronously</b>. Note that on |
|
117 completion, the asynchronous operation must call <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-1E38DA87-4D5C-3D3C-8FE9-145E5877D02A"><apiname>DMediaDriver::PartitionInfoComplete()</apiname></xref>, |
|
118 returning <codeph>KErrNone</codeph>, or one of the other system-wide error |
|
119 codes, if appropriate. </p> </li> |
|
120 <li id="GUID-4F34B295-062F-5FCB-809B-73F19E4DE1A2"><p>return a value <b>other |
|
121 than KErrNone </b>, if the decoding operation has been done <b>synchronously</b>. |
|
122 If the synchronous operation is successful, return <i>KErrCompletion</i>, |
|
123 otherwise return one of the other system-wide error codes, but not KErrNone. </p> </li> |
|
124 </ul> <p><b>Decoding |
|
125 simple partitions</b> </p> <p>The following example shows the implementation |
|
126 of a simple <codeph>PartitionInfo()</codeph> function. Such an implementation |
|
127 would be provided for non-removable media, such as internal Flash memory, |
|
128 where the layout is simple and known to the system. </p> <p>This implementation |
|
129 reports a single partition with the size of the entire media. The partition |
|
130 expects to be mounted with the FAT filesystem. </p> <p>Note that this operation |
|
131 is done synchronously, and the function returns <codeph>KErrCompletion</codeph> to |
|
132 indicate this. </p> <codeblock id="GUID-3D99CC4D-5996-502B-8A3F-6DE4675EAFE5" xml:space="preserve">TInt DMyMediaDriver::PartitionInfo(TPartitionInfo& aInfo) |
|
133 { |
|
134 aInfo.iPartitionCount = 1; |
|
135 aInfo.iEntry[0].iPartitionBaseAddr = 0; |
|
136 aInfo.iEntry[0].iPartitionLen = TotalSizeInBytes(); |
|
137 aInfo.iEntry[0].iPartitionType = KPartitionTypeFAT12; |
|
138 |
|
139 aInfo.iMediaSizeInBytes = TotalSizeInBytes(); |
|
140 |
|
141 return KErrCompletion; |
|
142 } |
|
143 </codeblock> <p><b>Decoding |
|
144 FAT Partitions</b> </p> <p>More complex implementations of <codeph>PartitionInfo()</codeph> may |
|
145 be required when handling removable media or more complex internal media where |
|
146 the layout of the media is unknown to the system. </p> <p>This example shows |
|
147 a typical implementation for a FAT based removable media device. Here, <codeph>PartitionInfo()</codeph> starts |
|
148 the operation, which is done asynchronously by the <codeph>DoPartitionInfo()</codeph> function. </p> <p>Note |
|
149 that <codeph>PartitionInfo()</codeph> returns <codeph>KErrNone</codeph>, which |
|
150 tells the media driver subsystem that the operation will be done asynchronously. </p> <p>Note |
|
151 also that on completion, <codeph>DoPartitionInfo()</codeph> calls <codeph>PartitionInfoComplete()</codeph> to |
|
152 tell the media driver subsystem that the operation is complete. </p> <codeblock id="GUID-17F9AD37-7D5F-5F8F-960B-EF36FBE03A04" xml:space="preserve">TInt DMyMediaDriverFlash::PartitionInfo(TPartitionInfo& aInfo) |
|
153 { |
|
154 iPartitionInfo = &anInfo // Store aInfo until needed |
|
155 |
|
156 TInt errCode = LaunchPartitionInfoRequest(); // Start the asynchronous request |
|
157 |
|
158 return(errCode); // This needs to be KErrNone to indicate that the operation |
|
159 // will be completed asynchronously (unless an error occurs launching |
|
160 // the asynchronous request!) |
|
161 } |
|
162 </codeblock> <p>This is the function that runs asynchronously </p> <codeblock id="GUID-84368231-7B29-5EE9-A93B-851C7E5C0B62" xml:space="preserve">TInt DMyMediaDriver::DoPartitionInfo() |
|
163 { |
|
164 TInt partitionCount = iPartitionInfo->iPartitionCount = 0; |
|
165 |
|
166 // Read of the first sector successful so check for a Master Boot Record |
|
167 if (*(TUint16*)(&iIntBuf[KMBRSignatureOffset])!=0xAA55) |
|
168 { |
|
169 return(KErrCorrupt); |
|
170 } |
|
171 |
|
172 // Move the partition entries to a 4 byte boundary |
|
173 memcpy(&iIntBuf[0],&iIntBuf[KMBRFirstPartitionEntry],(sizeof(TMBRPartitionEntry)<<2)); |
|
174 |
|
175 // Search for a x86 default boot partition - let this be the first |
|
176 TMBRPartitionEntry *pe=(TMBRPartitionEntry*)(&iIntBuf[0]); |
|
177 |
|
178 TInt i; |
|
179 TInt defaultPartitionNumber=-1; |
|
180 for (i=0;i<KMaxPartitionEntries;i++,pe++) |
|
181 { |
|
182 if (pe->IsDefaultBootPartition()) |
|
183 { |
|
184 SetPartitionEntry(&iPartitionInfo->iEntry[0],pe->iFirstSector,pe->iNumSectors); |
|
185 iHiddenSectors=pe->iFirstSector; |
|
186 defaultPartitionNumber=i; |
|
187 partitionCount++; |
|
188 break; |
|
189 } |
|
190 } |
|
191 |
|
192 // Now add any other partitions |
|
193 pe=(TMBRPartitionEntry*)(&iIntBuf[0]); // Reset it |
|
194 for (i=0;i<KMaxPartitionEntries;i++,pe++) |
|
195 { |
|
196 if (defaultPartitionNumber==i) |
|
197 { |
|
198 continue; // Already sorted |
|
199 } |
|
200 if (pe->IsValidDosPartition()) |
|
201 { |
|
202 SetPartitionEntry(&iPartitionInfo->iEntry[partitionCount],pe->iFirstSector,pe->iNumSectors); |
|
203 if (partitionCount==0) |
|
204 iHiddenSectors=pe->iFirstSector; |
|
205 partitionCount++; |
|
206 } |
|
207 } |
|
208 |
|
209 if (defaultPartitionNumber==(-1) && partitionCount==0) |
|
210 { |
|
211 // Assume it has no MBR, and the Boot Sector is in the 1st sector |
|
212 SetPartitionEntry(&iPartitionInfo->iEntry[0], 0, iMediaSize); |
|
213 iHiddenSectors=0; |
|
214 partitionCount=1; |
|
215 } |
|
216 |
|
217 iPartitionInfo->iPartitionCount=partitionCount; |
|
218 iPartitionInfo->iMediaSizeInBytes=TotalSizeInBytes(); |
|
219 |
|
220 PartitionInfoComplete(err); |
|
221 |
|
222 return(KErrNone); |
|
223 } |
|
224 </codeblock> </section> |
|
225 <section id="GUID-EC193360-31C2-5012-8ED2-19F1C48C8FC5"><title>Request() - |
|
226 handling requests</title> <p>You handle requests by implementing your media |
|
227 driver's <codeph>Request()</codeph> function. This is prototyped as: </p> <codeblock id="GUID-2DABBFE2-5F74-5DC4-B1A5-E50A41258E8A" xml:space="preserve">TInt Request(TLocDrvRequest& aRequest)=0;</codeblock> <p>This |
|
228 function is usually called in the context of the client thread that originally |
|
229 initiated the I/O request to the file server, although you should never assume |
|
230 so. Note that you may also see the originating thread referred to as <i>the |
|
231 remote thread</i>. </p> <p>The request type, as identified by the request |
|
232 ID, and the information associated with the request is accessed through the <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita"><apiname>TLocDrvRequest</apiname></xref> object, |
|
233 which is passed to the <codeph>Request()</codeph> function. The information |
|
234 supplied includes offsets, data lengths, the requesting thread etc, but clearly |
|
235 depends on the request ID. You get the request ID by calling <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-615CA1FE-0FDA-39F4-875D-FBB7B6F6FAD8"><apiname>TLocDrvRequest::Id()</apiname></xref>, |
|
236 and this will be one of the <xref href="GUID-720001A0-79E6-3E25-B5F0-4B39EAF95D12.dita#GUID-720001A0-79E6-3E25-B5F0-4B39EAF95D12/GUID-0559A1ED-05A1-386E-B728-0D500ED4E3EA"><apiname>DLocalDrive::TRequestId</apiname></xref> enum |
|
237 values. </p> <p>Each request ID, as defined by <xref href="GUID-720001A0-79E6-3E25-B5F0-4B39EAF95D12.dita#GUID-720001A0-79E6-3E25-B5F0-4B39EAF95D12/GUID-0559A1ED-05A1-386E-B728-0D500ED4E3EA"><apiname>DLocalDrive::TRequestId</apiname></xref> has |
|
238 a specific meaning. The information that is available also depends on the |
|
239 request ID. </p> <p>Depending on the request ID, the operation can be done |
|
240 synchronously or asynchronously. However, it is the responsibility of the |
|
241 implementor of the media driver to handle the incoming requests and to handle |
|
242 them as appropriate to the specific media, i.e. synchronously or asynchronously. </p> <p>In |
|
243 general, the function should return once the request is initiated. If the |
|
244 entire operation cannot be completed immediately, then further request processing |
|
245 must occur within ISRs and DFCs, i.e. using some hardware specific mechanism |
|
246 to indicate completion, or by the use of considerate poll timers to considerately |
|
247 poll the device for it’s current status, with the final request completion |
|
248 being done from within a DFC. The code that implements the asynchronous requests |
|
249 can run within its own thread, or use one of the default threads provided |
|
250 by the kernel (DFC queue thread 0). </p> <p>The underlying media driver framework |
|
251 allows multiple requests to be processed simultaneously. However, other than |
|
252 being able to issue multiple requests, there is no inherent support in the |
|
253 media driver framework to support the handling of multiple requests, so such |
|
254 functionality must be handled by the media driver itself. The underlying media |
|
255 driver framework does, however, provide basic support for deferring requests |
|
256 for later processing should the media driver not be capable of supporting |
|
257 multiple requests. </p> <table id="GUID-A216F849-6301-5C82-8D54-40BE612770F8"> |
|
258 <tgroup cols="2"><colspec colname="col0"/><colspec colname="col1"/> |
|
259 <tbody> |
|
260 <row> |
|
261 <entry><p>ECaps </p> </entry> |
|
262 <entry><p>This is a request for information about the size, type, attributes |
|
263 etc of the media. <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-F16E34E3-7DB1-3C6C-800D-173814AADC22"><apiname>TLocDrvRequest::RemoteDes()</apiname></xref> gives you |
|
264 access to the object into which the media driver should put the requested |
|
265 information. The object passed across is a <xref href="GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F.dita"><apiname>TLocalDriveCapsV2</apiname></xref> type, |
|
266 and this is passed by the media driver subsystem in the form of a package |
|
267 buffer, a <xref href="GUID-C7A094BD-846F-3ED2-8CCE-C0743DB3712A.dita"><apiname>TPckgBuf</apiname></xref> type. </p> <p>In practice, you just |
|
268 need to use a simple cast to access the object. The following code fragment |
|
269 is always used: </p> <codeblock id="GUID-35711E5C-5F23-5EC6-8B43-81C2168E1437" xml:space="preserve">... |
|
270 if (id == DLocalDrive::ECaps) |
|
271 { |
|
272 TLocalDriveCapsV2& c = *(TLocalDriveCapsV2*)aRequest.RemoteDes(); |
|
273 ... |
|
274 } |
|
275 .... |
|
276 </codeblock> <p>This request type is synchronous. </p> </entry> |
|
277 </row> |
|
278 <row> |
|
279 <entry><p>ERead </p> </entry> |
|
280 <entry><p>This is a request to read data from the media device asynchronously. </p> <p>You |
|
281 need to start an asynchronous operation that reads <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-5D4ABE07-1186-392F-911F-5586150A5FB4"><apiname>TLocDrvRequest::Length()</apiname></xref> bytes |
|
282 from the media, starting at position <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-0BF048C8-81F1-3E71-B13C-7605A6668A30"><apiname>TLocDrvRequest::Pos()</apiname></xref> on |
|
283 the media. </p> <p>You transfer the data to the requesting thread's process |
|
284 by calling <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>, where the first |
|
285 parameter is the source descriptor representing the data you have just read. |
|
286 For example: </p> <codeblock id="GUID-80E5A231-3F69-5C33-ADE0-1E0C7674992E" xml:space="preserve">... |
|
287 TPtrC8 des((const TUint8*)(iBase),len); |
|
288 TInt r=iReadReq->WriteRemote(&des,0); |
|
289 ... |
|
290 </codeblock> <p>In this example, <codeph>iBase</codeph> is the |
|
291 location of the data that has just been read in from the device, and <codeph>len</codeph> is |
|
292 the length of this data. The code fragment also assumes that the data to be |
|
293 returned starts at iBase, and not at some offset from iBase. </p> <p>As this |
|
294 is an asynchronous operation, then when all data has been transferred, the |
|
295 request must be completed by calling <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-8B9BB560-808F-3424-9638-7DED6737423E"><apiname>DMediaDriver::Complete()</apiname></xref>, |
|
296 passing the original request and a completion code. </p> </entry> |
|
297 </row> |
|
298 <row> |
|
299 <entry><p>EWrite </p> </entry> |
|
300 <entry><p>This is a request to write data to the media device asynchronously. </p> <p>You |
|
301 need to start an asynchronous operation that writes <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-5D4ABE07-1186-392F-911F-5586150A5FB4"><apiname>TLocDrvRequest::Length()</apiname></xref> bytes |
|
302 to the media, starting at position <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-0BF048C8-81F1-3E71-B13C-7605A6668A30"><apiname>TLocDrvRequest::Pos()</apiname></xref> on |
|
303 the media. </p> <p>Before doing the write, then you need to transfer the data |
|
304 to be written from the requesting thread's process by calling <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>, |
|
305 where the first parameter is the target descriptor. </p> <p>As this is an |
|
306 asynchronous operation, then when all data has been transferred, the request |
|
307 must be completed by calling <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-8B9BB560-808F-3424-9638-7DED6737423E"><apiname>DMediaDriver::Complete()</apiname></xref>, |
|
308 passing the original request and a completion code. </p> </entry> |
|
309 </row> |
|
310 <row> |
|
311 <entry><p>EFormat </p> </entry> |
|
312 <entry><p>This is a request to format a section of the media asynchronously. </p> <p>The |
|
313 start position of the section to be formatted can be found by calling <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-0BF048C8-81F1-3E71-B13C-7605A6668A30"><apiname>TLocDrvRequest::Pos()</apiname></xref>, |
|
314 and the number of bytes to be formatted can be found by calling <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-5D4ABE07-1186-392F-911F-5586150A5FB4"><apiname>TLocDrvRequest::Length()</apiname></xref>. </p> <p>Following |
|
315 a format operation, the state of the formatted section depends on the type |
|
316 of media. In practice, you should access locations within the specified section, |
|
317 so that bad regions can be detected and reported. </p> <p>The length of each |
|
318 format request is usually based on the value of <xref href="GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F.dita#GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F/GUID-BDF05FE9-56B5-328B-B562-9F59EF14DAB2"><apiname>TLocalDriveCapsV2::iEraseBlockSize</apiname></xref>, |
|
319 as returned by the <codeph>ECaps</codeph> request. If you need to adjust the |
|
320 start address of the next format request, you can return a positive value |
|
321 that is interpreted as indicating the actual number of bytes formatted in |
|
322 the current step. This feature is useful for media that prefers format operations |
|
323 to be performed on specific boundaries; for example MultiMedia Cards. </p> <p>As |
|
324 this is an asynchronous operation, then when the format operation has been |
|
325 done, the request must be completed by calling <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-8B9BB560-808F-3424-9638-7DED6737423E"><apiname>DMediaDriver::Complete()</apiname></xref>, |
|
326 passing the original request and a completion code. </p> </entry> |
|
327 </row> |
|
328 <row> |
|
329 <entry><p>EEnlarge </p> </entry> |
|
330 <entry><p>This is a request to enlarge the accessible range of the media asynchronously. |
|
331 For example, this is used on the internal RAM drive. </p> <p>Calling <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-5D4ABE07-1186-392F-911F-5586150A5FB4"><apiname>TLocDrvRequest::Length()</apiname></xref> gives |
|
332 you the number of bytes by which the accessible range is to be increased. </p> <p>The |
|
333 media attributes, as defined by the settings in <xref href="GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F.dita#GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F/GUID-886205BC-3234-3FDC-B17E-F9293C957097"><apiname>TLocalDriveCapsV2::iMediaAtt</apiname></xref> returned |
|
334 by the <codeph>ECaps</codeph> request, must have <xref href="GUID-8F5CB589-F631-3216-9BB0-DCCF96836E4E.dita"><apiname>KMediaAttVariableSize</apiname></xref> set, |
|
335 otherwise the request fails with <codeph>KErrNotSupported</codeph>. </p> <p>As |
|
336 this is an asynchronous operation, then when the operation is complete, the |
|
337 request must be completed by calling <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-8B9BB560-808F-3424-9638-7DED6737423E"><apiname>DMediaDriver::Complete()</apiname></xref>, |
|
338 passing the original request and a completion code. </p> </entry> |
|
339 </row> |
|
340 <row> |
|
341 <entry><p>EReduce </p> </entry> |
|
342 <entry><p>This is a request to reduce the accessible range of the media asynchronously. |
|
343 For example, this is used on the internal RAM drive. </p> <p>The range to |
|
344 be removed is defined as <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-5D4ABE07-1186-392F-911F-5586150A5FB4"><apiname>TLocDrvRequest::Length()</apiname></xref> bytes |
|
345 starting at <xref href="GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C.dita#GUID-D84A9903-AE0F-3F54-8833-E8956A88E26C/GUID-0BF048C8-81F1-3E71-B13C-7605A6668A30"><apiname>TLocDrvRequest::Pos()</apiname></xref>. In effect, the request |
|
346 removes the section from <codeph>Pos()</codeph> to <codeph>Pos()</codeph> + <codeph>Length()</codeph>, |
|
347 and the length is reduced by <codeph>Length()</codeph>. </p> <p>The media |
|
348 attributes, as defined by the settings in <xref href="GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F.dita#GUID-414CD5E5-216F-3D97-9F04-A93B1A0EA74F/GUID-886205BC-3234-3FDC-B17E-F9293C957097"><apiname>TLocalDriveCapsV2::iMediaAtt</apiname></xref> returned |
|
349 by the <codeph>ECaps</codeph> request, must have <xref href="GUID-8F5CB589-F631-3216-9BB0-DCCF96836E4E.dita"><apiname>KMediaAttVariableSize</apiname></xref> set, |
|
350 otherwise the request fails with <codeph>KErrNotSupported</codeph>. </p> <p>As |
|
351 this is an asynchronous operation, then when the operation is complete, the |
|
352 request must be completed by calling <xref href="GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC.dita#GUID-A0D4EB25-0BA4-39EE-874B-465EB9628DCC/GUID-8B9BB560-808F-3424-9638-7DED6737423E"><apiname>DMediaDriver::Complete()</apiname></xref>, |
|
353 passing the original request and a completion code. </p> </entry> |
|
354 </row> |
|
355 </tbody> |
|
356 </tgroup> |
|
357 </table> <p id="GUID-E6DF6D45-ACB1-5C0C-A4F1-02778EA69D34"><b>A simple implementation</b> </p> <codeblock id="GUID-6AABA189-4F0A-586D-B3DC-DC38F54A8721" xml:space="preserve">TInt DMyMediaDriver::Request(TLocDrvRequest& aRequest) |
|
358 { |
|
359 TInt err = KErrNotSupported; |
|
360 TInt id = aRequest.Id(); |
|
361 |
|
362 if (id == DLocalDrive::ECaps) |
|
363 { |
|
364 TLocalDriveCapsV2& c = *(TLocalDriveCapsV2*)aRequest.RemoteDes(); |
|
365 err = Caps(c); |
|
366 c.iSize = m.Drive()->iPartitionLen; |
|
367 c.iPartitionType = m.Drive()->iPartitionType; |
|
368 return(err); |
|
369 } |
|
370 |
|
371 if(iCurrentReq != NULL) |
|
372 { |
|
373 return(KMediaDriverDeferRequest); |
|
374 } |
|
375 |
|
376 iCurrentReq = &aRequest; |
|
377 |
|
378 switch(id) |
|
379 { |
|
380 case DLocalDrive::ERead: |
|
381 r = StartRead(); |
|
382 break; |
|
383 case DLocalDrive::EWrite: |
|
384 r = StartWrite(); |
|
385 break; |
|
386 case DLocalDrive::EFormat: |
|
387 r = StartErase(); |
|
388 break; |
|
389 } |
|
390 |
|
391 if (err < 0) |
|
392 { |
|
393 iCurrentReq = NULL; |
|
394 DMediaDriver::Complete(aRequest, err); |
|
395 } |
|
396 |
|
397 return(err); |
|
398 } |
|
399 </codeblock> <p>This demonstrates the following behaviour: </p> <ul> |
|
400 <li id="GUID-12FE8B56-D998-5FDC-B3B8-CAAD3CBDD6C8"><p>The <codeph>ECaps</codeph> request |
|
401 is inherently synchronous, and must complete immediately. </p> </li> |
|
402 <li id="GUID-4188EC10-8F2D-5755-BC7A-9BEB663114E5"><p>This example only handles |
|
403 a single request at a time. If the media driver is busy handling a request, |
|
404 it can return the value <xref href="GUID-4E76E7B3-202A-3FE1-AAF2-5B41FF13843D.dita"><apiname>KMediaDriverDeferRequest</apiname></xref> which |
|
405 defers the message until the current request is complete. </p> </li> |
|
406 <li id="GUID-2A148BC4-E4B8-5EF5-ACF7-50BA52E5A02E"><p>Each message is passed |
|
407 on to the specific function that is responsible for handling the message. |
|
408 This provides readability and ease of maintenance. </p> </li> |
|
409 <li id="GUID-648C0A4B-3481-55A9-B0A4-887378B0EB7A"><p>If an error occurs, |
|
410 the request is completed immediately with the specified error code. </p> </li> |
|
411 </ul> <p>The following code is the implementation of the <codeph>StartWrite()</codeph> function |
|
412 that initiates the asynchronous write operation. It gets the length and position |
|
413 information, and then calls <codeph>DoWriteStep()</codeph>: </p> <codeblock id="GUID-B853FBC7-7A25-5A6B-8799-CB577595B461" xml:space="preserve">TInt DMyMediaDriver::StartWrite() |
|
414 { |
|
415 // Start an asynchronous write operation |
|
416 |
|
417 iCurrentPos = iCurrentReq->Pos(); |
|
418 iCurrentLength = iCurrentReq->Length(); |
|
419 |
|
420 TInt err = DoWriteStep(); |
|
421 |
|
422 return(err); |
|
423 } |
|
424 </codeblock> <p> <codeph>DoWriteStep()</codeph> performs a |
|
425 single write operation. In this example, a single write operation cannot exceed |
|
426 the capabilities of the hardware, so the request is split up into chunks of <codeph>KMyMediaDriverWriteLength</codeph> bytes. </p> <codeblock id="GUID-3C11FDAE-EA06-5B7C-9FC5-DA2C591FFF49" xml:space="preserve">TInt DMyMediaDriver::DoWriteStep() |
|
427 { |
|
428 // Perform a single write step |
|
429 |
|
430 TUint8* destAddress = iBaseAddress + iCurrentPos; |
|
431 TInt writeLength = MIN(iCurrentLength, KMyMediaDriverWriteLength); |
|
432 |
|
433 TPtr8 des(iData, writeLength); |
|
434 TInt err = iCurrentReq->ReadRemote(&des, iCurrentPos - iCurrentReq->Pos()); |
|
435 if (err != KErrNone) |
|
436 { |
|
437 return(err); |
|
438 } |
|
439 |
|
440 iCurrentPos += writeLength; |
|
441 iCurrentLength -= writeLength; |
|
442 |
|
443 TheHardware::StartWrite(iCurrentPos, writeLength, iData); |
|
444 } |
|
445 </codeblock> <p>The write operation to the hardware is performed |
|
446 by <codeph>TheHardware::StartWrite()</codeph>. For most hardware, completion |
|
447 is signalled by an interrupt, and the ISR handling the interrupt will queue |
|
448 a DFC. This in turn can call a function like <codeph>WriteComplete()</codeph> shown |
|
449 below: </p> <codeblock id="GUID-E1F615B2-E148-5C79-8F78-41407C0C2013" xml:space="preserve">TInt DMyMediaDriver::WriteComplete(TInt aResult) |
|
450 { |
|
451 // Called upon completion of the write operation |
|
452 // (ie – in DFC after completion interrupt or polled status completion) |
|
453 |
|
454 TBool completeRequest = (iCurrentLength == 0) || (aResult ! = KErrNone); |
|
455 |
|
456 if(!completeRequest) |
|
457 { |
|
458 // There is more data remaining, so write some more data… |
|
459 if((aResult = DoWriteStep()) != KErrNone) |
|
460 { |
|
461 completeRequest = ETrue; |
|
462 } |
|
463 } |
|
464 |
|
465 if(completeRequest) |
|
466 { |
|
467 // We are all done, or an error occurred… |
|
468 DMediaDriver::Complete(iCurrentReq, aResult); |
|
469 iCurrentReq = NULL; |
|
470 } |
|
471 } |
|
472 </codeblock> <p> <codeph>WriteComplete()</codeph> is an example |
|
473 of a callback or completion function, and shows how a single request may be |
|
474 broken up into a number of smaller chunks. The write request is only completed |
|
475 when the entire write operation is complete or an error occurs. </p> <p>This |
|
476 simple example has demonstrated how a simple <codeph>EWrite</codeph> request |
|
477 may be handled. The <codeph>ERead</codeph> and <codeph>EFormat</codeph> requests |
|
478 are handled in exactly the same way, taking into account the message parameters |
|
479 shown in the previous table. </p> <p id="GUID-2FF89E43-6F12-5A63-A8A5-298667C5D7A2"><b>Issues about physical addresses</b> </p> <p>If |
|
480 the media driver can use physical addresses, you need to be aware of a number |
|
481 of issues. </p> <ul> |
|
482 <li id="GUID-3CCA7A0E-B245-5221-AA0F-AA9CA1F33869"><p> <i>The address scheme |
|
483 used by the hardware</i> </p> <p>All media devices have a minimum number |
|
484 of bytes that they can transfer. For example, the architecture of some memory |
|
485 card types requires data transfer in blocks of 512 bytes. To read one byte |
|
486 from this type of media device, the media driver must read a block of 512 |
|
487 bytes and extract the byte from the block. To write one byte to a media device, |
|
488 the media driver must read a block of 512 bytes, change the content of the |
|
489 byte, and write the block to the media device. </p> </li> |
|
490 <li id="GUID-6AA344A1-607A-5220-AE65-70F1A5B35967"><p> <i>Data transfer smaller |
|
491 than the minimum size</i> </p> <p>If the local media subsystem receives a |
|
492 request to transfer data with a length smaller than the minimum transfer size, |
|
493 the local media subsystem does not make a physical address available to the |
|
494 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 |
|
495 false. It is considered unsafe to give access to the physical data surrounding |
|
496 the requested memory location. </p> </li> |
|
497 <li id="GUID-B5CA6F1A-CCD3-5DA0-9297-547F0E80506A"><p> <i>Data transfer not |
|
498 aligned to the media device block boundary</i> </p> <p>If the local media |
|
499 subsystem receives a request to transfer data, and the address on the media |
|
500 device is <i>not aligned</i> to the media device block boundary, you need |
|
501 to adopt the technique suggested below. The local media subsystem will make |
|
502 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 |
|
503 true. </p> <p>Consider the following case. A request has been made to read |
|
504 1024 bytes from a media device that has a block size of 512 bytes. The 1024 |
|
505 bytes start at offset +256 on the media device. </p> <fig id="GUID-30EA5683-9B12-59D6-88EF-21F4E84988B3"> |
|
506 <image href="GUID-12CD8E91-4102-5253-A7DE-E5436028764F_d0e396579_href.png" placement="inline"/> |
|
507 </fig> <p>To get the first 256 bytes, you must read the first block of 512 |
|
508 bytes from the media device. This can corrupt the physical memory passed in |
|
509 the I/O request. The solution is to read the first block from the media device |
|
510 into an intermediate buffer. Copy the 256 bytes from that buffer into the |
|
511 physical memory passed in the I/O request. </p> <p>To get the last 256 bytes, |
|
512 you must read the third block of 512 bytes from the media device into the |
|
513 intermediate buffer. Copy the 256 bytes from that buffer into the correct |
|
514 position in the physical memory passed in the I/O request. </p> <p>The middle |
|
515 512 bytes are aligned on the media device block boundary. The media driver |
|
516 can read this data into the correct position in the physical memory passed |
|
517 in the I/O request. </p> </li> |
|
518 <li id="GUID-27C1D94D-09CB-5C70-94DF-B1FDC8784DE9"><p> <i>Scatter/Gather DMA |
|
519 controllers</i> </p> <p>DMA controllers can support the Scatter/Gather mode |
|
520 of operation. Each request in this mode of operation consists of a set of |
|
521 smaller requests chained together. This chain of requests is called the Scatter/Gather |
|
522 list. Each item in the list consists of a physical 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 |
|
523 help you populate the Scatter/Gather list. </p> <p>The following code fragment |
|
524 shows how you do this. The example assumes that the DMA controller supports |
|
525 a Scatter/Gather list with an unlimited number of entries. In practice, the |
|
526 number of entries in the list is finite. </p> <codeblock id="GUID-51D14E43-D388-507C-826E-8249EAA83287" xml:space="preserve">TPhysAddr physAddr; |
|
527 TInt physLength; |
|
528 TInt err = KErrNone; |
|
529 |
|
530 while (iRemaining > 0) |
|
531 { |
|
532 err = iCurrentReq->GetNextPhysicalAddress(physAddr, physLength); |
|
533 if(err != KErrNone) |
|
534 return err; |
|
535 |
|
536 iRemaining -= physLength; |
|
537 PopulateScatterGatherList(physAddr, physLength); |
|
538 } |
|
539 |
|
540 return DoDataTransfer(pos, length);</codeblock> </li> |
|
541 </ul> <p>See also <xref href="GUID-E55B4FE5-517C-5A23-8ACA-E28EE202330B.dita#GUID-E55B4FE5-517C-5A23-8ACA-E28EE202330B/GUID-575211BC-BF66-5817-9825-EE402648D0CD">Register |
|
542 media driver support for physical addresses</xref> </p> </section> |
|
543 </conbody></concept> |