1# SDIO 2 3## 概述 4 5### 功能简介 6 7SDIO是安全数字输入输出接口(Secure Digital Input and Output)的缩写,是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD卡,并且可以连接支持SDIO接口的其他设备。 8 9SDIO接口定义了操作SDIO的通用方法集合,包括: 10- 打开/关闭SDIO控制器 11- 独占/释放HOST 12- 使能/去使能设备 13- 申请/释放中断 14- 读写、获取/设置公共信息 15 16### 运作机制 17 18在HDF框架中,SDIO的接口适配模式采用独立服务模式。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。 19 20SDIO总线有两端,其中一端是主机端(HOST),另一端是设备端(DEVICE)。所有的通信都是由HOST端发出命令开始的,在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了。SDIO的HOST可以连接多个DEVICE,如下图所示: 21- CLK信号:HOST给DEVICE的时钟信号。 22- VDD信号:电源信号。 23- VSS信号:Ground信号。 24- D0-3信号:4条数据线,其中,DAT1信号线复用为中断线,在1BIT模式下DAT0用来传输数据,在4BIT模式下DAT0-DAT3用来传输数据。 25- CMD信号:用于HOST发送命令和DEVICE回复响应。 26 27**图1** SDIO的HOST-DEVICE连接示意图 28 29![image](figures/SDIO的HOST-DEVICE连接示意图.png "SDIO的HOST-DEVICE连接示意图") 30 31### 约束与限制 32 33SDIO模块API当前仅支持内核态调用。 34 35## 使用指导 36 37### 场景介绍 38 39SDIO的应用比较广泛,目前,有许多手机都支持SDIO功能,并且很多SDIO外设也被开发出来,使得手机外接外设更加容易。常见的SDIO外设有WLAN、GPS、CAMERA、蓝牙等。 40 41### 接口说明 42 43SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/sdio_if.h。 44 45**表1** SDIO驱动API接口功能介绍 46 47| 接口名 | 接口描述 | 48| -------- | -------- | 49| DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig \*config) | 打开指定总线号的SDIO控制器 | 50| void SdioClose(DevHandle handle) | 关闭SDIO控制器 | 51| int32_t SdioReadBytes(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size) | 从指定地址开始,增量读取指定长度的数据 | 52| int32_t SdioWriteBytes(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size) | 从指定地址开始,增量写入指定长度的数据 | 53| int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size, uint32_t scatterLen) | 从固定地址读取指定长度的数据 | 54| int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size, uint32_t scatterLen) | 向固定地址写入指定长度的数据 | 55| int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size) | 从SDIO function 0的指定地址空间读取指定长度的数据 | 56| int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t \*data, uint32_t addr, uint32_t size) | 向SDIO function 0的指定地址空间写入指定长度的数据 | 57| int32_t SdioSetBlockSize(DevHandle handle, uint32_t blockSize) | 设置块的大小 | 58| int32_t SdioGetCommonInfo(DevHandle handle, SdioCommonInfo \*info, SdioCommonInfoType infoType) | 获取公共信息 | 59| int32_t SdioSetCommonInfo(DevHandle handle, SdioCommonInfo \*info, SdioCommonInfoType infoType) | 设置公共信息 | 60| int32_t SdioFlushData(DevHandle handle) | 刷新数据 | 61| void SdioClaimHost(DevHandle handle) | 独占Host | 62| void SdioReleaseHost(DevHandle handle) | 释放Host | 63| int32_t SdioEnableFunc(DevHandle handle) | 使能SDIO功能设备 | 64| int32_t SdioDisableFunc(DevHandle handle) | 去使能SDIO功能设备 | 65| int32_t SdioClaimIrq(DevHandle handle, SdioIrqHandler \*irqHandler) | 申请中断 | 66| int32_t SdioReleaseIrq(DevHandle handle) | 释放中断 | 67 68### 使用流程 69 70使用SDIO的一般流程如下图所示。 71 72 **图2** SDIO使用流程图 73 74 ![image](figures/SDIO使用流程图.png "SDIO使用流程图") 75 76#### 打开SDIO控制器 77 78在使用SDIO进行通信前,首先要调用SdioOpen获取SDIO控制器的设备句柄,该函数会返回指定总线号的SDIO控制器的设备句柄。 79 80```c 81DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig *config); 82``` 83 84 **表2** SdioOpen函数的参数和返回值描述 85 86| 参数 | 参数描述 | 87| -------- | -------- | 88| mmcBusNum | 总线号 | 89| config | SDIO功能配置信息 | 90| **返回值** | **返回值描述** | 91| NULL | 获取SDIO控制器的设备句柄失败 | 92| 设备句柄 | SDIO控制器的设备句柄 | 93 94 打开SDIO控制器的示例如下: 95 96```c 97DevHandle handle = NULL; 98struct SdioFunctionConfig config; 99config.funcNr = 1; 100config.vendorId = 0x123; 101config.deviceId = 0x456; 102/* 打开总线号为1的SDIO控制器 */ 103handle = SdioOpen(1, &config); 104if (handle == NULL) { 105 HDF_LOGE("SdioOpen: failed!\n"); 106} 107``` 108 109#### 独占HOST 110 111获取到SDIO控制器的设备句柄之后,需要先独占HOST才能进行SDIO后续的一系列操作,独占HOST函数如下所示: 112 113```c 114void SdioClaimHost(DevHandle handle); 115``` 116 117 **表3** SdioClaimHost函数的参数描述 118 119| 参数 | 参数描述 | 120| -------- | -------- | 121| handle | SDIO控制器的设备句柄 | 122 123独占HOST示例如下: 124 125```c 126SdioClaimHost(handle); /* 独占HOST */ 127``` 128 129#### 使能SDIO设备 130 131在访问寄存器之前,需要先使能SDIO设备,使能SDIO设备的函数如下所示: 132 133```c 134int32_t SdioEnableFunc(DevHandle handle); 135``` 136 137 **表4** SdioEnableFunc函数的参数和返回值描述 138 139| 参数 | 参数描述 | 140| -------- | -------- | 141| handle | SDIO控制器的设备句柄 | 142| **返回值** | **返回值描述** | 143| 0 | SDIO使能成功 | 144| 负数 | SDIO使能失败 | 145 146使能SDIO设备的示例如下: 147 148```c 149int32_t ret; 150/* 使能SDIO设备 */ 151ret = SdioEnableFunc(handle); 152if (ret != 0) { 153 HDF_LOGE("SdioEnableFunc: failed, ret %d\n", ret); 154} 155``` 156 157#### 注册SDIO中断 158 159在通信之前,还需要注册SDIO中断,注册SDIO中断函数如下图所示: 160 161```c 162int32_t SdioClaimIrq(DevHandle handle, SdioIrqHandler *handler); 163``` 164 165 **表5** SdioClaimIrq函数的参数和返回值描述 166 167| 参数 | 参数描述 | 168| -------- | -------- | 169| handle | SDIO控制器的设备句柄 | 170| handler | 中断服务函数指针 | 171| **返回值** | **返回值描述** | 172| 0 | 注册中断成功 | 173| 负数 | 注册中断失败 | 174 175 注册SDIO中的示例如下: 176 177```c 178/* 中断服务函数需要根据各自平台的情况去实现 */ 179static void SdioIrqFunc(void *data) 180{ 181 if (data == NULL) { 182 HDF_LOGE("SdioIrqFunc: data is NULL.\n"); 183 return; 184 } 185 /* 需要开发者自行添加具体实现 */ 186} 187 188int32_t ret; 189/* 注册SDIO中断 */ 190ret = SdioClaimIrq(handle, SdioIrqFunc); 191if (ret != 0) { 192 HDF_LOGE("SdioClaimIrq: failed, ret %d\n", ret); 193} 194``` 195 196#### 进行SDIO通信 197 198- 向SDIO设备增量写入指定长度的数据 199 200 对应的接口函数如下所示: 201 202 ```c 203 int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); 204 ``` 205 206 **表6** SdioWriteBytes函数的参数和返回值描述 207 208 | 参数 | 参数描述 | 209 | -------- | -------- | 210 | handle | SDIO控制器的设备句柄 | 211 | data | 待写入数据的指针 | 212 | addr | 待写入数据的起始地址 | 213 | size | 待写入数据的长度 | 214 | **返回值** | **返回值描述** | 215 | 0 | SDIO写数据成功 | 216 | 负数 | SDIO写数据失败 | 217 218 向SDIO设备增量写入指定长度的数据的示例如下: 219 220 ```c 221 int32_t ret; 222 uint8_t wbuff[] = {1,2,3,4,5}; 223 uint32_t addr = 0x100 + 0x09; 224 /* 向SDIO设备起始地址0x109,增量写入5个字节的数据 */ 225 ret = SdioWriteBytes(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0])); 226 if (ret != 0) { 227 HDF_LOGE("SdioWriteBytes: failed, ret %d\n", ret); 228 } 229 ``` 230 231- 从SDIO设备增量读取指定长度的数据 232 233 对应的接口函数如下所示: 234 235 ```c 236 int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); 237 ``` 238 239 **表7** SdioReadBytes函数的参数和返回值描述 240 241 | 参数 | 参数描述 | 242 | -------- | -------- | 243 | handle | SDIO控制器的设备句柄 | 244 | data | 接收读取数据的指针 | 245 | addr | 待读取数据的起始地址 | 246 | size | 待读取数据的长度 | 247 | **返回值** | **返回值描述** | 248 | 0 | SDIO读数据成功 | 249 | 负数 | SDIO读数据失败 | 250 251 从SDIO设备增量读取指定长度的数据的示例如下: 252 253 ```c 254 int32_t ret; 255 uint8_t rbuff[5] = {0}; 256 uint32_t addr = 0x100 + 0x09; 257 /* 从SDIO设备起始地址0x109,增量读取5个字节的数据 */ 258 ret = SdioReadBytes(handle, rbuff, addr, 5); 259 if (ret != 0) { 260 HDF_LOGE("SdioReadBytes: failed, ret %d\n", ret); 261 } 262 ``` 263 264- 向SDIO设备的固定地址写入指定长度的数据 265 266 对应的接口函数如下所示: 267 268 ```c 269 int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); 270 ``` 271 272 **表8** SdioWriteBytesToFixedAddr函数的参数和返回值描述 273 274 | 参数 | 参数描述 | 275 | -------- | -------- | 276 | handle | SDIO控制器的设备句柄 | 277 | data | 待写入数据的指针 | 278 | addr | 待写入数据的固定地址 | 279 | size | 待写入数据的长度 | 280 | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 | 281 | **返回值** | **返回值描述** | 282 | 0 | SDIO写数据成功 | 283 | 负数 | SDIO写数据失败 | 284 285 向SDIO设备的固定地址写入指定长度的数据的示例如下: 286 287 ```c 288 int32_t ret; 289 uint8_t wbuff[] = {1,2,3,4,5}; 290 uint32_t addr = 0x100 + 0x09; 291 /* 向SDIO设备固定地址0x109写入5个字节的数据 */ 292 ret = SdioWriteBytesToFixedAddr(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]), 0); 293 if (ret != 0) { 294 HDF_LOGE("SdioWriteBytesToFixedAddr: failed, ret %d\n", ret); 295 } 296 ``` 297 298- 从SDIO设备的固定地址读取指定长度的数据 299 300 对应的接口函数如下所示: 301 302 ```c 303 int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); 304 ``` 305 306 **表9** SdioReadBytesFromFixedAddr函数的参数和返回值描述 307 308 | 参数 | 参数描述 | 309 | -------- | -------- | 310 | handle | SDIO控制器的设备句柄 | 311 | data | 接收读取数据的指针 | 312 | addr | 待读取数据的起始地址 | 313 | size | 待读取数据的长度 | 314 | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 | 315 | **返回值** | **返回值描述** | 316 | 0 | SDIO读数据成功 | 317 | 负数 | SDIO读数据失败 | 318 319 从SDIO设备的固定地址读取指定长度的数据的示例如下: 320 321 ```c 322 int32_t ret; 323 uint8_t rbuff[5] = {0}; 324 uint32_t addr = 0x100 + 0x09; 325 /* 从SDIO设备固定地址0x109中读取5个字节的数据 */ 326 ret = SdioReadBytesFromFixedAddr(handle, rbuff, addr, 5, 0); 327 if (ret != 0) { 328 HDF_LOGE("SdioReadBytesFromFixedAddr: failed, ret %d\n", ret); 329 } 330 ``` 331 332- 向SDIO function 0的指定地址空间写入指定长度的数据 333 334 当前只支持写入一个字节的数据,对应的接口函数如下所示: 335 336 ```c 337 int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); 338 ``` 339 340 **表10** SdioWriteBytesToFunc0函数的参数和返回值描述 341 342 | 参数 | 参数描述 | 343 | -------- | -------- | 344 | handle | SDIO控制器的设备句柄 | 345 | data | 待写入数据的指针 | 346 | addr | 待写入数据的起始地址 | 347 | size | 待写入数据的长度 | 348 | **返回值** | **返回值描述** | 349 | 0 | SDIO写数据成功 | 350 | 负数 | SDIO写数据失败 | 351 352 向SDIO function 0的指定地址空间写入指定长度的数据的示例如下: 353 354 ```c 355 int32_t ret; 356 uint8_t wbuff = 1; 357 /* 向SDIO function 0地址0x2中写入1字节的数据 */ 358 ret = SdioWriteBytesToFunc0(handle, &wbuff, 0x2, 1); 359 if (ret != 0) { 360 HDF_LOGE("SdioWriteBytesToFunc0: failed, ret %d\n", ret); 361 } 362 ``` 363 364- 从SDIO function 0的指定地址空间读取指定长度的数据 365 366 当前只支持读取一个字节的数据,对应的接口函数如下所示: 367 368 ```c 369 int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); 370 ``` 371 372 **表11** SdioReadBytesFromFunc0函数的参数和返回值描述 373 374 | 参数 | 参数描述 | 375 | -------- | -------- | 376 | handle | SDIO控制器的设备句柄 | 377 | data | 接收读取数据的指针 | 378 | addr | 待读取数据的起始地址 | 379 | size | 待读取数据的长度 | 380 | **返回值** | **返回值描述** | 381 | 0 | SDIO读数据成功 | 382 | 负数 | SDIO读数据失败 | 383 384 从SDIO function 0的指定地址空间读取指定长度的数据的示例如下: 385 386 ```c 387 int32_t ret; 388 uint8_t rbuff; 389 /* 从SDIO function 0设备地址0x2中读取1字节的数据 */ 390 ret = SdioReadBytesFromFunc0(handle, &rbuff, 0x2, 1); 391 if (ret != 0) { 392 HDF_LOGE("SdioReadBytesFromFunc0: failed, ret %d\n", ret); 393 } 394 ``` 395 396#### 释放SDIO中断 397 398通信完成之后,需要释放SDIO中断,函数如下所示: 399 400int32_t SdioReleaseIrq(DevHandle handle); 401 402 **表12** SdioReleaseIrq函数的参数和返回值描述 403 404| 参数 | 参数描述 | 405| -------- | -------- | 406| handle | SDIO控制器的设备句柄 | 407| **返回值** | **返回值描述** | 408| 0 | 释放SDIO中断成功 | 409| 负数 | 释放SDIO中断失败 | 410 411释放SDIO中断的示例如下: 412 413```c 414int32_t ret; 415/* 释放SDIO中断 */ 416ret = SdioReleaseIrq(handle); 417if (ret != 0) { 418 HDF_LOGE("SdioReleaseIrq: failed, ret %d\n", ret); 419} 420``` 421 422#### 去使能SDIO设备 423 424通信完成之后,还需要去使能SDIO设备,函数如下所示: 425 426int32_t SdioDisableFunc(DevHandle handle); 427 428 **表13** SdioDisableFunc函数的参数和返回值描述 429 430| 参数 | 参数描述 | 431| -------- | -------- | 432| handle | SDIO控制器的设备句柄 | 433| **返回值** | **返回值描述** | 434| 0 | 去使能SDIO设备成功 | 435| 负数 | 去使能SDIO设备失败 | 436 437去使能SDIO设备的示例如下: 438 439```c 440int32_t ret; 441/* 去使能SDIO设备 */ 442ret = SdioDisableFunc(handle); 443if (ret != 0) { 444 HDF_LOGE("SdioDisableFunc: failed, ret %d\n", ret); 445} 446``` 447 448#### 释放HOST 449 450通信完成之后,还需要释放去HOST,函数如下所示: 451 452```c 453void SdioReleaseHost(DevHandle handle); 454``` 455 456 **表14** SdioReleaseHost函数的参数描述 457 458| 参数 | 参数描述 | 459| -------- | -------- | 460| handle | SDIO控制器的设备句柄 | 461 462释放HOST的示例如下: 463 464```c 465SdioReleaseHost(handle); /* 释放HOST */ 466``` 467 468#### 关闭SDIO控制器 469 470SDIO通信完成之后,最后需要关闭SDIO控制器,函数如下所示: 471 472```c 473void SdioClose(DevHandle handle); 474``` 475 476该函数会释放掉申请的资源。 477 478 **表15** SdioClose函数的参数描述 479 480| 参数 | 参数描述 | 481| -------- | -------- | 482| handle | SDIO控制器的设备句柄 | 483 484关闭SDIO控制器的示例如下: 485 486```c 487SdioClose(handle); /* 关闭SDIO控制器 */ 488``` 489 490### 使用实例 491 492本例拟对Hi3516DV300开发板上SDIO设备进行操作。 493 494SDIO设备完整的使用示例如下所示,首先打开总线号为1的SDIO控制器,然后独占HOST、使能设备、注册中断,接着进行SDIO通信(读写等),通信完成之后,释放中断、去使能设备、释放HOST,最后关闭SDIO控制器。 495 496```c 497#include "hdf_log.h" 498#include "sdio_if.h" 499 500#define TEST_FUNC_NUM 1 /* 本测试用例中,使用编号为1的I/O function */ 501#define TEST_FBR_BASE_ADDR 0x100 /* 编号为1的I/O function的FBR基地址 */ 502#define TEST_ADDR_OFFSET 9 /* 本测试用例中,需要读写的寄存器的地址偏移 */ 503#define TEST_DATA_LEN 3 /* 本测试用例中,读写数据的长度 */ 504#define TEST_BLOCKSIZE 2 /* 本测试用例中,数据块的大小,单位字节 */ 505 506/* 中断服务函数,需要根据各自平台的情况去实现 */ 507static void SdioIrqFunc(void *data) 508{ 509 if (data == NULL) { 510 HDF_LOGE("SdioIrqFunc: data is NULL.\n"); 511 return; 512 } 513 /* 需要开发者自行添加具体的实现 */ 514} 515 516void SdioTestSample(void) 517{ 518 int32_t ret; 519 DevHandle handle = NULL; 520 uint8_t data[TEST_DATA_LEN] = {0}; 521 struct SdioFunctionConfig config = {1, 0x123, 0x456}; 522 uint8_t val; 523 uint32_t addr; 524 525 /* 打开总线号为1的SDIO设备 */ 526 handle = SdioOpen(1, &config); 527 if (handle == NULL) { 528 HDF_LOGE("SdioOpen: failed!\n"); 529 return; 530 } 531 /* 独占HOST */ 532 SdioClaimHost(handle); 533 /* 使能SDIO设备 */ 534 ret = SdioEnableFunc(handle); 535 if (ret != 0) { 536 HDF_LOGE("SdioEnableFunc: failed, ret %d\n", ret); 537 goto ENABLE_ERR; 538 } 539 /* 注册中断 */ 540 ret = SdioClaimIrq(handle, SdioIrqFunc); 541 if (ret != 0) { 542 HDF_LOGE("SdioClaimIrq: failed, ret %d\n", ret); 543 goto CLAIM_IRQ_ERR; 544 } 545 /* 设置块大小为2字节 */ 546 ret = SdioSetBlockSize(handle, TEST_BLOCKSIZE); 547 if (ret != 0) { 548 HDF_LOGE("SdioSetBlockSize: failed, ret %d\n", ret); 549 goto COMM_ERR; 550 } 551 /* 从SDIO设备增量地址读取3字节的数据 */ 552 addr = TEST_FBR_BASE_ADDR * TEST_FUNC_NUM + TEST_ADDR_OFFSET; 553 ret = SdioReadBytes(handle, data, addr, TEST_DATA_LEN); 554 if (ret != 0) { 555 HDF_LOGE("SdioReadBytes: failed, ret %d\n", ret); 556 goto COMM_ERR; 557 } 558 /* 向SDIO设备增量地址写入3字节的数据 */ 559 ret = SdioWriteBytes(handle, data, addr, TEST_DATA_LEN); 560 if (ret != 0) { 561 HDF_LOGE("SdioWriteBytes: failed, ret %d\n", ret); 562 goto COMM_ERR; 563 } 564 /* 从SDIO设备读取1字节的数据 */ 565 ret = SdioReadBytes(handle, &val, addr, 1); 566 if (ret != 0) { 567 HDF_LOGE("SdioReadBytes: failed, ret %d\n", ret); 568 goto COMM_ERR; 569 } 570 /* 向SDIO设备写入1字节的数据 */ 571 ret = SdioWriteBytes(handle, &val, addr, 1); 572 if (ret != 0) { 573 HDF_LOGE("SdioWriteBytes: failed, ret %d\n", ret); 574 goto COMM_ERR; 575 } 576 /* 从SDIO设备固定地址读取3字节的数据 */ 577 ret = SdioReadBytesFromFixedAddr(handle, data, addr, TEST_DATA_LEN, 0); 578 if (ret != 0) { 579 HDF_LOGE("SdioReadBytesFromFixedAddr: failed, ret %d\n", ret); 580 goto COMM_ERR; 581 } 582 /* 向SDIO设备固定地址写入1字节的数据 */ 583 ret = SdioWriteBytesToFixedAddr(handle, data, addr, 1, 0); 584 if (ret != 0) { 585 HDF_LOGE("SdioWriteBytesToFixedAddr: failed, ret %d\n", ret); 586 goto COMM_ERR; 587 } 588 /* 从SDIO function 0读取1字节的数据 */ 589 addr = 0x02; 590 ret = SdioReadBytesFromFunc0(handle, &val, addr, 1); 591 if (ret != 0) { 592 HDF_LOGE("SdioReadBytesFromFunc0: failed, ret %d\n", ret); 593 goto COMM_ERR; 594 } 595 /* 向SDIO function 0写入1字节的数据 */ 596 ret = SdioWriteBytesToFunc0(handle, &val, addr, 1); 597 if (ret != 0) { 598 HDF_LOGE("SdioWriteBytesToFunc0: failed, ret %d\n", ret); 599 goto COMM_ERR; 600 } 601COMM_ERR: 602 /* 释放中断 */ 603 ret = SdioReleaseIrq(handle); 604 if (ret != 0) { 605 HDF_LOGE("SdioReleaseIrq: failed, ret %d\n", ret); 606 } 607CLAIM_IRQ_ERR: 608 /* 去使能SDIO设备 */ 609 ret = SdioDisableFunc(handle); 610 if (ret != 0) { 611 HDF_LOGE("SdioDisableFunc: failed, ret %d\n", ret); 612 } 613ENABLE_ERR: 614 /* 释放HOST */ 615 SdioReleaseHost(handle); 616 /* 关闭SDIO设备 */ 617 SdioClose(handle); 618} 619``` 620