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

- [概述](##概述)
  - [功能简介](###功能简介)
  - [运作机制](###运作机制)
  
- [开发指导](##开发指导)
Z
zhouyanxu 已提交
8 9 10

  - [场景介绍](###场景介绍)

Z
zhouyanxu 已提交
11 12 13
  - [接口说明](###接口说明)
  - [开发步骤](###开发步骤)
  - [调测验证](###调测验证)
Z
zhouyanxu 已提交
14

Z
zhouyanxu 已提交
15 16 17 18 19

## 概述

### 功能简介

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

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

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

### 运作机制

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

Z
zhouyanxu 已提交
30
**图 2**  Light驱动运行图
Z
zhouyanxu 已提交
31

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

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

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

## 开发指导

Z
zhouyanxu 已提交
45 46
### 场景介绍

47
灯设备的控制,在实际生活中比比皆是,例如短信通知时闪灯、手机电量不足时预警、充电时根据充电进度变换灯的颜色等等。这些动作的实现,都需要使用Light驱动模型提供的接口,动态配置点灯模式、配置灯闪烁效果、点灯、熄灯等。
Z
zhouyanxu 已提交
48

Z
zhouyanxu 已提交
49 50
### 接口说明

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

Z
zhouyanxu 已提交
53
**表1**  Light驱动模型对外API接口能力介绍
Z
zhouyanxu 已提交
54 55 56 57 58 59 60 61

| 接口名                                                       | 功能描述                                                     |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| 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)                       | 根据指定的灯类型关闭灯列表中可用的灯。                       |

### 开发步骤
S
sunxuejiao 已提交
62
1. 基于HDF驱动框架,按照驱动Driver Entry程序,完成Light抽象驱动开发(主要由Bind、Init、Release、Dispatch函数接口实现),资源配置及HCS解析。完成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
     其Driver Entry入口函数定义如下:
S
sunxuejiao 已提交
67

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);
     ```
S
sunxuejiao 已提交
80

Z
zhouyanxu 已提交
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
         HdfWorkDestroy(&drvData->work);
         HdfWorkQueueDestroy(&drvData->workQueue);
         (void)OsalMutexDestroy(&drvData->mutex);
         (void)OsalMemFree(drvData);
         g_lightDrvData = NULL;
     }
     ```

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

S
sunxuejiao 已提交
170
     ```
Z
zhouyanxu 已提交
171 172 173
     /* 灯设备HCS配置 */
     device_light :: device {
         device0 :: deviceNode {
Z
zhouyanxu 已提交
174
             policy = 2; // 驱动服务发布的策略(0:不提供服务,1:对内核态发布服务,2:对内核态和用户态都发布服务)
Z
zhouyanxu 已提交
175 176 177 178 179 180
             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 已提交
181 182 183
         }
     ```

S
sunxuejiao 已提交
184
2. 调用配置解析接口,完成器件属性信息解析,器件寄存器解析,并注册到Light设备管理中。
Z
zhouyanxu 已提交
185

S
sunxuejiao 已提交
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
   ```c
   /* 分配资源,解析灯HCS配置 */
   static int32_t ParseLightInfo(const struct DeviceResourceNode *node)
   {
       .....
       /* 从HCS获取支持灯的类型个数 */
       drvData->lightNum = parser->GetElemNum(light, "lightType");
       ....
       for (i = 0; i < drvData->lightNum; ++i) {
       /* 获取类型 */
       ret = parser->GetUint32ArrayElem(light, "lightType", i, &temp, 0);
       CHECK_LIGHT_PARSER_RESULT_RETURN_VALUE(ret, "lightType");
       }
   
       for (i = 0; i < drvData->lightNum; ++i) {
       .....
       /* 类型作为下标开辟空间 */
       drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo));
       .....
       /* 将Light设备信息进行填充 */
       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;
   }
   ```

3. 完成Light获取类型、闪烁和停止接口开发,会根据闪烁模式创建和销毁定时器。
Z
zhouyanxu 已提交
218 219

   ```c
Z
zhouyanxu 已提交
220
   /* Light驱动服务调用GetAllLightInfo获取灯类型,Enable接口启动闪烁模式,
Z
zhouyanxu 已提交
221 222 223 224
      调用Disable接口停止闪烁 */
   static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply)
   {
       .....
Z
zhouyanxu 已提交
225
       /* 获取Light类型个数 */
Z
zhouyanxu 已提交
226 227 228 229 230 231 232 233 234 235
       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 已提交
236
           /* 将Light设备信息填充进reply */
Z
zhouyanxu 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249
           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 已提交
250
       /* 根据用户传的亮度值设置灯的颜色  RGB: R:16-31bit、G:8-15bit、B:0-7bit */
Z
zhouyanxu 已提交
251 252 253 254 255 256 257
       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 已提交
258
       /* 常亮模式 */
Z
zhouyanxu 已提交
259 260 261 262 263 264
       if (buf->flashEffect.flashMode == LIGHT_FLASH_NONE) {
   
           if (GpioWrite(drvData->info[lightType]->busNum, GPIO_VAL_HIGH) != HDF_SUCCESS) {
               return HDF_FAILURE;
           }
       }
Z
zhouyanxu 已提交
265
       /* 闪烁模式 */
Z
zhouyanxu 已提交
266 267
       if (buf->flashEffect.flashMode == LIGHT_FLASH_TIMED) {
           drvData->info[lightType]->lightState = LIGHT_STATE_START;
Z
zhouyanxu 已提交
268
           /* 用户设置的闪烁时间小于系统支持的最短时间,采用系统配置的时间(HCS配置) */
Z
zhouyanxu 已提交
269 270 271 272
           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 已提交
273
           /* 创建定时器 */
Z
zhouyanxu 已提交
274 275 276 277 278
           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 已提交
279
           /* 启动周期定时器 */
Z
zhouyanxu 已提交
280 281 282 283 284 285 286 287 288 289 290
           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 已提交
291
       /* 删除定时器 */
Z
zhouyanxu 已提交
292 293 294 295 296 297
       if (drvData->timer.realTimer != NULL) {
   
           if (OsalTimerDelete(&drvData->timer) != HDF_SUCCESS) {
               HDF_LOGE("%s: delete haptic timer fail!", __func__);
           }
       }
Z
zhouyanxu 已提交
298
       /* 对应的GPIO下电 */
Z
zhouyanxu 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 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
       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);
    }
}
```