mmc_disk: read using "simple DMA"

The SDHCI spec also offers an "advanced DMA" mode where we can use
scatter-gather lists. It would allow to remove several of the DMA
restrictions, but hardware support for it is optional, so we need this
version anyway.

The geometry is retrieved on demand in the first read or write or in a
call to the get geometry or get device size ioctl. It is not possible to
retrieve it from the device initialization because that is called as
part of the mmc_bus scanning, which needs a specific sequence of
commands and keeps the bus locked to prevent drivers to insert their own
commands in the middle of that sequence.

TODO:
- Move the DMA restrictions definition to sdhci_pci and forward it up to
  mmc_disk (which is the one creating the IOScheduler)
- Decide if we want to keep non-DMA support (probably should, but it
  makes things more complex, because it uses virtual addresses)

Change-Id: Ib1dd14eacf62052d747bfb3ef7820bc5a34d3030
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3471
Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
Adrien Destugues
2021-01-05 02:06:44 +00:00
committed by waddlesplash
parent cc2642c124
commit 7a160a8629
9 changed files with 306 additions and 128 deletions
+6 -4
View File
@@ -15,6 +15,9 @@
#define MMC_BUS_MODULE_NAME "bus_managers/mmc/driver_v1" #define MMC_BUS_MODULE_NAME "bus_managers/mmc/driver_v1"
struct IOOperation;
enum { enum {
CARD_TYPE_MMC, CARD_TYPE_MMC,
CARD_TYPE_SD, CARD_TYPE_SD,
@@ -64,8 +67,7 @@ typedef struct mmc_bus_interface {
status_t (*set_clock)(void* controller, uint32_t kilohertz); status_t (*set_clock)(void* controller, uint32_t kilohertz);
status_t (*execute_command)(void* controller, uint8_t command, status_t (*execute_command)(void* controller, uint8_t command,
uint32_t argument, uint32_t* result); uint32_t argument, uint32_t* result);
status_t (*read_naive)(void* controller, off_t pos, status_t (*do_io)(void* controller, IOOperation* operation);
void* buffer, size_t* _length);
} mmc_bus_interface; } mmc_bus_interface;
@@ -74,8 +76,8 @@ typedef struct mmc_device_interface {
driver_module_info info; driver_module_info info;
status_t (*execute_command)(device_node* node, uint8_t command, status_t (*execute_command)(device_node* node, uint8_t command,
uint32_t argument, uint32_t* result); uint32_t argument, uint32_t* result);
status_t (*read_naive)(device_node* controller, uint16_t rca, off_t pos, status_t (*do_io)(device_node* controller, uint16_t rca,
void* buffer, size_t* _length); IOOperation* operation);
} mmc_device_interface; } mmc_device_interface;
@@ -86,12 +86,12 @@ MMCBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
status_t status_t
MMCBus::Read(uint16_t rca, off_t position, void* buffer, size_t* length) MMCBus::DoIO(uint16_t rca, IOOperation* operation)
{ {
status_t status = _ActivateDevice(rca); status_t status = _ActivateDevice(rca);
if (status != B_OK) if (status != B_OK)
return status; return status;
return fController->read_naive(fCookie, position, buffer, length); return fController->do_io(fCookie, operation);
} }
@@ -43,8 +43,7 @@ public:
status_t ExecuteCommand(uint8_t command, status_t ExecuteCommand(uint8_t command,
uint32_t argument, uint32_t* response); uint32_t argument, uint32_t* response);
status_t Read(uint16_t rca, off_t position, void* buffer, status_t DoIO(uint16_t rca, IOOperation* operation);
size_t* length);
void AcquireBus() { acquire_sem(fLockSemaphore); } void AcquireBus() { acquire_sem(fLockSemaphore); }
void ReleaseBus() { release_sem(fLockSemaphore); } void ReleaseBus() { release_sem(fLockSemaphore); }
@@ -1,9 +1,10 @@
/* /*
* Copyright 2018 Haiku, Inc. All rights reserved. * Copyright 2018-2021 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* B Krishnan Iyer, [email protected] * B Krishnan Iyer, [email protected]
* Adrien Destugues, [email protected]
*/ */
#include "mmc_bus.h" #include "mmc_bus.h"
@@ -102,21 +103,23 @@ mmc_bus_execute_command(device_node* node, uint8_t command, uint32_t argument,
static status_t static status_t
mmc_bus_read_naive(device_node* node, uint16_t rca, off_t pos, void* buffer, mmc_bus_do_io(device_node* node, uint16_t rca, IOOperation* operation)
size_t* _length)
{ {
// FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parent each time.
driver_module_info* mmc; driver_module_info* mmc;
void* cookie; void* cookie;
// FIXME store the parent cookie in the bus cookie or something instead of
// getting/putting the parent each time.
device_node* parent = gDeviceManager->get_parent_node(node); device_node* parent = gDeviceManager->get_parent_node(node);
gDeviceManager->get_driver(parent, &mmc, &cookie); gDeviceManager->get_driver(parent, &mmc, &cookie);
gDeviceManager->put_node(parent); gDeviceManager->put_node(parent);
MMCBus* bus = (MMCBus*)cookie; MMCBus* bus = (MMCBus*)cookie;
bus->AcquireBus(); bus->AcquireBus();
status_t result = bus->Read(rca, pos, buffer, _length); status_t result = B_OK;
result = bus->DoIO(rca, operation);
bus->ReleaseBus(); bus->ReleaseBus();
return result; return result;
} }
@@ -172,7 +175,7 @@ mmc_device_interface mmc_bus_controller_module = {
NULL NULL
}, },
mmc_bus_execute_command, mmc_bus_execute_command,
mmc_bus_read_naive mmc_bus_do_io
}; };
+2
View File
@@ -2,7 +2,9 @@ SubDir HAIKU_TOP src add-ons kernel busses mmc ;
SubDirC++Flags -fno-rtti ; SubDirC++Flags -fno-rtti ;
UsePrivateKernelHeaders ;
UsePrivateHeaders drivers ; UsePrivateHeaders drivers ;
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
KernelAddon sdhci_pci : KernelAddon sdhci_pci :
sdhci_pci.cpp sdhci_pci.cpp
+84 -61
View File
@@ -4,7 +4,9 @@
* *
* Authors: * Authors:
* B Krishnan Iyer, [email protected] * B Krishnan Iyer, [email protected]
* Adrien Destugues, [email protected]
*/ */
#include <algorithm>
#include <new> #include <new>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -14,6 +16,7 @@
#include <KernelExport.h> #include <KernelExport.h>
#include "IOSchedulerSimple.h"
#include "mmc.h" #include "mmc.h"
#include "sdhci_pci.h" #include "sdhci_pci.h"
@@ -49,7 +52,7 @@ class SdhciBus {
status_t InitCheck(); status_t InitCheck();
void Reset(); void Reset();
void SetClock(int kilohertz); void SetClock(int kilohertz);
status_t ReadNaive(off_t pos, void* buffer, size_t* _length); status_t DoIO(IOOperation* operation);
private: private:
bool PowerOn(); bool PowerOn();
@@ -114,8 +117,8 @@ SdhciBus::SdhciBus(struct registers* registers, uint8_t irq)
return; return;
} }
EnableInterrupts(SDHCI_INT_CMD_CMP EnableInterrupts(SDHCI_INT_CMD_CMP | SDHCI_INT_CARD_INS
| SDHCI_INT_BUF_READ_READY | SDHCI_INT_CARD_INS | SDHCI_INT_CARD_REM); | SDHCI_INT_CARD_REM | SDHCI_INT_TRANS_CMP);
// We want to see the error bits in the status register, but not have an // We want to see the error bits in the status register, but not have an
// interrupt trigger on them (we get a "command complete" interrupt on // interrupt trigger on them (we get a "command complete" interrupt on
@@ -224,9 +227,10 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
fRegisters->argument = argument; fRegisters->argument = argument;
fRegisters->command.SendCommand(command, replyType); fRegisters->command.SendCommand(command, replyType);
// Wait for command response to be available (either "command complete" or // Wait for command response to be available ("command complete" interrupt)
// "buffer read ready" interrupt will happen, depending on the command) TRACE("Wait for command complete...");
acquire_sem(fSemaphore); acquire_sem(fSemaphore);
TRACE("command complete OK\n");
if (fCommandResult & SDHCI_INT_ERROR) { if (fCommandResult & SDHCI_INT_ERROR) {
fRegisters->interrupt_status |= fCommandResult; fRegisters->interrupt_status |= fCommandResult;
@@ -267,6 +271,12 @@ SdhciBus::ExecuteCommand(uint8_t command, uint32_t argument, uint32_t* response)
break; break;
} }
if (replyType == Command::kR1bType) {
// R1b commands may use the data line so we must wait for the
// "transfer complete" interrupt here.
acquire_sem(fSemaphore);
}
ERROR("Command execution %d complete\n", command); ERROR("Command execution %d complete\n", command);
return B_OK; return B_OK;
} }
@@ -328,57 +338,75 @@ SdhciBus::SetClock(int kilohertz)
status_t status_t
SdhciBus::ReadNaive(off_t pos, void* buffer, size_t* _length) SdhciBus::DoIO(IOOperation* operation)
{ {
// TODO read multiple blocks at once (don't ignore _length) bool isWrite = operation->IsWrite();
fRegisters->block_size = 512; if (isWrite)
fRegisters->block_count = 1; return B_NOT_SUPPORTED;
fRegisters->transfer_mode = TransferMode::kSingle | TransferMode::kRead
| TransferMode::kAutoCmdDisabled | TransferMode::kNoDmaOrNoData;
uint32_t response; static const uint32 kBlockSize = 512;
status_t result; off_t offset = operation->Offset();
result = ExecuteCommand(SD_READ_SINGLE_BLOCK, pos, &response); generic_size_t length = operation->Length();
TRACE("%s %ld bytes at %ld\n", isWrite ? "Write" : "Read", length, offset);
// Check that the IO scheduler did its job in following our DMA restrictions
// We can start a read only at a sector boundary
ASSERT(offset % kBlockSize == 0);
// We can only read complete sectors
ASSERT(length % kBlockSize == 0);
const generic_io_vec* vecs = operation->Vecs();
generic_size_t vecOffset = 0;
// Must always be 512 (on SD cards it can be changed, but not on SDHC)
// FIXME can this be moved to the init function instead?
fRegisters->block_size = kBlockSize;
status_t result = B_OK;
while (length > 0) {
size_t toCopy = std::min((generic_size_t)length,
vecs->length - vecOffset);
TRACE("Reading loop %ld bytes from position %ld\n", toCopy, offset);
// If the current vec is empty, we can move to the next
if (toCopy == 0) {
vecs++;
vecOffset = 0;
continue;
}
// With SDMA we can only transfer multiples of 1 sector
ASSERT(toCopy % kBlockSize == 0);
fRegisters->system_address = vecs->base + vecOffset;
// fRegisters->adma_system_address = fDmaMemory;
fRegisters->block_count = toCopy / kBlockSize;
fRegisters->transfer_mode = TransferMode::kSingle | TransferMode::kRead
| TransferMode::kAutoCmd12Enable | TransferMode::kDmaEnable;
uint32_t response;
result = ExecuteCommand(SD_READ_MULTIPLE_BLOCKS, offset, &response);
if (result != B_OK)
break;
// Wait for DMA transfer to complete
// In theory we could go on and send other commands as long as they
// don't need the DAT lines, but it's overcomplicating things.
TRACE("Wait for transfer complete...");
acquire_sem(fSemaphore);
TRACE("transfer complete OK.\n");
length -= toCopy;
vecOffset += toCopy;
offset += toCopy;
}
if (result != B_OK) if (result != B_OK)
return result; return result;
TRACE("Command response: %02x\n", response);
if (fCommandResult & SDHCI_INT_BUF_READ_READY == 0) {
TRACE("No data!\n");
return B_ERROR;
}
// We don't know how to read more than 512 bytes (CMD18 would be needed)
if (*_length > 512)
*_length = 512;
// read block data from Buffer Data Port register
// TODO use DMA instead
size_t to_read = *_length / sizeof(uint32_t);
size_t to_drop = 512 / sizeof(uint32_t) - to_read;
uint32_t* dest = (uint32_t*)buffer;
while(to_read > 0) {
*dest = fRegisters->buffer_data_port;
TRACE("read : 0x%x", *dest);
dest++;
to_read--;
}
// We cannot read less than one sector, so we have to drop the extra data.
// This will be fixed when we use DMA and the IO scheduler (since it makes
// sure to only ask for complete sectors).
// Currently the IO scheduler does not support bounce buffers for non-DMA
// transfers.
while(to_drop > 0) {
(void*)fRegisters->buffer_data_port;
to_drop--;
}
// wait for command complete interrupt
acquire_sem(fSemaphore);
return B_OK; return B_OK;
} }
@@ -526,8 +554,6 @@ SdhciBus::RecoverError()
int32 int32
SdhciBus::HandleInterrupt() SdhciBus::HandleInterrupt()
{ {
CALLED();
#if 0 #if 0
// We could use the slot register to quickly see for which slot the // We could use the slot register to quickly see for which slot the
// interrupt is. But since we have an interrupt handler call for each slot // interrupt is. But since we have an interrupt handler call for each slot
@@ -578,12 +604,10 @@ SdhciBus::HandleInterrupt()
TRACE("Command complete interrupt handled\n"); TRACE("Command complete interrupt handled\n");
} }
// handling data transfer interrupt if (intmask & SDHCI_INT_TRANS_CMP) {
if (intmask & SDHCI_INT_BUF_READ_READY) { fRegisters->interrupt_status |= SDHCI_INT_TRANS_CMP;
TRACE("buffer read ready interrupt raised");
fRegisters->interrupt_status |= (intmask & SDHCI_INT_BUF_READ_READY);
release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE); release_sem_etc(fSemaphore, 1, B_DO_NOT_RESCHEDULE);
TRACE("Transfer complete interrupt handled\n");
} }
// handling bus power interrupt // handling bus power interrupt
@@ -755,14 +779,13 @@ execute_command(void* controller, uint8_t command, uint32_t argument,
} }
//Very naive read protocol : non DMA, 32 bits at a time (size of Buffer Data Port)
static status_t static status_t
read_naive(void* controller, off_t pos, void* buffer, size_t* _length) do_io(void* controller, IOOperation* operation)
{ {
CALLED(); CALLED();
SdhciBus* bus = (SdhciBus*)controller; SdhciBus* bus = (SdhciBus*)controller;
return bus->ReadNaive(pos, buffer, _length); return bus->DoIO(operation);
} }
@@ -793,7 +816,7 @@ static mmc_bus_interface gSDHCIPCIDeviceModule = {
set_clock, set_clock,
execute_command, execute_command,
read_naive do_io
}; };
+3 -3
View File
@@ -38,8 +38,8 @@ class TransferMode {
static const uint8_t kAutoCmdDisabled = 0 << 2; static const uint8_t kAutoCmdDisabled = 0 << 2;
static const uint8_t kAutoCmd12Enable = 1 << 2; static const uint8_t kAutoCmd12Enable = 1 << 2;
static const uint8_t kAutoCmd23Enable = 2 << 2; static const uint8_t kAutoCmd23Enable = 2 << 2;
static const uint8_t kAutoCmdAutoSelect = static const uint8_t kAutoCmdAutoSelect
kAutoCmd23Enable | kAutoCmd12Enable; = kAutoCmd23Enable | kAutoCmd12Enable;
// TODO block count enable // TODO block count enable
@@ -65,7 +65,7 @@ class Command {
static const uint8_t kSubCommand = 0x4; static const uint8_t kSubCommand = 0x4;
static const uint8_t kReplySizeMask = 0x3; static const uint8_t kReplySizeMask = 0x3;
static const uint8_t k32BitResponse = 0x2; static const uint8_t k32BitResponse = 0x2;
static const uint8_t k128BitResponse = 0x1; static const uint8_t k128BitResponse = 0x1;
static const uint8_t k32BitResponseCheckBusy = 0x3; static const uint8_t k32BitResponseCheckBusy = 0x3;
// For simplicity pre-define the standard response types from the SD // For simplicity pre-define the standard response types from the SD
+183 -47
View File
@@ -10,10 +10,10 @@
#include <new> #include <new>
#include <string.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <string.h>
#include "mmc_disk.h" #include "mmc_disk.h"
#include "mmc_icon.h" #include "mmc_icon.h"
@@ -99,8 +99,57 @@ mmc_disk_register_device(device_node* node)
{ NULL } { NULL }
}; };
return sDeviceManager->register_node(node return sDeviceManager->register_node(node, MMC_DISK_DRIVER_MODULE_NAME,
, MMC_DISK_DRIVER_MODULE_NAME, attrs, NULL, NULL); attrs, NULL, NULL);
}
static status_t
mmc_disk_execute_iorequest(void* data, IOOperation* operation)
{
mmc_disk_driver_info* info = (mmc_disk_driver_info*)data;
status_t error;
error = info->mmc->do_io(info->parent, info->rca, operation);
if (error != B_OK) {
info->scheduler->OperationCompleted(operation, error, 0);
return error;
}
info->scheduler->OperationCompleted(operation, B_OK, operation->Length());
return B_OK;
}
static status_t
mmc_block_get_geometry(mmc_disk_driver_info* info, device_geometry* geometry)
{
struct mmc_disk_csd csd;
TRACE("Get geometry\n");
status_t error = info->mmc->execute_command(info->parent, SD_SEND_CSD,
info->rca << 16, (uint32_t*)&csd);
if (error != B_OK) {
TRACE("Could not get CSD! %s\n", strerror(error));
return error;
}
TRACE("CSD: %" PRIx64 " %" PRIx64 "\n", csd.bits[0], csd.bits[1]);
if (csd.structure_version() < 3) {
geometry->bytes_per_sector = 1 << csd.read_bl_len();
geometry->sectors_per_track = csd.c_size() + 1;
geometry->cylinder_count = 1 << (csd.c_size_mult() + 2);
geometry->head_count = 1;
geometry->device_type = B_DISK;
geometry->removable = true; // TODO detect eMMC which isn't
geometry->read_only = false; // TODO check write protect switch?
geometry->write_once = false;
return B_OK;
}
TRACE("unknown CSD version %d\n", csd.structure_version());
return B_NOT_SUPPORTED;
} }
@@ -131,8 +180,48 @@ mmc_disk_init_driver(device_node* node, void** cookie)
return B_BAD_DATA; return B_BAD_DATA;
} }
TRACE("MMC card device initialized for RCA %x\n", info->rca); static const uint32 kBlockSize = 512; // FIXME get it from the CSD
status_t error;
static const uint32 kDMAResourceBufferCount = 16;
static const uint32 kDMAResourceBounceBufferCount = 16;
// TODO relax this when we have ADMA2
// TODO the restrictions depend on the SDHCI bus and should be read
// from there, not hardcoded in mmc_disk
const dma_restrictions restrictions = {
0, UINT32_MAX, /* Only 32bit address space in SDMA mode */
512, /* Align requests on sectors start and end */
B_PAGE_SIZE, /* Do not cross pages (SDMA can't do it) */
512, 1, 512};
error = info->dmaResource.Init(restrictions, kBlockSize,
kDMAResourceBufferCount, kDMAResourceBounceBufferCount);
if (error != B_OK) {
TRACE("Failed to init DMA resource");
free(info);
return error;
}
info->scheduler = new(std::nothrow) IOSchedulerSimple(&info->dmaResource);
if (info->scheduler == NULL) {
TRACE("Failed to allocate scheduler");
free(info);
return B_NO_MEMORY;
}
error = info->scheduler->Init("mmc storage");
if (error != B_OK) {
TRACE("Failed to init scheduler");
delete info->scheduler;
free(info);
return error;
}
info->scheduler->SetCallback(&mmc_disk_execute_iorequest, info);
memset(&info->geometry, 0, sizeof(info->geometry));
TRACE("MMC card device initialized for RCA %x\n", info->rca);
*cookie = info; *cookie = info;
return B_OK; return B_OK;
} }
@@ -143,6 +232,7 @@ mmc_disk_uninit_driver(void* _cookie)
{ {
CALLED(); CALLED();
mmc_disk_driver_info* info = (mmc_disk_driver_info*)_cookie; mmc_disk_driver_info* info = (mmc_disk_driver_info*)_cookie;
delete info->scheduler;
sDeviceManager->put_node(info->parent); sDeviceManager->put_node(info->parent);
free(info); free(info);
} }
@@ -216,7 +306,7 @@ mmc_block_open(void* _info, const char* path, int openMode, void** _cookie)
static status_t static status_t
mmc_block_close(void* cookie) mmc_block_close(void* cookie)
{ {
mmc_disk_handle* handle = (mmc_disk_handle*)cookie; //mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
CALLED(); CALLED();
return B_OK; return B_OK;
@@ -234,24 +324,82 @@ mmc_block_free(void* cookie)
} }
static status_t static status_t
mmc_block_read(void* cookie, off_t pos, void* buffer, size_t* _length) mmc_block_read(void* cookie, off_t pos, void* buffer, size_t* _length)
{ {
CALLED(); CALLED();
mmc_disk_handle* handle = (mmc_disk_handle*)cookie; mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
TRACE("Ready to execute %p\n", handle->info->mmc->read_naive);
return handle->info->mmc->read_naive(handle->info->parent, handle->info->rca, pos, buffer, _length); size_t length = *_length;
if (handle->info->geometry.bytes_per_sector == 0) {
status_t error = mmc_block_get_geometry(handle->info,
&handle->info->geometry);
if (error != B_OK) {
TRACE("Failed to get disk capacity");
return error;
}
}
// Do not allow reading past device end
if (pos >= handle->info->DeviceSize())
return B_BAD_VALUE;
if (pos + (off_t)length > handle->info->DeviceSize())
length = handle->info->DeviceSize() - pos;
IORequest request;
status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
if (status != B_OK)
return status;
status = handle->info->scheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
if (status == B_OK)
*_length = length;
return status;
} }
static status_t static status_t
mmc_block_write(void* cookie, off_t position, const void* buffer, mmc_block_write(void* cookie, off_t position, const void* buffer,
size_t* length) size_t* _length)
{ {
CALLED(); CALLED();
mmc_disk_handle* handle = (mmc_disk_handle*)cookie; mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
return B_NOT_SUPPORTED; size_t length = *_length;
if (handle->info->geometry.bytes_per_sector == 0) {
status_t error = mmc_block_get_geometry(handle->info,
&handle->info->geometry);
if (error != B_OK) {
TRACE("Failed to get disk capacity");
return error;
}
}
if (position >= handle->info->DeviceSize())
return B_BAD_VALUE;
if (position + (off_t)length > handle->info->DeviceSize())
length = handle->info->DeviceSize() - position;
IORequest request;
status_t status = request.Init(position, (addr_t)buffer, length, true, 0);
if (status != B_OK)
return status;
status = handle->info->scheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
if (status == B_OK)
*_length = length;
return status;
} }
@@ -261,34 +409,7 @@ mmc_block_io(void* cookie, io_request* request)
CALLED(); CALLED();
mmc_disk_handle* handle = (mmc_disk_handle*)cookie; mmc_disk_handle* handle = (mmc_disk_handle*)cookie;
return B_NOT_SUPPORTED; return handle->info->scheduler->ScheduleRequest(request);
}
static status_t
mmc_block_get_geometry(mmc_disk_handle* handle, device_geometry* geometry)
{
struct mmc_disk_csd csd;
TRACE("Ready to execute %p\n", handle->info->mmc->execute_command);
handle->info->mmc->execute_command(handle->info->parent, SD_SEND_CSD,
handle->info->rca << 16, (uint32_t*)&csd);
TRACE("CSD: %lx %lx\n", csd.bits[0], csd.bits[1]);
if (csd.structure_version() == 0) {
geometry->bytes_per_sector = 1 << csd.read_bl_len();
geometry->sectors_per_track = csd.c_size() + 1;
geometry->cylinder_count = 1 << (csd.c_size_mult() + 2);
geometry->head_count = 1;
geometry->device_type = B_DISK;
geometry->removable = true; // TODO detect eMMC which isn't
geometry->read_only = true; // TODO add write support
geometry->write_once = false;
return B_OK;
}
TRACE("unknown CSD version %d\n", csd.structure_version());
return B_NOT_SUPPORTED;
} }
@@ -316,9 +437,20 @@ mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
case B_GET_DEVICE_SIZE: case B_GET_DEVICE_SIZE:
{ {
//size_t size = info->capacity * info->block_size; // Legacy ioctl, use B_GET_GEOMETRY
//return user_memcpy(buffer, &size, sizeof(size_t)); if (info->geometry.bytes_per_sector == 0) {
return B_NOT_SUPPORTED; status_t error = mmc_block_get_geometry(info, &info->geometry);
if (error != B_OK) {
TRACE("Failed to get disk capacity");
return error;
}
}
uint64_t size = info->DeviceSize();
if (size > SIZE_MAX)
return B_NOT_SUPPORTED;
size_t size32 = size;
return user_memcpy(buffer, &size32, sizeof(size_t));
} }
case B_GET_GEOMETRY: case B_GET_GEOMETRY:
@@ -326,12 +458,16 @@ mmc_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
if (buffer == NULL || length < sizeof(device_geometry)) if (buffer == NULL || length < sizeof(device_geometry))
return B_BAD_VALUE; return B_BAD_VALUE;
device_geometry geometry; if (info->geometry.bytes_per_sector == 0) {
status_t status = mmc_block_get_geometry(handle, &geometry); status_t error = mmc_block_get_geometry(info, &info->geometry);
if (status != B_OK) if (error != B_OK) {
return status; TRACE("Failed to get disk capacity");
return error;
}
}
return user_memcpy(buffer, &geometry, sizeof(device_geometry)); return user_memcpy(buffer, &info->geometry,
sizeof(device_geometry));
} }
case B_GET_ICON_NAME: case B_GET_ICON_NAME:
+15 -2
View File
@@ -17,17 +17,30 @@
#include <mmc.h> #include <mmc.h>
#include "IOSchedulerSimple.h"
// This is the device info structure, allocated once per device
typedef struct { typedef struct {
device_node* node; device_node* node;
device_node* parent; device_node* parent;
mmc_device_interface* mmc; mmc_device_interface* mmc;
uint16_t rca; uint16_t rca;
size_t block_size; device_geometry geometry;
uint64_t capacity;
DMAResource dmaResource;
IOScheduler* scheduler;
off_t DeviceSize() const {
return (off_t)geometry.bytes_per_sector * geometry.sectors_per_track
* geometry.cylinder_count * geometry.head_count;
}
} mmc_disk_driver_info; } mmc_disk_driver_info;
// This is allocated once per open() call on the device (there can be multiple
// open file descriptors for the same device)
typedef struct { typedef struct {
mmc_disk_driver_info* info; mmc_disk_driver_info* info;
} mmc_disk_handle; } mmc_disk_handle;