• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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