1# Light 2 3 4## Overview 5 6### Light 7 8The light driver model provides APIs for the upper-layer light hardware service layer to control lights, including obtaining the light type, setting the lighting mode and blinking effect, and turning on or off a light. This model implements cross-OS porting and differentiated configurations based on the Hardware Driver Foundation (HDF) to achieve the goal of "one-time development for cross-system deployment" of the light driver. 9 10The figure below shows the light driver model. 11 12**Figure 1** Light driver model 13 14![Light driver model](figures/light_driver_model.png) 15 16### Working Principles 17 18The figure below shows how the light driver works. 19 20**Figure 2** How light driver works 21 22![How light driver works](figures/light_working.png) 23 24The following describes how the light module driver loads and starts on a Hi3516D V300 board that runs the standard system. 25 261. The Device Manager reads the Light device management configuration from the **device_info.hcs** file. 272. The Device Manager reads the light data configuration from the **light_config.hcs** file. 283. The HCS Parser parses the light device management configuration, loads the Light Host, and controls the Host to load the driver. 294. The Light Proxy obtains the light HDI service instance and calls the Light Stub over Inter-Process Communication (IPC). 305. The Light Stub processes IPC-related service logic and calls the Light Controller after parameter deserialization. 316. The Light Controller implements the HDI APIs and calls the Light Abstract Driver APIs to operate the light devices. 32 33## Development Guidelines 34 35### When to Use 36 37Light control is widely used in daily life. For example, a light is blinking when a mobile phone receives an SMS message or has low battery level, and a light changes its colors based on the device charging status. These actions are implemented by calling the APIs provided by the light driver model. 38 39### Available APIs 40 41The light driver model provides APIs for obtaining information about all the lights in the system and dynamically setting the blinking mode and duration. The light hardware service calls **GetLightInfo()** to obtain the basic light information, calls **TurnOnLight()** to set the blinking effect, and calls **TurnOffLight()** to turn off lights. The following table describes the APIs of the light driver model. 42 43**Table 1** APIs of the light driver model 44 45| API | Description | 46| ------------------------------------------------------------ | ------------------------------------------------------------ | 47| int32_t (*GetLightInfo)([out] struct LightInfo **lightInfo, [out] uint32_t *count) | Obtains information about all types of lights in the system. <br>**lightInfo** indicates the double pointer to the light information obtained. <br>**count** indicates the pointer to the number of lights.| 48| int32_t (*TurnOnLight)([in] uint32_t lightId, [in] struct LightEffect *effect) | Turns on available lights in the list based on the specified light type. <br>**lightId** indicates the light type, and **effect** indicates the pointer to the light effect.| 49| int32_t (*TurnOffLight)([in] uint32_t lightId) | Turns off available lights in the list based on the specified light type. | 50 51### Development Procedure 521. Based on the HDF and the driver entry, complete the light abstract driver development (using the **Bind**, **Init**, **Release**, and **Dispatch** functions), resource configuration, and HCS parsing. 53 54 - Call **HDF_INIT** to register the driver entry with the HDF. Generally, the HDF calls the **Bind** function and then the **Init** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. 55 - The light driver model uses HDF configuration source (HCS). For details about HCS fields, see [Configuration Management](driver-hdf-manage.md). The light driver entry is defined as follows: 56 57 ```c 58 /* Register the light entry data structure object. */ 59 struct HdfDriverEntry g_lightDriverEntry = { 60 .moduleVersion = 1, // Version of the light module. 61 .moduleName = "HDF_LIGHT", // Light module name, which must be the same as the value of moduleName in the device_info.hcs file. 62 .Bind = BindLightDriver, // Bind() of the light driver. 63 .Init = InitLightDriver, // Init() of the light driver. 64 .Release = ReleaseLightDriver, // Release() of the light driver. 65 }; 66 /* Call HDF_INIT to register the driver entry with the HDF. */ 67 HDF_INIT(g_lightDriverEntry); 68 ``` 69 70 - Develop the light abstract driver. Specifically, implement the **Bind**, **Init**, **Release**, and **Dispatch** functions. 71 72 ```c 73 /* Dispatch the light driver. */ 74 static int32_t DispatchLight(struct HdfDeviceIoClient *client, 75 int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply) 76 { 77 ..... 78 if (cmd == LIGHT_IO_CMD_GET_INFO_LIST) { 79 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(reply, HDF_ERR_INVALID_PARAM); 80 return GetAllLightInfo(data, reply); 81 } 82 83 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); 84 (void)OsalMutexLock(&drvData->mutex); 85 if (!HdfSbufReadInt32(data, &lightId)) { 86 HDF_LOGE("%s: sbuf read lightId fail", __func__); 87 (void)OsalMutexUnlock(&drvData->mutex); 88 return HDF_ERR_INVALID_PARAM; 89 } 90 ..... 91 ret = DispatchCmdHandle(lightId, data, reply); 92 (void)OsalMutexUnlock(&drvData->mutex); 93 return ret; 94 } 95 96 /* Bind the external service provided by the light driver to the HDF. */ 97 int32_t BindLightDriver(struct HdfDeviceObject *device) 98 { 99 struct LightDriverData *drvData = NULL; 100 101 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(device, HDF_FAILURE); 102 /* Allocate resources for private interfaces. */ 103 drvData = (struct LightDriverData *)OsalMemCalloc(sizeof(*drvData)); 104 CHECK_LIGHT_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_MALLOC_FAIL); 105 /* Functions to be dispatched. */ 106 drvData->ioService.Dispatch = DispatchLight; 107 drvData->device = device; 108 device->service = &drvData->ioService; 109 g_lightDrvData = drvData; 110 return HDF_SUCCESS; 111 } 112 113 /* Initialize the light driver. */ 114 int32_t InitLightDriver(struct HdfDeviceObject *device) 115 { 116 ..... 117 /* Initialize the workqueue. */ 118 if (HdfWorkQueueInit(&drvData->workQueue, LIGHT_WORK_QUEUE_NAME) != HDF_SUCCESS) { 119 HDF_LOGE("%s: init workQueue fail!", __func__); 120 return HDF_FAILURE; 121 } 122 /* Initialize work items. */ 123 if (HdfWorkInit(&drvData->work, LightWorkEntry, (void*)drvData) != HDF_SUCCESS) { 124 HDF_LOGE("%s: init work fail!", __func__); 125 return HDF_FAILURE; 126 } 127 /* Parse the HCS. */ 128 if (GetLightConfigData(device->property) != HDF_SUCCESS) { 129 HDF_LOGE("%s: get light config fail!", __func__); 130 return HDF_FAILURE; 131 } 132 /* Set the GPIO pin direction. */ 133 if (SetLightGpioDir(drvData) != HDF_SUCCESS) { 134 HDF_LOGE("%s: set light gpio dir fail!", __func__); 135 return HDF_FAILURE; 136 } 137 138 return HDF_SUCCESS; 139 } 140 141 /* Release the resources allocated for driver initialization. */ 142 void ReleaseLightDriver(struct HdfDeviceObject *device) 143 { 144 ..... 145 /* Release the allocated resources. */ 146 for (i = LIGHT_TYPE_NONE; i < LIGHT_TYPE_BUTT; ++i) { 147 148 if (drvData->info[i] != NULL) { 149 OsalMemFree(drvData->info[i]); 150 drvData->info[i] = NULL; 151 } 152 } 153 /* Destroy workqueue resources. */ 154 HdfWorkDestroy(&drvData->work); 155 HdfWorkQueueDestroy(&drvData->workQueue); 156 (void)OsalMutexDestroy(&drvData->mutex); 157 OsalMemFree(drvData); 158 g_lightDrvData = NULL; 159 } 160 ``` 161 162 - The light device management module is responsible for publishing light device APIs in the system. During the system startup process, the HDF loads the device management driver based on **Light Host** in the HCS. 163 164 ```c 165 /* HCS of the light device. */ 166 light :: host { 167 hostName = "light_host"; 168 device_light :: device { 169 device0 :: deviceNode { 170 policy = 2; // Policy for the driver to publish services. If the value is 0, the driver does not publish services. If the value is 1, the driver publishes services to the kernel space. If the value is 2, the driver publishes services to both the kernel space and user space. 171 priority = 100; // Priority (0–200) for starting the light driver. A larger value indicates a lower priority. The recommended value is 100. If the priorities are the same, the device loading sequence is not ensured. 172 preload = 0; // The value 0 means to load the driver by default during the startup of the system. The value 2 means the opposite. 173 permission = 0664; // Permission for the device node created. 174 moduleName = "HDF_LIGHT"; // Light driver name. The value must be the same as the value of moduleName in the driver entry structure. 175 serviceName = "hdf_light"; // Service published by the light driver. The service name must be unique. 176 deviceMatchAttr = "hdf_light_driver"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. 177 } 178 } 179 } 180 ``` 181 1822. Parse the device attribute information and registers, and register them with the light device management module. 183 184 ```c 185 /* Allocate resources and parse the HCS. */ 186 static int32_t ParseLightInfo(const struct DeviceResourceNode *node, const struct DeviceResourceIface *parser) 187 { 188 ..... 189 /* Obtain the number of supported light types from the HCS. */ 190 drvData->lightNum = parser->GetElemNum(light, "lightId"); 191 .... 192 for (i = 0; i < drvData->lightNum; ++i) { 193 /* Obtain the light type. */ 194 ret = parser->GetUint32ArrayElem(light, "lightId", i, &temp, 0); 195 CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "lightId"); 196 } 197 198 for (i = 0; i < drvData->lightNum; ++i) { 199 ..... 200 /* Types are used as subscripts to create space. */ 201 drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo)); 202 ..... 203 /* Fill in the light device information. */ 204 ret = parser->GetUint32(node, "busRNum", (uint32_t *)&drvData->info[temp]->busRNum, 0); 205 if (ret != HDF_SUCCESS) { 206 /* If busNum fails to be obtained, the color of the light corresponding to busNum cannot be set. */ 207 drvData->info[temp]->busRNum = LIGHT_INVALID_GPIO; 208 } 209 ret = parser->GetUint32(node, "busGNum", (uint32_t *)&drvData->info[temp]->busGNum, 0); 210 if (ret != HDF_SUCCESS) { 211 drvData->info[temp]->busGNum = LIGHT_INVALID_GPIO; 212 } 213 ret = parser->GetUint32(node, "busBNum", (uint32_t *)&drvData->info[temp]->busBNum, 0); 214 if (ret != HDF_SUCCESS) { 215 drvData->info[temp]->busBNum = LIGHT_INVALID_GPIO; 216 } 217 } 218 ..... 219 return HDF_SUCCESS; 220 } 221 ``` 222 2233. Implement the APIs for obtaining the light type, setting the blinking mode, turning on and off lights, and creating and destroying a timer based on the blinking mode. 224 225 ```c 226 /* Call GetAllLightInfo() to obtain the light types, call TurnOnLight() to turn on lights, 227 and call TurnOffLight() to turn off lights. */ 228 static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply) 229 { 230 ..... 231 /* Obtain the number of light types. */ 232 if (!HdfSbufWriteUint32(reply, drvData->lightNum)) { 233 HDF_LOGE("%s: write sbuf fail", __func__); 234 return HDF_FAILURE; 235 } 236 for (i = 0; i < LIGHT_TYPE_BUTT; ++i) { 237 if (drvData->info[i] == NULL) { 238 continue; 239 } 240 lightInfo.lightId = i; 241 lightInfo.reserved = NULL; 242 /* Fill the light device information into the reply. */ 243 if (!HdfSbufWriteBuffer(reply, &lightInfo, sizeof(lightInfo))) { 244 HDF_LOGE("%s: write sbuf fail", __func__); 245 return HDF_FAILURE; 246 } 247 } 248 249 return HDF_SUCCESS; 250 } 251 252 /* Update the status of the lights of the specified type. */ 253 static int32_t UpdateLight(uint32_t lightId, uint32_t lightOn) 254 { 255 ..... 256 /* If the lightBrightness value passed in is invalid, use the default value. */ 257 if (drvData->info[lightId]->lightBrightness == 0) { 258 lightBrightness = drvData->info[lightId]->defaultBrightness; 259 } else { 260 lightBrightness = drvData->info[lightId]->lightBrightness; 261 } 262 /* If bits 0 to 7 are not 0, output the GPIO pins corresponding to blue based on the status of lightOn. */ 263 if ((lightBrightness & LIGHT_MAKE_B_BIT) != 0) { 264 ret = WriteGpio(drvData->info[lightId]->busBNum, lightOn); 265 if (ret != HDF_SUCCESS) { 266 HDF_LOGE("%s: write blue gpio fail", __func__); 267 return HDF_FAILURE; 268 } 269 } 270 /* If bits 8 to 15 are not 0, output the GPIO pins corresponding to green based on the status of lightOn. */ 271 if ((lightBrightness & LIGHT_MAKE_G_BIT) != 0) { 272 ret = WriteGpio(drvData->info[lightId]->busGNum, lightOn); 273 if (ret != HDF_SUCCESS) { 274 HDF_LOGE("%s: write green gpio fail", __func__); 275 return HDF_FAILURE; 276 } 277 } 278 /* If bits 16 to 23 are not 0, output the GPIO pins corresponding to red based on the status of lightOn. */ 279 if ((lightBrightness & LIGHT_MAKE_R_BIT) != 0) { 280 ret = WriteGpio(drvData->info[lightId]->busRNum, lightOn); 281 if (ret != HDF_SUCCESS) { 282 HDF_LOGE("%s: write red gpio fail", __func__); 283 return HDF_FAILURE; 284 } 285 } 286 ..... 287 } 288 289 /* Enable lights based on the specified light type and input parameters. */ 290 static int32_t TurnOnLight(uint32_t lightId, struct HdfSBuf *data, struct HdfSBuf *reply) 291 { 292 ..... 293 /* Receive the lightBrightness value passed in. Bits 24 to 31 are extension bits, bits 16 to 23 indicate red, bits 8 to 15 indicate green, and bits 0 to 7 indicate blue. If lightBrightness is not 0, turn on the light in the specified color. 294 Set the light brightness to a value ranging from 0 to 255 if supported. */ 295 drvData->info[lightId]->lightBrightness = buf->lightBrightness; 296 /* The light is steady on. */ 297 if (buf->flashEffect.flashMode == LIGHT_FLASH_NONE) { 298 return UpdateLight(lightId, LIGHT_STATE_START); 299 } 300 /* The light is blinking. */ 301 if (buf->flashEffect.flashMode == LIGHT_FLASH_TIMED) { 302 drvData->info[lightId]->lightState = LIGHT_STATE_START; 303 /* If the specified blinking duration is less than the minimum time period supported by the system, the time configured by the system (in HCS) is used. */ 304 drvData->info[lightId]->onTime = buf->flashEffect.onTime < drvData->info[lightId]->onTime ? 305 drvData->info[lightId]->onTime : buf->flashEffect.onTime; 306 drvData->info[lightId]->offTime = buf->flashEffect.offTime < drvData->info[lightId]->offTime ? 307 drvData->info[lightId]->offTime : buf->flashEffect.offTime; 308 /* Create a timer. */ 309 if (OsalTimerCreate(&drvData->timer, drvData->info[lightId]->onTime, 310 LightTimerEntry, (uintptr_t)lightId) != HDF_SUCCESS) { 311 HDF_LOGE("%s: create light timer fail!", __func__); 312 return HDF_FAILURE; 313 } 314 /* Start the periodic timer. */ 315 if (OsalTimerStartLoop(&drvData->timer) != HDF_SUCCESS) { 316 HDF_LOGE("%s: start light timer fail!", __func__); 317 return HDF_FAILURE; 318 } 319 } 320 return HDF_SUCCESS; 321 } 322 323 /* Turn off lights based on the specified light type. */ 324 static int32_t TurnOffLight(uint32_t lightId, struct HdfSBuf *data, struct HdfSBuf *reply) 325 { 326 /* Delete the timer. */ 327 if (drvData->timer.realTimer != NULL) { 328 329 if (OsalTimerDelete(&drvData->timer) != HDF_SUCCESS) { 330 HDF_LOGE("%s: delete haptic timer fail!", __func__); 331 } 332 } 333 if (UpdateLight(lightId, LIGHT_STATE_STOP) != HDF_SUCCESS) { 334 HDF_LOGE("%s: gpio write fail", __func__); 335 return HDF_FAILURE; 336 } 337 338 return HDF_SUCCESS; 339 } 340 ``` 341 342### Verification 343 344After the driver is developed, develop auto-test cases in the light unit test to verify the basic functionalities of the driver. Use the developer self-test platform as the test environment. 345 346```c++ 347/* Initialize the LightInterfaceInstance before executing the test case. */ 348void HdfLightTest::SetUpTestCase() 349{ 350 g_lightDev = NewLightInterfaceInstance(); 351 if (g_lightDev == nullptr) { 352 printf("test light get Module instance fail\n\r"); 353 } 354 int32_t ret = g_lightDev->GetLightInfo(&g_lightInfo, &g_count); 355 if (ret == -1) { 356 printf("get light informations fail\n\r"); 357 } 358} 359 360/* After the test case is executed, release the resources used by the test case. */ 361void HdfLightTest::TearDownTestCase() 362{ 363 if(g_lightDev != nullptr){ 364 FreeLightInterfaceInstance(); 365 g_lightDev = nullptr; 366 } 367} 368 369/* Obtain the test light type. */ 370HWTEST_F(HdfLightTest, GetLightList001, TestSize.Level1) 371{ 372 struct LightInfo *info = nullptr; 373 374 if (g_lightInfo == nullptr) { 375 EXPECT_NE(nullptr, g_lightInfo); 376 return; 377 } 378 379 printf("get light list num[%d]\n\r", g_count); 380 info = g_lightInfo; 381 382 for (int i = 0; i < g_count; ++i) { 383 printf("get lightId[%d]\n\r", info->lightId); 384 EXPECT_GE(info->lightId, g_minLightId); 385 EXPECT_LE(info->lightId, g_maxLightId); 386 info++; 387 } 388} 389 390/* Verify the steady on state of the light. */ 391HWTEST_F(HdfLightTest, EnableLight001, TestSize.Level1) 392{ 393 int32_t i; 394 int32_t ret; 395 struct LightEffect effect; 396 effect->lightBrightness = 0x00800000; 397 effect->flashEffect.flashMode = LIGHT_FLASH_NONE; 398 effect->flashEffect.onTime = 0; 399 effect->flashEffect.offTime = 0; 400 401 for (i = 0; i < g_count; ++i) { 402 403 ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightId, effect); 404 EXPECT_EQ(0, ret); 405 406 OsalSleep(LIGHT_WAIT_TIME); 407 408 ret = g_lightDev->TurnOffLight(type); 409 EXPECT_EQ(0, ret); 410 } 411} 412 413/* Verify the blinking mode of the light. */ 414HWTEST_F(HdfLightTest, EnableLight002, TestSize.Level1) 415{ 416 int32_t i; 417 int32_t ret; 418 struct LightEffect effect; 419 effect->lightBrightness = 0x00800000; 420 effect->flashEffect.flashMode = LIGHT_FLASH_TIMED; 421 effect->flashEffect.onTime = g_onTime; 422 effect->flashEffect.offTime = g_offTime; 423 424 for (i = 0; i < g_count; ++i) { 425 426 ret = g_lightDev->TurnOnLight(g_lightInfo[i]->lightId, effect); 427 EXPECT_EQ(0, ret); 428 429 OsalSleep(LIGHT_WAIT_TIME); 430 431 ret = g_lightDev->TurnOffLight(type); 432 EXPECT_EQ(0, ret); 433 } 434} 435``` 436