driver-peripherals-light-des.md 17.4 KB
Newer Older
Z
zhouyanxu 已提交
1 2 3 4 5 6 7
# LIGHT

- [概述](##概述)
  - [功能简介](###功能简介)
  - [运作机制](###运作机制)
  
- [开发指导](##开发指导)
Z
zhouyanxu 已提交
8 9 10 11
  - [接口说明](###接口说明)
  - [开发步骤](###开发步骤)
  - [开发实例](###开发实例)
  - [调测验证](###调测验证)
Z
zhouyanxu 已提交
12 13 14 15 16 17
  

## 概述

### 功能简介

Z
zhouyanxu 已提交
18
​        Light驱动模型为上层Light硬件服务层提供稳定的灯控制能力接口,包括获取灯类型、配置点灯模式、配置灯闪烁效果、点灯、熄灯等。基于HDF(Hardware Driver Foundation)驱动框架开发的Light驱动模型,实现跨操作系统迁移,器件差异配置等功能。实现Light驱动“一次开发,多系统部署”的目标。Light驱动模型如[图1](#Light驱动模型图)所示:
Z
zhouyanxu 已提交
19 20 21

**图 1**  Light驱动模型图

Z
zhouyanxu 已提交
22
![Light驱动模型图](figures/Light%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9E%8B%E5%9B%BE.png)
Z
zhouyanxu 已提交
23 24 25

### 运作机制

Z
zhouyanxu 已提交
26
通过介绍Light驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如[图2](#Lihgt驱动运行图)所示:
Z
zhouyanxu 已提交
27

Z
zhouyanxu 已提交
28
**图 2**  Lihgt驱动运行图
Z
zhouyanxu 已提交
29

Z
zhouyanxu 已提交
30
![Light驱动运行图](figures/Light%E9%A9%B1%E5%8A%A8%E8%BF%90%E8%A1%8C%E5%9B%BE.png)
Z
zhouyanxu 已提交
31

Z
zhouyanxu 已提交
32
Light驱动模型以标准系统Hi3516DV300为例,介绍整个驱动加载及运行流程:
Z
zhouyanxu 已提交
33 34 35 36 37 38

1. 从device info HCS 的Light Host里读取Light设备管理配置信息。
2. 从light_config HCS读取Light数据配置信息。
3. 解析Light设备管理配置信息,并关联对应设备驱动。
4. 客户端下发Light Stub控制到服务端。
5. 服务端调用Light Stub控制。
Z
zhouyanxu 已提交
39
6. 启动Light抽象驱动接口。
Z
zhouyanxu 已提交
40 41 42 43 44

## 开发指导

### 接口说明

Z
zhouyanxu 已提交
45
Light驱动模型支持获取系统中所有灯的信息,动态配置闪烁模式和闪烁时间的能力。Light硬件服务调用GetLightInfo获取Light设备的基本信息;调用TurnOnLight接口启动配置的闪烁效果。Light驱动模型对HDI开放的API接口能力,参考[表1](#Light驱动模型对外API接口能力介绍)
Z
zhouyanxu 已提交
46

Z
zhouyanxu 已提交
47
**表1 **  Light驱动模型对外API接口能力介绍
Z
zhouyanxu 已提交
48 49 50 51 52 53 54 55

| 接口名                                                       | 功能描述                                                     |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| int32_t (*GetLightInfo)(struct LightInfo **lightInfo, uint32_t *count) | 获取系统中所有灯的信息,lightInfo表示灯设备的基本信息,count表示获取灯的个数。 |
| int32_t (*TurnOnLight)(uint32_t type, struct LightEffect *effect) | 根据指定的灯类型打开灯列表中可用的灯,type表示灯类型,effect表示要设置的效果信息。 |
| int32_t (*TurnOffLight)(uint32_t type)                       | 根据指定的灯类型关闭灯列表中可用的灯。                       |

### 开发步骤
Z
zhouyanxu 已提交
56
1.  基于HDF驱动框架,按照驱动Driver Entry程序,完成Light抽象驱动开发(主要由Bind、Init、Release、Dispatch函数接口实现),资源配置及HCS解析。完成Light驱动的设备信息配置。
Z
zhouyanxu 已提交
57
3.  调用配置解析接口,完成器件属性信息解析,器件寄存器解析,并注册到Light设备管理中。
Z
zhouyanxu 已提交
58
3.  完成Light获取类型、闪烁和停止接口开发,会根据闪烁模式创建和销毁定时器。
Z
zhouyanxu 已提交
59 60 61

### 开发实例

Z
zhouyanxu 已提交
62
1. Light驱动的初始化和去初始化
Z
zhouyanxu 已提交
63

Z
zhouyanxu 已提交
64
   - 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
Z
zhouyanxu 已提交
65
     Light驱动模型使用HCS作为配置描述源码,HCS配置字段详细介绍请参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)
Z
zhouyanxu 已提交
66 67
     其Driver Entry入口函数定义如下:
     
Z
zhouyanxu 已提交
68
     ```c
Z
zhouyanxu 已提交
69
     /* 注册灯入口数据结构体对象 */
Z
zhouyanxu 已提交
70
     struct HdfDriverEntry g_lightDriverEntry = {
Z
zhouyanxu 已提交
71 72 73 74 75
         .moduleVersion = 1, // 灯模块版本号
         .moduleName = "HDF_LIGHT", // 灯模块名,要与device_info.hcs文件里灯moduleName字段值一样
         .Bind = BindLightDriver, // 灯绑定函数
         .Init = InitLightDriver, // 灯初始化函数
         .Release = ReleaseLightDriver, // 灯资源释放函数
Z
zhouyanxu 已提交
76
     };
Z
zhouyanxu 已提交
77
     /* 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release函数释放驱动资源并退出 */
Z
zhouyanxu 已提交
78 79
     HDF_INIT(g_lightDriverEntry);
     ```
Z
zhouyanxu 已提交
80 81
     
   - 基于HDF驱动框架,按照驱动Driver Entry程序,完成Light抽象驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现。
Z
zhouyanxu 已提交
82 83

     ```c
Z
zhouyanxu 已提交
84
     /* Light驱动对外发布的能力 */
Z
zhouyanxu 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
     static int32_t DispatchLight(struct HdfDeviceIoClient *client,
         int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
     {
         .....
         if (cmd == LIGHT_IO_CMD_GET_INFO_LIST) {
             CHECK_LIGHT_NULL_PTR_RETURN_VALUE(reply, HDF_ERR_INVALID_PARAM);
             return GetAllLightInfo(data, reply);
         }
     
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM);
         (void)OsalMutexLock(&drvData->mutex);
         if (!HdfSbufReadInt32(data, &lightType)) {
             HDF_LOGE("%s: sbuf read lightType failed", __func__);
             (void)OsalMutexUnlock(&drvData->mutex);
             return HDF_ERR_INVALID_PARAM;
         }
         .....
         ret = DispatchCmdHandle(lightType, data, reply);
         (void)OsalMutexUnlock(&drvData->mutex);
         return ret;
     }
     
Z
zhouyanxu 已提交
107
     /* Light驱动对外提供的服务绑定到HDF框架 */
Z
zhouyanxu 已提交
108 109 110 111 112
     int32_t BindLightDriver(struct HdfDeviceObject *device)
     {
         struct LightDriverData *drvData = NULL;
     
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE);
Z
zhouyanxu 已提交
113
         /* 私有接口分配资源 */
Z
zhouyanxu 已提交
114 115
         drvData = (struct LightDriverData *)OsalMemCalloc(sizeof(*drvData));
         CHECK_LIGHT_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_MALLOC_FAIL);
Z
zhouyanxu 已提交
116
         /* 需要发布的接口函数 */
Z
zhouyanxu 已提交
117 118 119 120 121 122 123
         drvData->ioService.Dispatch = DispatchLight;
         drvData->device = device;
         device->service = &drvData->ioService;
         g_lightDrvData = drvData;
         return HDF_SUCCESS;
     }
     
Z
zhouyanxu 已提交
124
     /* Light驱动初始化入口函数*/
Z
zhouyanxu 已提交
125 126 127
     int32_t InitLightDriver(struct HdfDeviceObject *device)
     { 
     	.....
Z
zhouyanxu 已提交
128
         /* 工作队列初始化 */
Z
zhouyanxu 已提交
129 130 131 132
         if (HdfWorkQueueInit(&drvData->workQueue, LIGHT_WORK_QUEUE_NAME) != HDF_SUCCESS) {
             HDF_LOGE("%s: init workQueue fail!", __func__);
             return HDF_FAILURE;
         }
Z
zhouyanxu 已提交
133
         /* 工作项初始化 */
Z
zhouyanxu 已提交
134 135 136 137
         if (HdfWorkInit(&drvData->work, LightWorkEntry, (void*)drvData) != HDF_SUCCESS) {
             HDF_LOGE("%s: init workQueue fail!", __func__);
             return HDF_FAILURE;
         }
Z
zhouyanxu 已提交
138
         /* 解析HCS配置文件 */
Z
zhouyanxu 已提交
139 140 141 142 143 144 145 146
         if (GetLightConfigData(device->property) != HDF_SUCCESS) {
             HDF_LOGE("%s: get light config fail!", __func__);
             return HDF_FAILURE;
         }
     
         return HDF_SUCCESS;
     }
     
Z
zhouyanxu 已提交
147
     /* 释放Light驱动初始化时分配的资源 */
Z
zhouyanxu 已提交
148 149 150
     void ReleaseLightDriver(struct HdfDeviceObject *device)
     { 
         .....
Z
zhouyanxu 已提交
151
         /* 释放已分配资源 */
Z
zhouyanxu 已提交
152 153 154 155 156 157 158
         for (i = LIGHT_TYPE_NONE; i < LIGHT_TYPE_BUTT; ++i) {
     
             if (drvData->info[i] != NULL) {
                 OsalMemFree(drvData->info[i]);
                 drvData->info[i] = NULL;
             }
         }
Z
zhouyanxu 已提交
159
         /* 销毁工作队列资源 */
Z
zhouyanxu 已提交
160 161 162 163 164 165 166 167 168
         HdfWorkDestroy(&drvData->work);
         HdfWorkQueueDestroy(&drvData->workQueue);
         (void)OsalMutexDestroy(&drvData->mutex);
         (void)OsalMemFree(drvData);
         g_lightDrvData = NULL;
     }
     
     ```

Z
zhouyanxu 已提交
169
   - Light设备管理模块负责系统中Light器件接口发布,在系统启动过程中,HDF框架机制通过灯Host里设备HCS配置信息,加载设备管理驱动。
Z
zhouyanxu 已提交
170 171 172 173 174

     ```hcs
     /* 灯设备HCS配置 */
     device_light :: device {
         device0 :: deviceNode {
Z
zhouyanxu 已提交
175 176 177 178 179 180 181
             policy = 2; // 驱动服务发布的策略
             priority = 100; // Light驱动启动优先级(0-200),值越大优先级越低,建议配置为100,优先级相同则不保证device的加载顺序
             preload = 0; // 驱动按需加载字段,0表示加载,2表示不加载
             permission = 0664;  // 驱动创建设备节点权限
             moduleName = "HDF_LIGHT"; // Light驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
             serviceName = "hdf_light"; // Light驱动对外发布服务的名称,必须唯一
             deviceMatchAttr = "hdf_light_driver"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
Z
zhouyanxu 已提交
182 183 184 185
         }
     }
     ```

Z
zhouyanxu 已提交
186
2. 分配资源,解析灯HCS配置。
Z
zhouyanxu 已提交
187

Z
zhouyanxu 已提交
188
   - 解析HCS配置文件。
Z
zhouyanxu 已提交
189 190 191 192 193 194

     ```c
     /* 分配资源,解析灯HCS配置 */
     static int32_t ParseLightInfo(const struct DeviceResourceNode *node)
     {
         .....
Z
zhouyanxu 已提交
195
         /* 从HCS获取支持灯的类型个数 */
Z
zhouyanxu 已提交
196 197 198
         drvData->lightNum = parser->GetElemNum(light, "lightType");
         ....
         for (i = 0; i < drvData->lightNum; ++i) {
Z
zhouyanxu 已提交
199
         /* 获取类型 */
Z
zhouyanxu 已提交
200 201 202 203 204 205
         ret = parser->GetUint32ArrayElem(light, "lightType", i, &temp, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "lightType");
         }
     
         for (i = 0; i < drvData->lightNum; ++i) {
         .....
Z
zhouyanxu 已提交
206
         /* 类型作为下标开辟空间 */
Z
zhouyanxu 已提交
207 208
         drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo));
         .....
Z
zhouyanxu 已提交
209
         /* 将Light设备信息进行填充 */
Z
zhouyanxu 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
         ret = parser->GetUint32(light, "busRNum", &drvData->info[temp]->busRNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busRNum");
         ret = parser->GetUint32(light, "busGNum", &drvData->info[temp]->busGNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busGNum");
         ret = parser->GetUint32(light, "busBNum", &drvData->info[temp]->busBNum, 0);
         CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "busBNum");
         .....
         return HDF_SUCCESS;
     
     }
     ```

   - 灯效果模型使用HCS作为配置描述源码,HCS配置字段详细介绍参考[配置管理](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md)介绍。

     ```hcs
Z
zhouyanxu 已提交
225
     /* 灯数据配置模板(light_config.hcs) */
Z
zhouyanxu 已提交
226 227 228 229 230 231
     root {
         lightConfig {
             boardConfig {
                 match_attr = "hdf_light_driver";
                 lightAttr {
                     light01 {
Z
zhouyanxu 已提交
232 233 234 235 236 237 238
                         lightType = [1, 2];          // 灯类型
                         busRNum = 31;                // 红色对应的GPIO管脚
                         busGNum = 30;                // 绿色对应的GPIO管脚
                         busBNum = 29;                // 蓝色对应的GPIO管脚
                         lightBrightness = 0X80000000;// RGB: R:16-31bit、G:8-15bit、B:0-7bit
                         onTime = 50;                 // 一个闪烁周期内亮灯时长
                         offTime = 50;                // 一个闪烁周期内熄灯时长
Z
zhouyanxu 已提交
239 240 241 242 243 244 245 246 247 248
                     }
                 }
             }
         }
     }
     ```

3. 完成获取灯类型,闪烁和停止接口开发,会根据闪烁模式创建和销毁定时器。

   ```c
Z
zhouyanxu 已提交
249
   /* Light驱动服务调用GetAllLightInfo获取灯类型,Enable接口启动闪烁模式,
Z
zhouyanxu 已提交
250 251 252 253
      调用Disable接口停止闪烁 */
   static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       .....
Z
zhouyanxu 已提交
254
       /* 获取Light类型个数 */
Z
zhouyanxu 已提交
255 256 257 258 259 260 261 262 263 264
       if (!HdfSbufWriteUint32(reply, drvData->lightNum)) {
           HDF_LOGE("%s: write sbuf failed", __func__);
           return HDF_FAILURE;
       }
       for (i = 0; i < LIGHT_TYPE_BUTT; ++i) {
           if (drvData->info[i] == NULL) {
               continue;
           }
           lightInfo.lightType = i;
           lightInfo.reserved = NULL;
Z
zhouyanxu 已提交
265
           /* 将Light设备信息填充进reply */
Z
zhouyanxu 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278
           if (!HdfSbufWriteBuffer(reply, &lightInfo, sizeof(lightInfo))) {
               HDF_LOGE("%s: write sbuf failed", __func__);
               return HDF_FAILURE;
           }
       }
   
       return HDF_SUCCESS;
   }
   
   /* 按照指定的类型和用户传入的参数使能灯 */
   static int32_t Enable(uint32_t lightType, struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       .....
Z
zhouyanxu 已提交
279
       /* 根据用户传的亮度值设置灯的颜色  RGB: R:16-31bit、G:8-15bit、B:0-7bit */
Z
zhouyanxu 已提交
280 281 282 283 284 285 286
       if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_R_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busRNum;
       } else if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_G_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busGNum;
       } else if ((drvData->info[lightType]->lightBrightness & LIGHT_MAKE_B_BIT) != 0) {
           drvData->info[lightType]->busNum = drvData->info[lightType]->busBNum;
       }
Z
zhouyanxu 已提交
287
       /* 常亮模式 */
Z
zhouyanxu 已提交
288 289 290 291 292 293
       if (buf->flashEffect.flashMode == LIGHT_FLASH_NONE) {
   
           if (GpioWrite(drvData->info[lightType]->busNum, GPIO_VAL_HIGH) != HDF_SUCCESS) {
               return HDF_FAILURE;
           }
       }
Z
zhouyanxu 已提交
294
       /* 闪烁模式 */
Z
zhouyanxu 已提交
295 296
       if (buf->flashEffect.flashMode == LIGHT_FLASH_TIMED) {
           drvData->info[lightType]->lightState = LIGHT_STATE_START;
Z
zhouyanxu 已提交
297
           /* 用户设置的闪烁时间小于系统支持的最短时间,采用系统配置的时间(HCS配置) */
Z
zhouyanxu 已提交
298 299 300 301
           drvData->info[lightType]->onTime = buf->flashEffect.onTime < drvData->info[lightType]->onTime ?
           drvData->info[lightType]->onTime : buf->flashEffect.onTime;
           drvData->info[lightType]->offTime = buf->flashEffect.offTime < drvData->info[lightType]->offTime ?
           drvData->info[lightType]->offTime : buf->flashEffect.offTime;
Z
zhouyanxu 已提交
302
           /* 创建定时器 */
Z
zhouyanxu 已提交
303 304 305 306 307
           if (OsalTimerCreate(&drvData->timer, drvData->info[lightType]->onTime,
               LightTimerEntry, (uintptr_t)lightType) != HDF_SUCCESS) {
           HDF_LOGE("%s: create light timer fail!", __func__);
           return HDF_FAILURE;
           }
Z
zhouyanxu 已提交
308
           /* 启动周期定时器 */
Z
zhouyanxu 已提交
309 310 311 312 313 314 315 316 317 318 319
           if (OsalTimerStartLoop(&drvData->timer) != HDF_SUCCESS) {
           HDF_LOGE("%s: start light timer fail!", __func__);
           return HDF_FAILURE;
           }
       }
       return HDF_SUCCESS;
   }
   
   /* 按照指定的类型关闭灯 */
   static int32_t Disable(uint32_t lightType, struct HdfSBuf *data, struct HdfSBuf *reply)
   {
Z
zhouyanxu 已提交
320
       /* 删除定时器 */
Z
zhouyanxu 已提交
321 322 323 324 325 326
       if (drvData->timer.realTimer != NULL) {
   
           if (OsalTimerDelete(&drvData->timer) != HDF_SUCCESS) {
               HDF_LOGE("%s: delete haptic timer fail!", __func__);
           }
       }
Z
zhouyanxu 已提交
327
       /* 对应的GPIO下电 */
Z
zhouyanxu 已提交
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
       if (GpioWrite(drvData->info[lightType]->busRNum, GPIO_VAL_LOW) != HDF_SUCCESS){
           HDF_LOGE("%s: gpio write failed", __func__);
           return HDF_FAILURE;
       }
   
       return HDF_SUCCESS;
   }
   ```

### 调测验证

驱动开发完成后,在灯单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。

```c++
/* 用例执行前,初始化灯接口实例 */
void HdfLightTest::SetUpTestCase()
{
    g_lightDev = NewLightInterfaceInstance();
    if (g_lightDev == nullptr) {
        printf("test light get Module instance failed\n\r");
    }
    int32_t ret = g_lightDev->GetLightInfo(&g_lightInfo, &g_count);
    if (ret == -1) {
        printf("get light informations failed\n\r");
    }
}

/* 用例执行后,释放用例资源 */
void HdfLightTest::TearDownTestCase()
{
    if(g_lightDev != nullptr){
        FreeLightInterfaceInstance();
        g_lightDev = nullptr;
    }
}

/* 测试灯获取类型 */
HWTEST_F(HdfLightTest, GetLightList001, TestSize.Level1)
{
    struct LightInfo *info = nullptr;

    if (g_lightInfo == nullptr) {
        EXPECT_NE(nullptr, g_lightInfo);
        return;
    }

    printf("get light list num[%d]\n\r", g_count);
    info = g_lightInfo;

    for (int i = 0; i < g_count; ++i) {
        printf("get lightId[%d]\n\r", info->lightType);
        EXPECT_GE(info->lightType, g_minLightType);
        EXPECT_LE(info->lightType, g_maxLightType);
        info++;
    }
}

/* 测试灯常亮模式 */
HWTEST_F(HdfLightTest, EnableLight001, TestSize.Level1)
{
    int32_t i;
    int32_t ret;
    struct LightEffect effect;
    effect->lightBrightness = 0x80000000;
    effect->flashEffect.flashMode = LIGHT_FLASH_NONE;
    effect->flashEffect.onTime = 0;
    effect->flashEffect.offTime = 0;

    for (i = 0; i < g_count; ++i) {

        ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightType, effect);
        EXPECT_EQ(0, ret);

        OsalSleep(LIGHT_WAIT_TIME);

        ret = g_lightDev->TurnOffLight(type);
        EXPECT_EQ(0, ret);
    }
}

/* 测试灯闪烁模式 */
HWTEST_F(HdfLightTest, EnableLight002, TestSize.Level1)
{
    int32_t i;
    int32_t ret;
    struct LightEffect effect;
    effect->lightBrightness = 0x80000000;
    effect->flashEffect.flashMode = LIGHT_FLASH_TIMED;
    effect->flashEffect.onTime = g_onTime;
    effect->flashEffect.offTime = g_offTime;

    for (i = 0; i < g_count; ++i) {

        ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightType, effect);
        EXPECT_EQ(0, ret);

        OsalSleep(LIGHT_WAIT_TIME);

        ret = g_lightDev->TurnOffLight(type);
        EXPECT_EQ(0, ret);
    }
}
```