xref: /device/qemu/drivers/virtio/virtblock.c (revision d6aed566)
1/*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * Simple virtio-mmio block driver emulating MMC device (spec 4.3).
17 */
18
19#include "los_vm_iomap.h"
20#include "mmc_block.h"
21#include "dmac_core.h"
22#include "osal.h"
23#include "osal/osal_io.h"
24#include "virtmmio.h"
25
26/*
27 * Kernel take care lock & cache(bcache), we only take care I/O.
28 * When I/O is carrying, we must wait for completion. Since any
29 * time there is only one I/O request come here, we can use the
30 * most simple virt-queue -- only have 4 descriptors: one for
31 * "request header", one for "I/O buffer", one for "response",
32 * and one left unused. That is, the driver and the device are
33 * always in synchonous mode!
34 */
35#define VIRTQ_REQUEST_QSZ       4
36
37#define VIRTIO_BLK_F_RO         (1 << 5)
38#define VIRTIO_BLK_F_BLK_SIZE   (1 << 6)
39#define VIRTMMIO_BLK_NAME       "virtblock"
40#define VIRTBLK_DRIVER          "/dev/mmcblk"
41#define VIRTBLK_DEF_BLK_SIZE    8192
42
43struct VirtblkConfig {
44    uint64_t capacity;
45    uint32_t segMax;
46    struct VirtblkGeometry {
47        uint16_t cylinders;
48        uint8_t heads;
49        uint8_t sectors;
50    } geometry;
51    uint32_t blkSize;
52    uint8_t otherFieldsOmitted[0];
53};
54
55/* request type: only support IN, OUT */
56#define VIRTIO_BLK_T_IN             0
57#define VIRTIO_BLK_T_OUT            1
58#define VIRTIO_BLK_T_FLUSH          4
59#define VIRTIO_BLK_T_DISCARD        11
60#define VIRTIO_BLK_T_WRITE_ZEROES   13
61
62/* response status */
63#define VIRTIO_BLK_S_OK             0
64#define VIRTIO_BLK_S_IOERR          1
65#define VIRTIO_BLK_S_UNSUPP         2
66
67struct VirtblkReq {
68    uint32_t type;
69    uint32_t reserved;
70    uint64_t startSector;
71};
72
73struct Virtblk {
74    struct VirtmmioDev dev;
75
76    uint64_t capacity;      /* in 512-byte-sectors */
77    uint32_t blkSize;       /* block(cluster) size */
78    struct VirtblkReq req;  /* static memory for request */
79    uint8_t resp;           /*              and response */
80    DmacEvent event;        /* for waiting I/O completion */
81};
82
83#define FAT32_MAX_CLUSTER_SECS  128
84
85static bool Feature0(uint32_t features, uint32_t *supported, void *dev)
86{
87    struct Virtblk *blk = dev;
88    struct VirtblkConfig *conf = (void *)(blk->dev.base + VIRTMMIO_REG_CONFIG);
89    uint32_t bs;
90
91    if (features & VIRTIO_BLK_F_RO) {
92        HDF_LOGE("[%s]not support readonly device", __func__);
93        return false;
94    }
95
96    blk->blkSize = VIRTBLK_DEF_BLK_SIZE;
97    if (features & VIRTIO_BLK_F_BLK_SIZE) {
98        bs = conf->blkSize;
99        if ((bs >= MMC_SEC_SIZE) && (bs <= FAT32_MAX_CLUSTER_SECS * MMC_SEC_SIZE) &&
100            ((bs & (bs - 1)) == 0)) {
101            blk->blkSize = bs;
102            *supported |= VIRTIO_BLK_F_BLK_SIZE;
103        }
104    }
105
106    blk->capacity = conf->capacity;
107    return true;
108}
109
110static bool Feature1(uint32_t features, uint32_t *supported, void *dev)
111{
112    (void)dev;
113    if (features & VIRTIO_F_VERSION_1) {
114        *supported |= VIRTIO_F_VERSION_1;
115    } else {
116        HDF_LOGE("[%s]virtio-mmio block has no VERSION_1 feature", __func__);
117        return false;
118    }
119
120    return true;
121}
122
123static void PopulateRequestQ(const struct Virtblk *blk)
124{
125    const struct Virtq *q = &blk->dev.vq[0];
126    int i = 0;
127
128    q->desc[i].pAddr = VMM_TO_DMA_ADDR((VADDR_T)&blk->req);
129    q->desc[i].len = sizeof(struct VirtblkReq);
130    q->desc[i].flag = VIRTQ_DESC_F_NEXT;
131    q->desc[i].next = i + 1;
132
133    i++;
134    q->desc[i].next = i + 1;
135
136    i++;
137    q->desc[i].pAddr = VMM_TO_DMA_ADDR((VADDR_T)&blk->resp);
138    q->desc[i].len = sizeof(uint8_t);
139    q->desc[i].flag = VIRTQ_DESC_F_WRITE;
140}
141
142static uint8_t VirtblkIO(struct Virtblk *blk, uint32_t cmd, uint64_t startSector,
143                         uint8_t *buf, uint32_t sectors)
144{
145    uint32_t ret;
146    struct Virtq *q = &blk->dev.vq[0];
147
148    /* fill in and notify virt queue */
149    blk->req.type = cmd;
150    blk->req.startSector = startSector;
151    q->desc[1].pAddr = VMM_TO_DMA_ADDR((VADDR_T)buf);
152    q->desc[1].len = sectors * MMC_SEC_SIZE;
153    if (cmd == VIRTIO_BLK_T_IN) {
154        q->desc[1].flag = VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE;
155    } else { /* must be VIRTIO_BLK_T_OUT */
156        q->desc[1].flag = VIRTQ_DESC_F_NEXT;
157    }
158    q->avail->ring[q->avail->index % q->qsz] = 0;
159    DSB;
160    q->avail->index++;
161    OSAL_WRITEL(0, blk->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
162
163    /* wait for completion */
164    if ((ret = DmaEventWait(&blk->event, 1, HDF_WAIT_FOREVER)) != 1) {
165        HDF_LOGE("[%s]FATAL: wait event failed: %u", __func__, ret);
166        return VIRTIO_BLK_S_IOERR;
167    }
168
169    return blk->resp;
170}
171
172static uint32_t VirtblkIRQhandle(uint32_t swIrq, void *dev)
173{
174    (void)swIrq;
175    struct Virtblk *blk = dev;
176    struct Virtq *q = &blk->dev.vq[0];
177
178    if (!(OSAL_READL(blk->dev.base + VIRTMMIO_REG_INTERRUPTSTATUS) & VIRTMMIO_IRQ_NOTIFY_USED)) {
179        return 1;
180    }
181
182    (void)DmaEventSignal(&blk->event, 1);
183    q->last++;
184
185    OSAL_WRITEL(VIRTMMIO_IRQ_NOTIFY_USED, blk->dev.base + VIRTMMIO_REG_INTERRUPTACK);
186    return 0;
187}
188
189static void VirtblkDeInit(struct Virtblk *blk)
190{
191    if (blk->dev.irq & ~_IRQ_MASK) {
192        OsalUnregisterIrq(blk->dev.irq & _IRQ_MASK, blk);
193    }
194    LOS_DmaMemFree(blk);
195}
196
197static struct Virtblk *VirtblkInitDev(void)
198{
199    struct Virtblk *blk = NULL;
200    VADDR_T base;
201    uint16_t qsz;
202    int len, ret;
203
204    len = sizeof(struct Virtblk) + VirtqSize(VIRTQ_REQUEST_QSZ);
205    if ((blk = LOS_DmaMemAlloc(NULL, len, sizeof(void *), DMA_CACHE)) == NULL) {
206        HDF_LOGE("[%s]alloc virtio-block memory failed", __func__);
207        return NULL;
208    }
209    memset_s(blk, len, 0, len);
210
211    if (!VirtmmioDiscover(VIRTMMIO_DEVICE_ID_BLK, &blk->dev)) {
212        goto ERR_OUT;
213    }
214
215    VirtmmioInitBegin(&blk->dev);
216    if (!VirtmmioNegotiate(&blk->dev, Feature0, Feature1, blk)) {
217        goto ERR_OUT1;
218    }
219    base = ALIGN((VADDR_T)blk + sizeof(struct Virtblk), VIRTQ_ALIGN_DESC);
220    qsz = VIRTQ_REQUEST_QSZ;
221    if (VirtmmioConfigQueue(&blk->dev, base, &qsz, 1) == 0) {
222        goto ERR_OUT1;
223    }
224
225    if ((ret = DmaEventInit(&blk->event)) != HDF_SUCCESS) {
226        HDF_LOGE("[%s]initialize event control block failed: %#x", __func__, ret);
227        goto ERR_OUT1;
228    }
229    ret = OsalRegisterIrq(blk->dev.irq, OSAL_IRQF_TRIGGER_NONE, (OsalIRQHandle)VirtblkIRQhandle,
230                          VIRTMMIO_BLK_NAME, blk);
231    if (ret != HDF_SUCCESS) {
232        HDF_LOGE("[%s]register IRQ failed: %d", __func__, ret);
233        goto ERR_OUT1;
234    }
235    blk->dev.irq |= ~_IRQ_MASK;
236
237    PopulateRequestQ(blk);
238    VritmmioInitEnd(&blk->dev);  /* now virt queue can be used */
239    return blk;
240
241ERR_OUT1:
242    VirtmmioInitFailed(&blk->dev);
243ERR_OUT:
244    VirtblkDeInit(blk);
245    return NULL;
246}
247
248
249/*
250 * MMC code
251 *
252 * HDF MmcCntlr act like an application adapter, they discover MMC device,
253 * send I/O request, receive response data.
254 * We act like a card adapter, receive requests, control MMC bus, drive 'card'
255 * execution, and report result. Yes, we are part of MmcCntlr -- MMC controller!
256 * Every hardware internal information are in our scope, such as state, CRC, RCA.
257 * So, we COULD(SHOULD) safely ignore them!
258 */
259
260#define OCR_LE_2G       (0x00FF8080 | MMC_CARD_BUSY_STATUS)
261#define OCR_GT_2G       (0x40FF8080 | MMC_CARD_BUSY_STATUS)
262#define CAPACITY_2G     (0x80000000 / 512)
263#define READ_BL_LEN     11
264#define C_SIZE_MULT     7
265#define U32_BITS        32
266
267/*
268 * Example bits: start=62 bits=4 value=0b1011
269 *
270 *             bit127
271 *  resp[0]
272 *  resp[1]                         1   0
273 *  resp[2]      1    1
274 *  resp[3]
275 *                                     bit0
276 *
277 * NOTE: no error check, related 'resp' bits must be zeroed and set only once.
278 */
279static void FillCidCsdBits(uint32_t *resp, int start, int bits, uint32_t value)
280{
281    uint32_t index, lsb;
282
283    index = CID_LEN - 1 - start / U32_BITS; /* CSD has the same length */
284    lsb = start % U32_BITS;
285    resp[index] |= value << lsb;
286
287    if (lsb + bits > U32_BITS) {
288        resp[index - 1] |= value >> (U32_BITS - lsb);
289    }
290}
291
292#define MMC_CID_CBX_SBIT    112
293#define MMC_CID_CBX_WIDTH   2
294#define MMC_CID_PNM_SBYTE   3
295#define MMC_CID_PNM_BYTES   6
296#define MMC_CID_PSN_SBYTE   10
297static void VirtMmcFillRespCid(struct MmcCmd *cmd, const struct Virtblk *blk)
298{
299    uint8_t *b = (uint8_t *)cmd->resp;
300
301    /* embedded card, so can leverage kernel eMMC rootfs support */
302    FillCidCsdBits(cmd->resp, MMC_CID_CBX_SBIT, MMC_CID_CBX_WIDTH, 1);
303
304    (void)memcpy_s(&b[MMC_CID_PNM_SBYTE], MMC_CID_PNM_BYTES, VIRTMMIO_BLK_NAME, MMC_CID_PNM_BYTES);
305    *(uint32_t *)&b[MMC_CID_PSN_SBYTE] = (uint32_t)blk; /* unique sn */
306    /* leave other fields random */
307}
308
309#define MMC_CSD_STRUCT_SBIT     126
310#define MMC_CSD_STRUCT_WIDTH    2
311
312#define MMC_CSD_VERS_SBIT       122
313#define MMC_CSD_VERS_WIDTH      4
314
315#define MMC_CSD_CCC_SBIT        84
316#define MMC_CSD_CCC_WIDTH       12
317
318#define MMC_CSD_RBLEN_SBIT      80
319#define MMC_CSD_RBLEN_WIDTH     4
320
321#define MMC_CSD_RBPART_SBIT     79
322
323#define MMC_CSD_WBMISALIGN_SBIT 78
324
325#define MMC_CSD_RBMISALIGN_SBIT 77
326
327#define MMC_CSD_DSRIMP_SBIT     76
328
329#define MMC_CSD_CSIZE_SBIT      62
330#define MMC_CSD_CSIZE_WIDTH     12
331#define MMC_CSD_CSIZE_VAL       0xFFF
332
333#define MMC_CSD_CSIZEMUL_SBIT   47
334#define MMC_CSD_CSIZEMUL_WIDTH  3
335
336#define MMC_CSD_EGRPSIZE_SBIT   42
337#define MMC_CSD_EGRPSIZE_WIDTH  5
338#define MMC_CSD_EGRPSIZE_VAL    31
339
340#define MMC_CSD_EGRPMULT_SBIT   37
341#define MMC_CSD_EGRPMULT_WIDTH  5
342#define MMC_CSD_EGRPMULT_VAL    15
343
344#define MMC_CSD_WBLEN_SBIT      22
345#define MMC_CSD_WBLEN_WIDTH     4
346
347#define MMC_CSD_WBPART_SBIT     21
348
349#define MMC_CSD_FFORMGRP_SBIT   15
350
351#define MMC_CSD_FFORMAT_SBIT    10
352#define MMC_CSD_FFORMAT_WIDTH   2
353static void VirtMmcFillRespCsd(struct MmcCmd *cmd, const struct Virtblk *blk)
354{
355    FillCidCsdBits(cmd->resp, MMC_CSD_STRUCT_SBIT, MMC_CSD_STRUCT_WIDTH, MMC_CSD_STRUCTURE_VER_1_2);
356    FillCidCsdBits(cmd->resp, MMC_CSD_VERS_SBIT, MMC_CSD_VERS_WIDTH, MMC_CSD_SPEC_VER_4);
357    FillCidCsdBits(cmd->resp, MMC_CSD_CCC_SBIT, MMC_CSD_CCC_WIDTH, MMC_CSD_CCC_BASIC |
358                                                MMC_CSD_CCC_BLOCK_READ | MMC_CSD_CCC_BLOCK_WRITE);
359    FillCidCsdBits(cmd->resp, MMC_CSD_RBPART_SBIT, 1, 0);       /* READ_BL_PARTIAL: no */
360    FillCidCsdBits(cmd->resp, MMC_CSD_WBMISALIGN_SBIT, 1, 0);   /* WRITE_BLK_MISALIGN: no */
361    FillCidCsdBits(cmd->resp, MMC_CSD_RBMISALIGN_SBIT, 1, 0);   /* READ_BLK_MISALIGN: no */
362    FillCidCsdBits(cmd->resp, MMC_CSD_DSRIMP_SBIT, 1, 0);       /* DSR_IMP: no */
363    if (blk->capacity > CAPACITY_2G) {
364        uint32_t e = U32_BITS - __builtin_clz(blk->blkSize) - 1;
365        FillCidCsdBits(cmd->resp, MMC_CSD_RBLEN_SBIT, MMC_CSD_RBLEN_WIDTH, e);  /* READ_BL_LEN */
366        FillCidCsdBits(cmd->resp, MMC_CSD_WBLEN_SBIT, MMC_CSD_WBLEN_WIDTH, e);  /* WRITE_BL_LEN */
367        FillCidCsdBits(cmd->resp, MMC_CSD_CSIZE_SBIT, MMC_CSD_CSIZE_WIDTH, MMC_CSD_CSIZE_VAL);
368    } else {                                /* ensure c_size can up to 2G (512B can't) */
369        FillCidCsdBits(cmd->resp, MMC_CSD_RBLEN_SBIT, MMC_CSD_RBLEN_WIDTH, READ_BL_LEN);
370        FillCidCsdBits(cmd->resp, MMC_CSD_WBLEN_SBIT, MMC_CSD_WBLEN_WIDTH, READ_BL_LEN);
371        uint32_t size = blk->capacity*MMC_SEC_SIZE / (1<<READ_BL_LEN) / (1<<(C_SIZE_MULT+2)) - 1;
372        FillCidCsdBits(cmd->resp, MMC_CSD_CSIZE_SBIT, MMC_CSD_CSIZE_WIDTH, size);   /* C_SIZE */
373    }
374    FillCidCsdBits(cmd->resp, MMC_CSD_CSIZEMUL_SBIT, MMC_CSD_CSIZEMUL_WIDTH, C_SIZE_MULT);
375    FillCidCsdBits(cmd->resp, MMC_CSD_EGRPSIZE_SBIT, MMC_CSD_EGRPSIZE_WIDTH, MMC_CSD_EGRPSIZE_VAL);
376    FillCidCsdBits(cmd->resp, MMC_CSD_EGRPMULT_SBIT, MMC_CSD_EGRPMULT_WIDTH, MMC_CSD_EGRPMULT_VAL);
377    FillCidCsdBits(cmd->resp, MMC_CSD_WBPART_SBIT, 1, 0);   /* WRITE_BL_PARTIAL: no */
378    FillCidCsdBits(cmd->resp, MMC_CSD_FFORMGRP_SBIT, 1, 0); /* FILE_FORMAT_GRP */
379    FillCidCsdBits(cmd->resp, MMC_CSD_FFORMAT_SBIT, MMC_CSD_FFORMAT_WIDTH, 0);  /* hard disk-like */
380    /* leave other fields random */
381}
382
383#define EMMC_EXT_CSD_CMD_SET_REV    189
384#define EMMC_EXT_CSD_CMD_SET        191
385#define EMMC_EXT_CSD_ACC_SIZE       225
386#define EMMC_EXT_CSD_S_CMD_SET      504
387static void VirtMmcFillDataExtCsd(const struct MmcCmd *cmd, const struct Virtblk *blk)
388{
389    uint8_t *b = (uint8_t *)cmd->data->dataBuffer;
390
391    b[EMMC_EXT_CSD_S_CMD_SET] = 0;      /* standard MMC */
392    b[EMMC_EXT_CSD_ACC_SIZE] = blk->blkSize / MMC_SEC_SIZE;
393    b[EMMC_EXT_CSD_REL_WR_SEC_C] = blk->blkSize / MMC_SEC_SIZE;
394    *(uint32_t*)&b[EMMC_EXT_CSD_SEC_CNT] = blk->capacity;
395    b[EMMC_EXT_CSD_CARD_TYPE] = EMMC_EXT_CSD_CARD_TYPE_26 | EMMC_EXT_CSD_CARD_TYPE_52;
396    b[EMMC_EXT_CSD_STRUCTURE] = EMMC_EXT_CSD_STRUCTURE_VER_1_2;
397    b[EMMC_EXT_CSD_REV] = EMMC_EXT_CSD_REV_1_3;
398    b[EMMC_EXT_CSD_CMD_SET] = 0;        /* standard MMC */
399    b[EMMC_EXT_CSD_CMD_SET_REV] = 0;    /* v4.0 */
400    b[EMMC_EXT_CSD_BUS_WIDTH] = EMMC_EXT_CSD_BUS_WIDTH_1;
401    /* leave other fields random */
402}
403
404#define MMC_RESP_STATE_BIT  9
405static inline void VirtMmcFillRespR1(struct MmcCmd *cmd)
406{
407    cmd->resp[0] = READY_FOR_DATA | (STATE_READY << MMC_RESP_STATE_BIT);
408    cmd->resp[1] = cmd->cmdCode;
409}
410
411static int32_t VirtMmcIO(const struct MmcCntlr *cntlr, const struct MmcCmd *cmd)
412{
413    struct Virtblk *blk = cntlr->priv;
414    uint64_t startSector = (uint64_t)cmd->argument;
415    uint32_t io, ret;
416
417    if (cntlr->curDev->state.bits.blockAddr == 0) {
418        startSector >>= MMC_SEC_SHIFT;
419    }
420
421    if (cmd->data->dataFlags == DATA_READ) {
422        io = VIRTIO_BLK_T_IN;
423    } else {
424        io = VIRTIO_BLK_T_OUT;
425    }
426
427    ret = VirtblkIO(blk, io, startSector, cmd->data->dataBuffer, cmd->data->blockNum);
428    if (ret == VIRTIO_BLK_S_OK) {
429        cmd->data->returnError = HDF_SUCCESS;
430    } else {
431        HDF_LOGE("[%s]QEMU backend I/O error", __func__);
432        cmd->data->returnError = HDF_ERR_IO;
433    }
434
435    return HDF_SUCCESS;
436}
437
438static int32_t VirtMmcDoRequest(struct MmcCntlr *cntlr, struct MmcCmd *cmd)
439{
440    if ((cntlr == NULL) || (cntlr->priv == NULL) || (cmd == NULL)) {
441        return HDF_ERR_INVALID_OBJECT;
442    }
443    struct Virtblk *blk = cntlr->priv;
444
445    cmd->returnError = HDF_SUCCESS;
446    switch (cmd->cmdCode) {
447        case GO_IDLE_STATE:         // CMD0
448            break;
449        case SEND_OP_COND:          // CMD1
450            if (blk->capacity > CAPACITY_2G) {
451                cmd->resp[0] = OCR_GT_2G;
452            } else {
453                cmd->resp[0] = OCR_LE_2G;
454            }
455            break;
456        case ALL_SEND_CID:          // CMD2, fall through
457        case SEND_CID:              // CMD10
458            VirtMmcFillRespCid(cmd, blk);
459            break;
460        case SEND_EXT_CSD:          // CMD8, fall through
461            VirtMmcFillDataExtCsd(cmd, blk);
462            cmd->data->returnError = HDF_SUCCESS;
463        case SET_RELATIVE_ADDR:     // CMD3, fall through
464        case SWITCH:                // CMD6, fall through
465        case SELECT_CARD:           // CMD7, fall through
466        case SEND_STATUS:           // CMD13
467            VirtMmcFillRespR1(cmd);
468            break;
469        case SEND_CSD:              // CMD9
470            VirtMmcFillRespCsd(cmd, blk);
471            break;
472        case READ_SINGLE_BLOCK:     // CMD17, fall through
473        case READ_MULTIPLE_BLOCK:   // CMD18, fall through
474        case WRITE_BLOCK:           // CMD24, fall through
475        case WRITE_MULTIPLE_BLOCK:  // CMD25
476            return VirtMmcIO(cntlr, cmd);
477        default:
478            HDF_LOGE("[%s]unsupported command: %u", __func__, cmd->cmdCode);
479            cmd->returnError = HDF_ERR_NOT_SUPPORT;
480    }
481    return cmd->returnError;
482}
483
484static bool VirtMmcPlugged(struct MmcCntlr *cntlr)
485{
486    (void)cntlr;
487    return true;
488}
489
490static bool VirtMmcBusy(struct MmcCntlr *cntlr)
491{
492    (void)cntlr;
493    return false;
494}
495
496static struct MmcCntlrOps g_virtblkOps = {
497    .request = VirtMmcDoRequest,
498    .devPlugged = VirtMmcPlugged,
499    .devBusy = VirtMmcBusy,
500};
501
502
503/*
504 * HDF entry
505 */
506
507static void HdfVirtblkRelease(struct HdfDeviceObject *deviceObject)
508{
509    struct MmcCntlr *cntlr = deviceObject->priv;
510    struct Virtblk *blk = cntlr->priv;
511
512    if (blk) {
513        VirtblkDeInit(blk);
514    }
515    if (cntlr->curDev != NULL) {
516        MmcDeviceRemove(cntlr->curDev);
517        OsalMemFree(cntlr->curDev);
518        cntlr->curDev = NULL;
519    }
520    MmcCntlrRemove(cntlr);
521    OsalMemFree(cntlr);
522}
523
524static int32_t HdfVirtblkBind(struct HdfDeviceObject *obj)
525{
526    struct MmcCntlr *cntlr = NULL;
527    struct Virtblk *blk = NULL;
528    int32_t ret;
529
530    if (obj == NULL) {
531        HDF_LOGE("[%s]HdfDeviceObject is NULL", __func__);
532        return HDF_ERR_INVALID_OBJECT;
533    }
534
535    cntlr = OsalMemCalloc(sizeof(struct MmcCntlr));
536    if (cntlr == NULL) {
537        HDF_LOGE("[%s]alloc MmcCntlr memory failed", __func__);
538        return HDF_ERR_MALLOC_FAIL;
539    }
540
541    if ((blk = VirtblkInitDev()) == NULL) {
542        OsalMemFree(cntlr);
543        return HDF_FAILURE;
544    }
545
546    obj->service = &cntlr->service;
547    obj->priv = cntlr;
548    cntlr->priv = blk;
549    cntlr->ops = &g_virtblkOps;
550    cntlr->hdfDevObj = obj;
551    if ((ret = MmcCntlrParse(cntlr, obj)) != HDF_SUCCESS) {
552        goto _ERR;
553    }
554
555    if ((ret = MmcCntlrAdd(cntlr, true)) != HDF_SUCCESS) {
556        goto _ERR;
557    }
558    (void)MmcCntlrAddDetectMsgToQueue(cntlr);
559
560    return HDF_SUCCESS;
561
562_ERR:   /* Bind failure, so we must call release manually. */
563    HdfVirtblkRelease(obj);
564    return ret;
565}
566
567static int32_t HdfVirtblkInit(struct HdfDeviceObject *device)
568{
569    if (device == NULL) {
570        HDF_LOGE("[%s]device is null", __func__);
571        return HDF_ERR_INVALID_PARAM;
572    }
573
574    return HDF_SUCCESS;
575}
576
577struct HdfDriverEntry g_virtBlockEntry = {
578    .moduleVersion = 1,
579    .moduleName = "HDF_VIRTIO_BLOCK",
580    .Bind = HdfVirtblkBind,
581    .Init = HdfVirtblkInit,
582    .Release = HdfVirtblkRelease,
583};
584
585HDF_INIT(g_virtBlockEntry);
586