1# Light 2 3 4## 概述 5 6### 功能简介 7 8Light驱动模型为上层Light硬件服务层提供稳定的灯控制能力接口,包括获取灯类型、配置点灯模式、配置灯闪烁效果、点灯、熄灯等。基于HDF(Hardware Driver Foundation)驱动框架开发的Light驱动模型,实现跨操作系统迁移,器件差异配置等功能。实现Light驱动“一次开发,多系统部署”的目标。Light驱动模型如图1示: 9 10**图 1** Light驱动模型图 11 12![Light驱动模型图](figures/Light驱动模型图.png) 13 14### 运作机制 15 16通过介绍Light驱动模型的加载以及运行流程,对模型内部关键组件以及关联组件之间的关系进行了划分,整体加载流程如图2所示: 17 18**图 2** Light驱动运行图 19 20![Light驱动运行图](figures/Light驱动运行图.png) 21 22以标准系统Hi3516DV300为例,介绍Light模块驱动加载及运行流程: 23 241. Device Manager从device_info.hcs配置文件中读取Light设备管理配置信息。 252. Device Manager从light_config.hcs配置文件中读取Light数据配置信息。 263. HCS Parser解析Light设备管理配置信息,加载对应的Light Host,并控制Host完成驱动的加载。 274. Light Proxy获取到Light HDI接口服务实例后,通过IPC(Inter-Process Communication)调用到Light Stub。 285. Light Stub主要处理与IPC相关的业务逻辑,完成参数反序列化后调用Light Controller。 296. Light Controller中是HDI接口的真正实现,通过IPC调用Light抽象驱动接口,进一步操作Light硬件设备。 30 31## 开发指导 32 33### 场景介绍 34 35灯设备的控制,在实际生活中比比皆是,例如短信通知时闪灯、手机电量不足时预警、充电时根据充电进度变换灯的颜色等等。这些动作的实现,都需要使用Light驱动模型提供的接口,动态配置点灯模式、配置灯闪烁效果、点灯、熄灯等。 36 37### 接口说明 38 39Light驱动模型支持获取系统中所有灯的信息、动态配置闪烁模式和闪烁时间的能力。Light硬件服务调用GetLightInfo获取Light设备的基本信息,调用TurnOnLight接口启动配置的闪烁效果,调用TurnOffLight接口关闭Light设备。Light驱动模型对外开放的API接口能力,参考表1。 40 41**表1** Light驱动模型对外API接口能力介绍 42 43| 接口名 | 功能描述 | 44| ------------------------------------------------------------ | ------------------------------------------------------------ | 45| int32_t (*GetLightInfo)([out] struct LightInfo **lightInfo, [out] uint32_t *count) | 获取当前系统中所有类型的灯信息,lightInfo表示指向灯信息的二级指针,count表示指向灯数量的指针。 | 46| int32_t (*TurnOnLight)([in] uint32_t lightId, [in] struct LightEffect *effect) | 根据指定的灯类型ID打开列表中的可用灯,lightId表示灯类型ID,effect表示指向灯效果的指针。 | 47| int32_t (*TurnOffLight)([in] uint32_t lightId) | 根据指定的灯类型ID关闭列表中的可用灯。 | 48 49### 开发步骤 501. 基于HDF驱动框架,按照驱动Driver Entry程序,完成Light抽象驱动开发(主要由Bind、Init、Release、Dispatch函数接口实现),资源配置及HCS配置文件解析。 51 52 - 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 53 - Light驱动模型使用HCS配置文件作为配置描述源码。HCS配置字段详细介绍请参考[配置管理](driver-hdf-manage.md)。其Driver Entry入口函数定义如下: 54 55 ```c 56 /* 注册灯入口数据结构体对象 */ 57 struct HdfDriverEntry g_lightDriverEntry = { 58 .moduleVersion = 1, // 灯模块版本号 59 .moduleName = "HDF_LIGHT", // 灯模块名,要与device_info.hcs文件里灯moduleName字段值一样 60 .Bind = BindLightDriver, // 灯绑定函数 61 .Init = InitLightDriver, // 灯初始化函数 62 .Release = ReleaseLightDriver, // 灯资源释放函数 63 }; 64 /* 调用HDF_INIT将驱动入口注册到HDF框架中 */ 65 HDF_INIT(g_lightDriverEntry); 66 ``` 67 68 - 基于HDF驱动框架,按照驱动Driver Entry程序,完成Light抽象驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现。 69 70 ```c 71 /* Light驱动对外发布的能力 */ 72 static int32_t DispatchLight(struct HdfDeviceIoClient *client, 73 int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply) 74 { 75 ..... 76 if (cmd == LIGHT_IO_CMD_GET_INFO_LIST) { 77 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(reply, HDF_ERR_INVALID_PARAM); 78 return GetAllLightInfo(data, reply); 79 } 80 81 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); 82 (void)OsalMutexLock(&drvData->mutex); 83 if (!HdfSbufReadInt32(data, &lightId)) { 84 HDF_LOGE("%s: sbuf read lightId fail", __func__); 85 (void)OsalMutexUnlock(&drvData->mutex); 86 return HDF_ERR_INVALID_PARAM; 87 } 88 ..... 89 ret = DispatchCmdHandle(lightId, data, reply); 90 (void)OsalMutexUnlock(&drvData->mutex); 91 return ret; 92 } 93 94 /* Light驱动对外提供的服务绑定到HDF框架 */ 95 int32_t BindLightDriver(struct HdfDeviceObject *device) 96 { 97 struct LightDriverData *drvData = NULL; 98 99 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE); 100 /* 私有接口分配资源 */ 101 drvData = (struct LightDriverData *)OsalMemCalloc(sizeof(*drvData)); 102 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_MALLOC_FAIL); 103 /* 需要发布的接口函数 */ 104 drvData->ioService.Dispatch = DispatchLight; 105 drvData->device = device; 106 device->service = &drvData->ioService; 107 g_lightDrvData = drvData; 108 return HDF_SUCCESS; 109 } 110 111 /* Light驱动初始化入口函数*/ 112 int32_t InitLightDriver(struct HdfDeviceObject *device) 113 { 114 ..... 115 /* 工作队列初始化 */ 116 if (HdfWorkQueueInit(&drvData->workQueue, LIGHT_WORK_QUEUE_NAME) != HDF_SUCCESS) { 117 HDF_LOGE("%s: init workQueue fail!", __func__); 118 return HDF_FAILURE; 119 } 120 /* 工作项初始化 */ 121 if (HdfWorkInit(&drvData->work, LightWorkEntry, (void*)drvData) != HDF_SUCCESS) { 122 HDF_LOGE("%s: init work fail!", __func__); 123 return HDF_FAILURE; 124 } 125 /* 解析HCS配置文件 */ 126 if (GetLightConfigData(device->property) != HDF_SUCCESS) { 127 HDF_LOGE("%s: get light config fail!", __func__); 128 return HDF_FAILURE; 129 } 130 /* 设置GPIO引脚方向 */ 131 if (SetLightGpioDir(drvData) != HDF_SUCCESS) { 132 HDF_LOGE("%s: set light gpio dir fail!", __func__); 133 return HDF_FAILURE; 134 } 135 136 return HDF_SUCCESS; 137 } 138 139 /* 释放Light驱动初始化时分配的资源 */ 140 void ReleaseLightDriver(struct HdfDeviceObject *device) 141 { 142 ..... 143 /* 释放已分配资源 */ 144 for (i = LIGHT_TYPE_NONE; i < LIGHT_TYPE_BUTT; ++i) { 145 146 if (drvData->info[i] != NULL) { 147 OsalMemFree(drvData->info[i]); 148 drvData->info[i] = NULL; 149 } 150 } 151 /* 销毁工作队列资源 */ 152 HdfWorkDestroy(&drvData->work); 153 HdfWorkQueueDestroy(&drvData->workQueue); 154 (void)OsalMutexDestroy(&drvData->mutex); 155 OsalMemFree(drvData); 156 g_lightDrvData = NULL; 157 } 158 ``` 159 160 - Light设备管理模块负责系统中Light器件接口发布。在系统启动过程中,HDF框架机制通过Light Host里的设备HCS配置信息,加载设备管理驱动。 161 162 ```c 163 /* Light设备HCS配置 */ 164 light :: host { 165 hostName = "light_host"; 166 device_light :: device { 167 device0 :: deviceNode { 168 policy = 2; // 驱动服务发布的策略(0:不提供服务,1:对内核态发布服务;2:对内核态和用户态都发布服务) 169 priority = 100; // Light驱动启动优先级(0-200),值越大优先级越低,建议配置为100,优先级相同则不保证device的加载顺序 170 preload = 0; // 驱动按需加载字段,0:加载;2:不加载 171 permission = 0664; // 驱动创建设备节点权限 172 moduleName = "HDF_LIGHT"; // Light驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 173 serviceName = "hdf_light"; // Light驱动对外发布服务的名称,必须唯一 174 deviceMatchAttr = "hdf_light_driver"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等 175 } 176 } 177 } 178 ``` 179 1802. 调用配置解析接口,完成器件属性信息解析、器件寄存器解析,并注册到Light设备管理中。 181 182 ```c 183 /* 分配资源,解析灯HCS配置。 */ 184 static int32_t ParseLightInfo(const struct DeviceResourceNode *node, const struct DeviceResourceIface *parser) 185 { 186 ..... 187 /* 从HCS配置获取支持的灯类型个数 */ 188 drvData->lightNum = parser->GetElemNum(light, "lightId"); 189 .... 190 for (i = 0; i < drvData->lightNum; ++i) { 191 /* 获取灯的类型 */ 192 ret = parser->GetUint32ArrayElem(light, "lightId", i, &temp, 0); 193 CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "lightId"); 194 } 195 196 for (i = 0; i < drvData->lightNum; ++i) { 197 ..... 198 /* 类型作为下标开辟空间 */ 199 drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo)); 200 ..... 201 /* 将Light设备信息进行填充 */ 202 ret = parser->GetUint32(node, "busRNum", (uint32_t *)&drvData->info[temp]->busRNum, 0); 203 if (ret != HDF_SUCCESS) { 204 /* 如果没有成功获取busNum,代表不支持设置busNum对应颜色的灯。 */ 205 drvData->info[temp]->busRNum = LIGHT_INVALID_GPIO; 206 } 207 ret = parser->GetUint32(node, "busGNum", (uint32_t *)&drvData->info[temp]->busGNum, 0); 208 if (ret != HDF_SUCCESS) { 209 drvData->info[temp]->busGNum = LIGHT_INVALID_GPIO; 210 } 211 ret = parser->GetUint32(node, "busBNum", (uint32_t *)&drvData->info[temp]->busBNum, 0); 212 if (ret != HDF_SUCCESS) { 213 drvData->info[temp]->busBNum = LIGHT_INVALID_GPIO; 214 } 215 } 216 ..... 217 return HDF_SUCCESS; 218 } 219 ``` 220 2213. 完成Light类型获取、闪烁模式设置和停止的接口开发,并实现根据闪烁模式创建和销毁定时器。 222 223 ```c 224 /* Light驱动服务调用GetAllLightInfo接口获取灯类型,调用TurnOnLight接口启动闪烁模式, 225 调用TurnOffLight接口停止闪烁。 */ 226 static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply) 227 { 228 ..... 229 /* 获取灯类型的个数 */ 230 if (!HdfSbufWriteUint32(reply, drvData->lightNum)) { 231 HDF_LOGE("%s: write sbuf fail", __func__); 232 return HDF_FAILURE; 233 } 234 for (i = 0; i < LIGHT_TYPE_BUTT; ++i) { 235 if (drvData->info[i] == NULL) { 236 continue; 237 } 238 lightInfo.lightId = i; 239 lightInfo.reserved = NULL; 240 /* 将Light设备信息填充进reply */ 241 if (!HdfSbufWriteBuffer(reply, &lightInfo, sizeof(lightInfo))) { 242 HDF_LOGE("%s: write sbuf fail", __func__); 243 return HDF_FAILURE; 244 } 245 } 246 247 return HDF_SUCCESS; 248 } 249 250 /* 更新指定类型灯的状态 */ 251 static int32_t UpdateLight(uint32_t lightId, uint32_t lightOn) 252 { 253 ..... 254 /* 如果用户传入的亮度值无效,则使用系统默认的亮度值。 */ 255 if (drvData->info[lightId]->lightBrightness == 0) { 256 lightBrightness = drvData->info[lightId]->defaultBrightness; 257 } else { 258 lightBrightness = drvData->info[lightId]->lightBrightness; 259 } 260 /* 如果0-7bit不等于0,根据lightOn的状态,输出蓝色对应的GPIO引脚。 */ 261 if ((lightBrightness & LIGHT_MAKE_B_BIT) != 0) { 262 ret = WriteGpio(drvData->info[lightId]->busBNum, lightOn); 263 if (ret != HDF_SUCCESS) { 264 HDF_LOGE("%s: write blue gpio fail", __func__); 265 return HDF_FAILURE; 266 } 267 } 268 /* 如果8-15bit不等于0,根据lightOn的状态,输出绿色对应的GPIO引脚。 */ 269 if ((lightBrightness & LIGHT_MAKE_G_BIT) != 0) { 270 ret = WriteGpio(drvData->info[lightId]->busGNum, lightOn); 271 if (ret != HDF_SUCCESS) { 272 HDF_LOGE("%s: write green gpio fail", __func__); 273 return HDF_FAILURE; 274 } 275 } 276 /* 如果16-23bit不等于0,根据lightOn的状态,输出红色对应的GPIO引脚。 */ 277 if ((lightBrightness & LIGHT_MAKE_R_BIT) != 0) { 278 ret = WriteGpio(drvData->info[lightId]->busRNum, lightOn); 279 if (ret != HDF_SUCCESS) { 280 HDF_LOGE("%s: write red gpio fail", __func__); 281 return HDF_FAILURE; 282 } 283 } 284 ..... 285 } 286 287 /* 按照指定的类型和用户传入的参数使能灯 */ 288 static int32_t TurnOnLight(uint32_t lightId, struct HdfSBuf *data, struct HdfSBuf *reply) 289 { 290 ..... 291 /* 接收用户传入的亮度值。24-31bit表示扩展位,16-23bit表示红色,8-15bit表示绿色,0-7bit表示蓝色。如果字段不等于0,表示使能相应颜色的灯。 292 如果支持亮度设置,则通过0-255设置不同的亮度。 */ 293 drvData->info[lightId]->lightBrightness = buf->lightBrightness; 294 /* 常亮模式 */ 295 if (buf->flashEffect.flashMode == LIGHT_FLASH_NONE) { 296 return UpdateLight(lightId, LIGHT_STATE_START); 297 } 298 /* 闪烁模式 */ 299 if (buf->flashEffect.flashMode == LIGHT_FLASH_TIMED) { 300 drvData->info[lightId]->lightState = LIGHT_STATE_START; 301 /* 用户设置的闪烁时间小于系统支持的最短时间,采用系统配置的时间(HCS配置)。 */ 302 drvData->info[lightId]->onTime = buf->flashEffect.onTime < drvData->info[lightId]->onTime ? 303 drvData->info[lightId]->onTime : buf->flashEffect.onTime; 304 drvData->info[lightId]->offTime = buf->flashEffect.offTime < drvData->info[lightId]->offTime ? 305 drvData->info[lightId]->offTime : buf->flashEffect.offTime; 306 /* 创建定时器 */ 307 if (OsalTimerCreate(&drvData->timer, drvData->info[lightId]->onTime, 308 LightTimerEntry, (uintptr_t)lightId) != HDF_SUCCESS) { 309 HDF_LOGE("%s: create light timer fail!", __func__); 310 return HDF_FAILURE; 311 } 312 /* 启动周期定时器 */ 313 if (OsalTimerStartLoop(&drvData->timer) != HDF_SUCCESS) { 314 HDF_LOGE("%s: start light timer fail!", __func__); 315 return HDF_FAILURE; 316 } 317 } 318 return HDF_SUCCESS; 319 } 320 321 /* 按照指定的类型关闭灯 */ 322 static int32_t TurnOffLight(uint32_t lightId, struct HdfSBuf *data, struct HdfSBuf *reply) 323 { 324 /* 删除定时器 */ 325 if (drvData->timer.realTimer != NULL) { 326 327 if (OsalTimerDelete(&drvData->timer) != HDF_SUCCESS) { 328 HDF_LOGE("%s: delete haptic timer fail!", __func__); 329 } 330 } 331 if (UpdateLight(lightId, LIGHT_STATE_STOP) != HDF_SUCCESS) { 332 HDF_LOGE("%s: gpio write fail", __func__); 333 return HDF_FAILURE; 334 } 335 336 return HDF_SUCCESS; 337 } 338 ``` 339 340### 调测验证 341 342驱动开发完成后,在灯单元测试里面开发自测试用例,验证驱动基本功能。测试环境采用开发者自测试平台。 343 344```c++ 345/* 用例执行前,初始化Light接口实例。 */ 346void HdfLightTest::SetUpTestCase() 347{ 348 g_lightDev = NewLightInterfaceInstance(); 349 if (g_lightDev == nullptr) { 350 printf("test light get Module instance fail\n\r"); 351 } 352 int32_t ret = g_lightDev->GetLightInfo(&g_lightInfo, &g_count); 353 if (ret == -1) { 354 printf("get light informations fail\n\r"); 355 } 356} 357 358/* 用例执行后,释放用例资源。 */ 359void HdfLightTest::TearDownTestCase() 360{ 361 if(g_lightDev != nullptr){ 362 FreeLightInterfaceInstance(); 363 g_lightDev = nullptr; 364 } 365} 366 367/* 获取测试灯类型 */ 368HWTEST_F(HdfLightTest, GetLightList001, TestSize.Level1) 369{ 370 struct LightInfo *info = nullptr; 371 372 if (g_lightInfo == nullptr) { 373 EXPECT_NE(nullptr, g_lightInfo); 374 return; 375 } 376 377 printf("get light list num[%d]\n\r", g_count); 378 info = g_lightInfo; 379 380 for (int i = 0; i < g_count; ++i) { 381 printf("get lightId[%d]\n\r", info->lightId); 382 EXPECT_GE(info->lightId, g_minLightId); 383 EXPECT_LE(info->lightId, g_maxLightId); 384 info++; 385 } 386} 387 388/* 测试灯常亮模式 */ 389HWTEST_F(HdfLightTest, EnableLight001, TestSize.Level1) 390{ 391 int32_t i; 392 int32_t ret; 393 struct LightEffect effect; 394 effect->lightBrightness = 0x00800000; 395 effect->flashEffect.flashMode = LIGHT_FLASH_NONE; 396 effect->flashEffect.onTime = 0; 397 effect->flashEffect.offTime = 0; 398 399 for (i = 0; i < g_count; ++i) { 400 401 ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightId, effect); 402 EXPECT_EQ(0, ret); 403 404 OsalSleep(LIGHT_WAIT_TIME); 405 406 ret = g_lightDev->TurnOffLight(type); 407 EXPECT_EQ(0, ret); 408 } 409} 410 411/* 测试灯闪烁模式 */ 412HWTEST_F(HdfLightTest, EnableLight002, TestSize.Level1) 413{ 414 int32_t i; 415 int32_t ret; 416 struct LightEffect effect; 417 effect->lightBrightness = 0x00800000; 418 effect->flashEffect.flashMode = LIGHT_FLASH_TIMED; 419 effect->flashEffect.onTime = g_onTime; 420 effect->flashEffect.offTime = g_offTime; 421 422 for (i = 0; i < g_count; ++i) { 423 424 ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightId, effect); 425 EXPECT_EQ(0, ret); 426 427 OsalSleep(LIGHT_WAIT_TIME); 428 429 ret = g_lightDev->TurnOffLight(type); 430 EXPECT_EQ(0, ret); 431 } 432} 433``` 434