diff -r 578be2adaf3e -r 307f4279f433 Adaptation/GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adaptation/GUID-EBF025DB-1552-5E99-8C07-09932DB60552.dita Fri Oct 15 14:32:18 2010 +0100 @@ -0,0 +1,543 @@ + + + + + +Physical +Channel ImplementationA media driver must implement a physical channel class derived +from the DMediaDriver base class. +

+

This includes those drivers associated with fixed media, such as the internal +drive, or removable media, such as a PC Card or MultiMediaCard.

+

DMediaDriver is an abstract class that has virtual functions +that must be implemented by your derived class. The following class definition +is typical:

+class DMyMediaDriver : public DMediaDriver + { +public: + DMyMediaDriver (TInt aMediaId); + ~DMmcMediaDriver (); +public: + virtual void Close(); +public: + virtual void Disconnect(DLocalDrive* aLocalDrive, TThreadMessage*); + virtual TInt Request(TLocDrvRequest& aRequest); + virtual TInt PartitionInfo(TPartitionInfo& anInfo); + virtual void NotifyPowerDown(); + virtual void NotifyEmergencyPowerDown(); +public: + TInt DoCreate(TInt aMediaId); + }; + +

All the functions except the constructor and DoCreate() either +implement or re-implement virtual functions defined by DMediaDriver.

+

The framework does not require the DoCreate() function, +but it is useful to implement such a function to act as a second-phase constructor +in the creation of the media driver. In the example code fragments, we call DoCreate() from +the PDD +factory object's Create() function that is responsible for creating +the media driver.

+

There is, of course, nothing to stop you from adding your own functions +and data members, if this is appropriate for your implementation. In addition, +your are also free to add other classes, functions and enums to your media +driver implementation.

+ +
Constructor

The +media driver object is created by your PDD factory object's implementation +of the Create() function. +The following is the relevant line of code:

... +//Create my DMediaDriver derived object +DMyMediaDriver* pD=new DMyMediaDriver (aMediaId); +...

Your constructor, prototyped as:

DMyMediaDriver (TInt aMediaId);

gives you the chance to do any initialisation that is safe, i.e. that +cannot fail. Typically, this is the kind of initialisation that does not need +to acquire resources. This is the first phase of the typical Symbian platform +two-phase construction process.

DMyMediaDriver::DMyMediaDriver (TInt aMediaId) + :DMediaDriver(aMediaId) + { + //…do safe initialisation here + } +

As this code fragment shows, you need to call the +base class constructor first, forwarding the TInt aMediaId value. +You do not need to do anything else with this value. Note that this value +is the unique media ID used when the media driver was registered.

+
DoCreate() +- second phase constructor

The media driver object is created by +your PDD factory object's implementation of the Create() function. The following is the relevant line of code, which is called after +successful creation of the media driver object:

... +// Call my media driver’s second-stage constructor +Tint r = KErrNoMemory; + if(pD) + { + r = pD->DoCreate(aMediaId); + } +... +

This is a second-phase constructor that allows you +to do more complex initialisation, and initialisation that might fail. Typically, +this is initialisation that acquires resources (including memory). The outline +implementation of DoCreate() is:

TInt DMyMediaDriver::DoCreate(TInt aMediaId) + { + TInt r = KErrNone; + //…do complex initialisation here + return r; + } +

Depending on the complexity of your initialisation, +you can either do all your initialisation here, and complete immediately, +or you can do the initialisation as an asynchronous operation, in which case +initialisation will complete at some later time.

If you do this synchronously, +then the return code should reflect the success or failure of the operation. +In practice, this will almost always be KErrNone.

If +you do this asynchronously, then, on completion of the initialisation +processing, a call should be made to: DMediaDriver::OpenMediaDriverComplete() passing +either KErrNone or one of the other system-wide codes as +appropriate.

+
PartitionInfo() +- return the partition information

Once the media driver has been +successfully created and initialised, and has informed the media driver subsystem +of this fact by a call to DMediaDriver::OpenMediaDriverComplete(), +then the subsystem makes a call to the media driver's PartitionInfo() function +to get partition information for the media device.

The prototype function +is:

TInt PartitionInfo(TPartitionInfo& anInfo);

A TPartitionInfo object is passed to the function, which the media driver must fill in.

Decoding +of partition information may require media access, and as such may be a long +running activity. Support is provided that allows this to be done asynchronously. +You use the return code from PartitionInfo() to tell +the media driver subsystem which operational mode you are using:

    +
  • return KErrNone, +if the decoding operation is to be done asynchronously. Note that on +completion, the asynchronous operation must call DMediaDriver::PartitionInfoComplete(), +returning KErrNone, or one of the other system-wide error +codes, if appropriate.

  • +
  • return a value other +than KErrNone , if the decoding operation has been done synchronously. +If the synchronous operation is successful, return KErrCompletion, +otherwise return one of the other system-wide error codes, but not KErrNone.

  • +

Decoding +simple partitions

The following example shows the implementation +of a simple PartitionInfo() function. Such an implementation +would be provided for non-removable media, such as internal Flash memory, +where the layout is simple and known to the system.

This implementation +reports a single partition with the size of the entire media. The partition +expects to be mounted with the FAT filesystem.

Note that this operation +is done synchronously, and the function returns KErrCompletion to +indicate this.

TInt DMyMediaDriver::PartitionInfo(TPartitionInfo& aInfo) + { + aInfo.iPartitionCount = 1; + aInfo.iEntry[0].iPartitionBaseAddr = 0; + aInfo.iEntry[0].iPartitionLen = TotalSizeInBytes(); + aInfo.iEntry[0].iPartitionType = KPartitionTypeFAT12; + + aInfo.iMediaSizeInBytes = TotalSizeInBytes(); + + return KErrCompletion; + } +

Decoding +FAT Partitions

More complex implementations of PartitionInfo() may +be required when handling removable media or more complex internal media where +the layout of the media is unknown to the system.

This example shows +a typical implementation for a FAT based removable media device. Here, PartitionInfo() starts +the operation, which is done asynchronously by the DoPartitionInfo() function.

Note +that PartitionInfo() returns KErrNone, which +tells the media driver subsystem that the operation will be done asynchronously.

Note +also that on completion, DoPartitionInfo() calls PartitionInfoComplete() to +tell the media driver subsystem that the operation is complete.

TInt DMyMediaDriverFlash::PartitionInfo(TPartitionInfo& aInfo) + { + iPartitionInfo = &anInfo // Store aInfo until needed + + TInt errCode = LaunchPartitionInfoRequest(); // Start the asynchronous request + + return(errCode); // This needs to be KErrNone to indicate that the operation + // will be completed asynchronously (unless an error occurs launching + // the asynchronous request!) + } +

This is the function that runs asynchronously

TInt DMyMediaDriver::DoPartitionInfo() + { + TInt partitionCount = iPartitionInfo->iPartitionCount = 0; + + // Read of the first sector successful so check for a Master Boot Record + if (*(TUint16*)(&iIntBuf[KMBRSignatureOffset])!=0xAA55) + { + return(KErrCorrupt); + } + + // Move the partition entries to a 4 byte boundary + memcpy(&iIntBuf[0],&iIntBuf[KMBRFirstPartitionEntry],(sizeof(TMBRPartitionEntry)<<2)); + + // Search for a x86 default boot partition - let this be the first + TMBRPartitionEntry *pe=(TMBRPartitionEntry*)(&iIntBuf[0]); + + TInt i; + TInt defaultPartitionNumber=-1; + for (i=0;i<KMaxPartitionEntries;i++,pe++) + { + if (pe->IsDefaultBootPartition()) + { + SetPartitionEntry(&iPartitionInfo->iEntry[0],pe->iFirstSector,pe->iNumSectors); + iHiddenSectors=pe->iFirstSector; + defaultPartitionNumber=i; + partitionCount++; + break; + } + } + + // Now add any other partitions + pe=(TMBRPartitionEntry*)(&iIntBuf[0]); // Reset it + for (i=0;i<KMaxPartitionEntries;i++,pe++) + { + if (defaultPartitionNumber==i) + { + continue; // Already sorted + } + if (pe->IsValidDosPartition()) + { + SetPartitionEntry(&iPartitionInfo->iEntry[partitionCount],pe->iFirstSector,pe->iNumSectors); + if (partitionCount==0) + iHiddenSectors=pe->iFirstSector; + partitionCount++; + } + } + + if (defaultPartitionNumber==(-1) && partitionCount==0) + { + // Assume it has no MBR, and the Boot Sector is in the 1st sector + SetPartitionEntry(&iPartitionInfo->iEntry[0], 0, iMediaSize); + iHiddenSectors=0; + partitionCount=1; + } + + iPartitionInfo->iPartitionCount=partitionCount; + iPartitionInfo->iMediaSizeInBytes=TotalSizeInBytes(); + + PartitionInfoComplete(err); + + return(KErrNone); + } +
+
Request() - +handling requests

You handle requests by implementing your media +driver's Request() function. This is prototyped as:

TInt Request(TLocDrvRequest& aRequest)=0;

This +function is usually called in the context of the client thread that originally +initiated the I/O request to the file server, although you should never assume +so. Note that you may also see the originating thread referred to as the +remote thread.

The request type, as identified by the request +ID, and the information associated with the request is accessed through the TLocDrvRequest object, +which is passed to the Request() function. The information +supplied includes offsets, data lengths, the requesting thread etc, but clearly +depends on the request ID. You get the request ID by calling TLocDrvRequest::Id(), +and this will be one of the DLocalDrive::TRequestId enum +values.

Each request ID, as defined by DLocalDrive::TRequestId has +a specific meaning. The information that is available also depends on the +request ID.

Depending on the request ID, the operation can be done +synchronously or asynchronously. However, it is the responsibility of the +implementor of the media driver to handle the incoming requests and to handle +them as appropriate to the specific media, i.e. synchronously or asynchronously.

In +general, the function should return once the request is initiated. If the +entire operation cannot be completed immediately, then further request processing +must occur within ISRs and DFCs, i.e. using some hardware specific mechanism +to indicate completion, or by the use of considerate poll timers to considerately +poll the device for it’s current status, with the final request completion +being done from within a DFC. The code that implements the asynchronous requests +can run within its own thread, or use one of the default threads provided +by the kernel (DFC queue thread 0).

The underlying media driver framework +allows multiple requests to be processed simultaneously. However, other than +being able to issue multiple requests, there is no inherent support in the +media driver framework to support the handling of multiple requests, so such +functionality must be handled by the media driver itself. The underlying media +driver framework does, however, provide basic support for deferring requests +for later processing should the media driver not be capable of supporting +multiple requests.

+ + + +

ECaps

+

This is a request for information about the size, type, attributes +etc of the media. TLocDrvRequest::RemoteDes() gives you +access to the object into which the media driver should put the requested +information. The object passed across is a TLocalDriveCapsV2 type, +and this is passed by the media driver subsystem in the form of a package +buffer, a TPckgBuf type.

In practice, you just +need to use a simple cast to access the object. The following code fragment +is always used:

... +if (id == DLocalDrive::ECaps) + { + TLocalDriveCapsV2& c = *(TLocalDriveCapsV2*)aRequest.RemoteDes(); + ... + } +.... +

This request type is synchronous.

+
+ +

ERead

+

This is a request to read data from the media device asynchronously.

You +need to start an asynchronous operation that reads TLocDrvRequest::Length() bytes +from the media, starting at position TLocDrvRequest::Pos() on +the media.

You transfer the data to the requesting thread's process +by calling TLocDrvRequest::WriteRemote(), where the first +parameter is the source descriptor representing the data you have just read. +For example:

... +TPtrC8 des((const TUint8*)(iBase),len); +TInt r=iReadReq->WriteRemote(&des,0); +... +

In this example, iBase is the +location of the data that has just been read in from the device, and len is +the length of this data. The code fragment also assumes that the data to be +returned starts at iBase, and not at some offset from iBase.

As this +is an asynchronous operation, then when all data has been transferred, the +request must be completed by calling DMediaDriver::Complete(), +passing the original request and a completion code.

+
+ +

EWrite

+

This is a request to write data to the media device asynchronously.

You +need to start an asynchronous operation that writes TLocDrvRequest::Length() bytes +to the media, starting at position TLocDrvRequest::Pos() on +the media.

Before doing the write, then you need to transfer the data +to be written from the requesting thread's process by calling TLocDrvRequest::ReadRemote(), +where the first parameter is the target descriptor.

As this is an +asynchronous operation, then when all data has been transferred, the request +must be completed by calling DMediaDriver::Complete(), +passing the original request and a completion code.

+
+ +

EFormat

+

This is a request to format a section of the media asynchronously.

The +start position of the section to be formatted can be found by calling TLocDrvRequest::Pos(), +and the number of bytes to be formatted can be found by calling TLocDrvRequest::Length().

Following +a format operation, the state of the formatted section depends on the type +of media. In practice, you should access locations within the specified section, +so that bad regions can be detected and reported.

The length of each +format request is usually based on the value of TLocalDriveCapsV2::iEraseBlockSize, +as returned by the ECaps request. If you need to adjust the +start address of the next format request, you can return a positive value +that is interpreted as indicating the actual number of bytes formatted in +the current step. This feature is useful for media that prefers format operations +to be performed on specific boundaries; for example MultiMedia Cards.

As +this is an asynchronous operation, then when the format operation has been +done, the request must be completed by calling DMediaDriver::Complete(), +passing the original request and a completion code.

+
+ +

EEnlarge

+

This is a request to enlarge the accessible range of the media asynchronously. +For example, this is used on the internal RAM drive.

Calling TLocDrvRequest::Length() gives +you the number of bytes by which the accessible range is to be increased.

The +media attributes, as defined by the settings in TLocalDriveCapsV2::iMediaAtt returned +by the ECaps request, must have KMediaAttVariableSize set, +otherwise the request fails with KErrNotSupported.

As +this is an asynchronous operation, then when the operation is complete, the +request must be completed by calling DMediaDriver::Complete(), +passing the original request and a completion code.

+
+ +

EReduce

+

This is a request to reduce the accessible range of the media asynchronously. +For example, this is used on the internal RAM drive.

The range to +be removed is defined as TLocDrvRequest::Length() bytes +starting at TLocDrvRequest::Pos(). In effect, the request +removes the section from Pos() to Pos() + Length(), +and the length is reduced by Length().

The media +attributes, as defined by the settings in TLocalDriveCapsV2::iMediaAtt returned +by the ECaps request, must have KMediaAttVariableSize set, +otherwise the request fails with KErrNotSupported.

As +this is an asynchronous operation, then when the operation is complete, the +request must be completed by calling DMediaDriver::Complete(), +passing the original request and a completion code.

+
+ + +

A simple implementation

TInt DMyMediaDriver::Request(TLocDrvRequest& aRequest) + { + TInt err = KErrNotSupported; + TInt id = aRequest.Id(); + + if (id == DLocalDrive::ECaps) + { + TLocalDriveCapsV2& c = *(TLocalDriveCapsV2*)aRequest.RemoteDes(); + err = Caps(c); + c.iSize = m.Drive()->iPartitionLen; + c.iPartitionType = m.Drive()->iPartitionType; + return(err); + } + + if(iCurrentReq != NULL) + { + return(KMediaDriverDeferRequest); + } + + iCurrentReq = &aRequest; + + switch(id) + { + case DLocalDrive::ERead: + r = StartRead(); + break; + case DLocalDrive::EWrite: + r = StartWrite(); + break; + case DLocalDrive::EFormat: + r = StartErase(); + break; + } + + if (err < 0) + { + iCurrentReq = NULL; + DMediaDriver::Complete(aRequest, err); + } + + return(err); + } +

This demonstrates the following behaviour:

    +
  • The ECaps request +is inherently synchronous, and must complete immediately.

  • +
  • This example only handles +a single request at a time. If the media driver is busy handling a request, +it can return the value KMediaDriverDeferRequest which +defers the message until the current request is complete.

  • +
  • Each message is passed +on to the specific function that is responsible for handling the message. +This provides readability and ease of maintenance.

  • +
  • If an error occurs, +the request is completed immediately with the specified error code.

  • +

The following code is the implementation of the StartWrite() function +that initiates the asynchronous write operation. It gets the length and position +information, and then calls DoWriteStep():

TInt DMyMediaDriver::StartWrite() + { + // Start an asynchronous write operation + + iCurrentPos = iCurrentReq->Pos(); + iCurrentLength = iCurrentReq->Length(); + + TInt err = DoWriteStep(); + + return(err); + } +

DoWriteStep() performs a +single write operation. In this example, a single write operation cannot exceed +the capabilities of the hardware, so the request is split up into chunks of KMyMediaDriverWriteLength bytes.

TInt DMyMediaDriver::DoWriteStep() + { + // Perform a single write step + + TUint8* destAddress = iBaseAddress + iCurrentPos; + TInt writeLength = MIN(iCurrentLength, KMyMediaDriverWriteLength); + + TPtr8 des(iData, writeLength); + TInt err = iCurrentReq->ReadRemote(&des, iCurrentPos - iCurrentReq->Pos()); + if (err != KErrNone) + { + return(err); + } + + iCurrentPos += writeLength; + iCurrentLength -= writeLength; + + TheHardware::StartWrite(iCurrentPos, writeLength, iData); + } +

The write operation to the hardware is performed +by TheHardware::StartWrite(). For most hardware, completion +is signalled by an interrupt, and the ISR handling the interrupt will queue +a DFC. This in turn can call a function like WriteComplete() shown +below:

TInt DMyMediaDriver::WriteComplete(TInt aResult) + { + // Called upon completion of the write operation + // (ie – in DFC after completion interrupt or polled status completion) + + TBool completeRequest = (iCurrentLength == 0) || (aResult ! = KErrNone); + + if(!completeRequest) + { + // There is more data remaining, so write some more data… + if((aResult = DoWriteStep()) != KErrNone) + { + completeRequest = ETrue; + } + } + + if(completeRequest) + { + // We are all done, or an error occurred… + DMediaDriver::Complete(iCurrentReq, aResult); + iCurrentReq = NULL; + } + } +

WriteComplete() is an example +of a callback or completion function, and shows how a single request may be +broken up into a number of smaller chunks. The write request is only completed +when the entire write operation is complete or an error occurs.

This +simple example has demonstrated how a simple EWrite request +may be handled. The ERead and EFormat requests +are handled in exactly the same way, taking into account the message parameters +shown in the previous table.

Issues about physical addresses

If +the media driver can use physical addresses, you need to be aware of a number +of issues.

    +
  • The address scheme +used by the hardware

    All media devices have a minimum number +of bytes that they can transfer. For example, the architecture of some memory +card types requires data transfer in blocks of 512 bytes. To read one byte +from this type of media device, the media driver must read a block of 512 +bytes and extract the byte from the block. To write one byte to a media device, +the media driver must read a block of 512 bytes, change the content of the +byte, and write the block to the media device.

  • +
  • Data transfer smaller +than the minimum size

    If the local media subsystem receives a +request to transfer data with a length smaller than the minimum transfer size, +the local media subsystem does not make a physical address available to the +media driver. A call to TLocDrvRequest::IsPhysicalAddress() returns +false. It is considered unsafe to give access to the physical data surrounding +the requested memory location.

  • +
  • Data transfer not +aligned to the media device block boundary

    If the local media +subsystem receives a request to transfer data, and the address on the media +device is not aligned to the media device block boundary, you need +to adopt the technique suggested below. The local media subsystem will make +the physical address available to the media driver. A call to TLocDrvRequest::IsPhysicalAddress() returns +true.

    Consider the following case. A request has been made to read +1024 bytes from a media device that has a block size of 512 bytes. The 1024 +bytes start at offset +256 on the media device.

    + +

    To get the first 256 bytes, you must read the first block of 512 +bytes from the media device. This can corrupt the physical memory passed in +the I/O request. The solution is to read the first block from the media device +into an intermediate buffer. Copy the 256 bytes from that buffer into the +physical memory passed in the I/O request.

    To get the last 256 bytes, +you must read the third block of 512 bytes from the media device into the +intermediate buffer. Copy the 256 bytes from that buffer into the correct +position in the physical memory passed in the I/O request.

    The middle +512 bytes are aligned on the media device block boundary. The media driver +can read this data into the correct position in the physical memory passed +in the I/O request.

  • +
  • Scatter/Gather DMA +controllers

    DMA controllers can support the Scatter/Gather mode +of operation. Each request in this mode of operation consists of a set of +smaller requests chained together. This chain of requests is called the Scatter/Gather +list. Each item in the list consists of a physical address and a length.

    Use TLocDrvRequest::GetNextPhysicalAddress() to +help you populate the Scatter/Gather list.

    The following code fragment +shows how you do this. The example assumes that the DMA controller supports +a Scatter/Gather list with an unlimited number of entries. In practice, the +number of entries in the list is finite.

    TPhysAddr physAddr; + TInt physLength; + TInt err = KErrNone; + + while (iRemaining > 0) + { + err = iCurrentReq->GetNextPhysicalAddress(physAddr, physLength); + if(err != KErrNone) + return err; + + iRemaining -= physLength; + PopulateScatterGatherList(physAddr, physLength); + } + + return DoDataTransfer(pos, length);
  • +

See also Register +media driver support for physical addresses

+
\ No newline at end of file