1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2020-2021 Huawei Device Co., Ltd.
5#
6# HDF is dual licensed: you can use it either under the terms of
7# the GPL, or the BSD license, at your option.
8# See the LICENSE file in the root of this repository for complete details.
9
10
11import json
12from string import Template
13import os
14
15import hdf_utils
16from hdf_tool_settings import HdfToolSettings
17from hdf_tool_exception import HdfToolException
18from command_line.operate_group_passwd import OperateGroupPasswd
19from .driver_add.hdf_add_driver import HdfAddDriver
20from .hdf_command_handler_base import HdfCommandHandlerBase
21from .hdf_command_error_code import CommandErrorCode
22from .hdf_device_info_hcs import HdfDeviceInfoHcsFile
23from .hdf_get_handler import HdfGetHandler
24from .hdf_vendor_build_file import HdfVendorBuildFile
25from .hdf_vendor_kconfig_file import HdfVendorKconfigFile
26from .hdf_vendor_mk_file import HdfVendorMkFile
27from .hdf_driver_config_file import HdfDriverConfigFile
28from .hdf_vendor_makefile import HdfVendorMakeFile
29from .hdf_defconfig_patch import HdfDefconfigAndPatch
30
31
32class HdfAddHandler(HdfCommandHandlerBase):
33    def __init__(self, args):
34        super(HdfAddHandler, self).__init__()
35        self.cmd = 'add'
36        self.handlers = {
37            'vendor': self._add_vendor_handler,
38            'module': self._add_module_handler,
39            'driver': self._add_driver_handler,
40            'config': self._add_config_handler,
41        }
42        self.parser.add_argument("--action_type",
43                                 help=' '.join(self.handlers.keys()),
44                                 required=True)
45        self.parser.add_argument("--root_dir", required=True)
46        self.parser.add_argument("--vendor_name")
47        self.parser.add_argument("--module_name")
48        self.parser.add_argument("--driver_name")
49        self.parser.add_argument("--board_name")
50        self.parser.add_argument("--kernel_name")
51        self.parser.add_argument("--runmode")
52        self.parser.add_argument("--device_name")
53        self.args_original = args
54        self.args = self.parser.parse_args(args)
55        self.hdf_tool = HdfToolSettings()
56
57    @staticmethod
58    def _render(template_path, output_path, data_model):
59        if not os.path.exists(template_path):
60            return
61        raw_content = hdf_utils.read_file(template_path)
62        contents = Template(raw_content).safe_substitute(data_model)
63        hdf_utils.write_file(output_path, contents)
64
65    def _file_gen_lite(self, template, out_dir, filename, model):
66        templates_dir = hdf_utils.get_templates_lite_dir()
67        template_path = os.path.join(templates_dir, template)
68        file_path = os.path.join(out_dir, filename)
69        self._render(template_path, file_path, model)
70
71    def _add_vendor_handler(self):
72        self.check_arg_raise_if_not_exist("vendor_name")
73        root, vendor, _, _, board = self.get_args()
74        target_dir = hdf_utils.get_vendor_hdf_dir(root, vendor)
75        if os.path.exists(target_dir):
76            raise HdfToolException(
77                "%s already exists" %
78                target_dir, CommandErrorCode.TARGET_ALREADY_EXIST)
79        os.makedirs(target_dir)
80        self._file_gen_lite('hdf_vendor_kconfig.template', target_dir,
81                            'Kconfig', {})
82        board_parent_path = self.hdf_tool.get_board_parent_path(board)
83        if not board_parent_path:
84            board_parent_path = 'vendor/hisilicon'
85        data_model = {
86            "board_parent_path": board_parent_path
87        }
88        self._file_gen_lite('hdf_vendor_mk.template', target_dir,
89                            'hdf_vendor.mk', data_model)
90
91    def _add_module_handler(self):
92        self.check_arg_raise_if_not_exist("vendor_name")
93        self.check_arg_raise_if_not_exist("module_name")
94        self.check_arg_raise_if_not_exist("kernel_name")
95        self.check_arg_raise_if_not_exist("board_name")
96        self.check_arg_raise_if_not_exist("driver_name")
97        args_tuple = self.get_args()
98        root, vendor, module, driver, board, kernel, _ = args_tuple
99        board_list = self.hdf_tool.get_board_list()
100        if board not in board_list:
101            raise HdfToolException(
102                'supported boards name : %s not exits ' % board)
103        framework_hdf = hdf_utils.get_vendor_hdf_dir_framework(root)
104        if not os.path.exists(framework_hdf):
105            raise HdfToolException(
106                ' framework model path  "%s" not exist' %
107                framework_hdf, CommandErrorCode.TARGET_NOT_EXIST)
108        # framework create file .c
109        framework_drv_root_dir = hdf_utils.get_drv_root_dir(
110            root, module)
111        if os.path.exists(framework_drv_root_dir):
112            raise HdfToolException('module "%s" already exist' % module,
113                                   CommandErrorCode.TARGET_ALREADY_EXIST)
114        os.makedirs(framework_drv_root_dir)
115        # create .c template driver file
116        state, driver_file_path = self._add_driver(*args_tuple)
117        if not state:
118            raise HdfToolException(
119                'create drivers file fail  "%s" ' %
120                driver_file_path.split("\\")[-1])
121        config_item, config_file_out = \
122            self.diff_type_module_add_operation(
123                framework_hdf, driver_file_path, args_tuple)
124        config_name = "create_model.config"
125        config_file = hdf_utils.read_file(
126            os.path.join('resources', config_name))
127        config_file_json = json.loads(config_file)
128        config_file_json[module] = config_file_out
129        hdf_utils.write_config(
130            root_path=root, config_file_json=config_file_json,
131            config_name=config_name)
132        return json.dumps(config_item)
133
134    def diff_type_module_add_operation(self, framework_hdf,
135                                       driver_file_path, args_tuple):
136        root, vendor, module, driver, board, kernel, _ = args_tuple
137        converter = hdf_utils.WordsConverter(self.args.module_name)
138        driver_name_converter = hdf_utils.WordsConverter(self.args.driver_name)
139        if board.endswith("user"):
140            file_path, model_level_config = \
141                self._add_module_handler_linux_user(
142                    framework_hdf, driver_file_path,
143                    *args_tuple)
144        else:
145            adapter_hdf = hdf_utils.get_vendor_hdf_dir_adapter(root, kernel)
146            if not os.path.exists(adapter_hdf):
147                raise HdfToolException(
148                    ' adapter model path  "%s" not exist' %
149                    adapter_hdf, CommandErrorCode.TARGET_NOT_EXIST)
150            # create module folder under the adapter path
151            adapter_model_path = os.path.join(adapter_hdf, 'model', module)
152            if not os.path.exists(adapter_model_path):
153                os.makedirs(adapter_model_path)
154            data_model = {
155                "module_upper_case": converter.upper_case(),
156                "module_lower_case": converter.lower_case(),
157                "driver_file_name": ("%s_driver.c" % driver_name_converter.lower_case()),
158                "driver_name": driver_name_converter.lower_case()
159            }
160            # create files in the module under the adapter
161            if kernel == 'liteos':
162                file_path, model_level_config = \
163                    self._add_module_handler_liteos(
164                        framework_hdf, adapter_model_path,
165                        data_model, converter, *args_tuple)
166            elif kernel == "linux":
167                file_path, model_level_config = \
168                    self._add_module_handler_linux(
169                        framework_hdf, adapter_model_path,
170                        data_model, *args_tuple)
171            else:
172                model_level_config = ""
173        base_config = {
174            'module_name': module,
175            'module_path': file_path,
176            'driver_name': "%s_driver" % driver,
177            'driver_file_path': driver_file_path,
178        }
179        config_item = {'enabled': True}
180        config_item.update(base_config)
181        config_file_out = {'module_level_config_path': model_level_config}
182        config_file_out.update(base_config)
183        return config_item, config_file_out
184
185    def _add_module_handler_liteos(self, framework_hdf, adapter_model_path,
186                                   data_model, converter, *args_tuple):
187        root, vendor, module, driver, board, kernel, _ = args_tuple
188        liteos_file_path = {}
189        liteos_level_config_file_path = {}
190        liteos_file_name = ['BUILD.gn', 'Kconfig', 'Makefile']
191        temp_path = self.hdf_tool.get_template_path()
192        template_path = os.path.join(framework_hdf, temp_path)
193        for file_name in liteos_file_name:
194            for i in hdf_utils.template_filename_filtrate(
195                    template_path, kernel):
196                if i.find(file_name.split(".")[0]) > 0:
197                    out_path = os.path.join(adapter_model_path, file_name)
198                    self._render(os.path.join(template_path, i),
199                                 out_path, data_model)
200                    liteos_file_path[file_name] = out_path
201
202        # Modify Kconfig file
203        vendor_k = HdfVendorKconfigFile(root, vendor, kernel, path="")
204        vendor_k_path = vendor_k.add_module([module, 'Kconfig'])
205        liteos_level_config_file_path[module + "_Kconfig"] = vendor_k_path
206
207        # Modify hdf_lite.mk file
208        vendor_mk = HdfVendorMkFile(root, vendor)
209        vendor_mk_path = vendor_mk.add_module(module)
210        liteos_level_config_file_path[module + "_hdf_lite"] = vendor_mk_path
211
212        # Modify Build.gn file
213        vendor_gn = HdfVendorBuildFile(root, vendor)
214        vendor_gn_path = vendor_gn.add_module(module)
215        liteos_level_config_file_path[module + "Build"] = vendor_gn_path
216
217        # Modify config file
218        device_info = HdfDeviceInfoHcsFile(
219            root, vendor, module, board, driver, path="")
220        hcs_file_path = device_info.add_model_hcs_file_config()
221        liteos_file_path["devices_info.hcs"] = hcs_file_path
222
223        dot_file_list = hdf_utils.get_dot_configs_path(root, vendor, board)
224        template_string = "LOSCFG_DRIVERS_HDF_${module_upper_case}=y\n"
225        new_demo_config = Template(template_string).substitute(
226            {"module_upper_case": converter.upper_case()})
227        for dot_file in dot_file_list:
228            file_lines = hdf_utils.read_file_lines(dot_file)
229            file_lines[-1] = "{}\n".format(file_lines[-1].strip())
230            if new_demo_config != file_lines[-1]:
231                file_lines.append(new_demo_config)
232                hdf_utils.write_file_lines(dot_file, file_lines)
233        liteos_level_config_file_path[module + "_dot_configs"] = dot_file_list
234        return liteos_file_path, liteos_level_config_file_path
235
236    def _add_module_handler_linux(self, framework_hdf, adapter_model_path,
237                                  data_model, *args_tuple):
238        root, vendor, module, driver, board, kernel, _ = args_tuple
239        linux_file_path = {}
240        linux_level_config_file_path = {}
241        linux_file_name = ['Kconfig', 'Makefile']
242        temp_path = self.hdf_tool.get_template_path()
243        template_path = os.path.join(framework_hdf, temp_path)
244        for file_name in linux_file_name:
245            for i in hdf_utils.template_filename_filtrate(
246                    template_path, kernel):
247                if i.find(file_name.split(".")[0]) > 0:
248                    out_path = os.path.join(adapter_model_path, file_name)
249                    self._render(os.path.join(template_path, i),
250                                 out_path, data_model)
251                    linux_file_path[file_name] = out_path
252
253        # Modify Kconfig file
254        vendor_k = HdfVendorKconfigFile(root, vendor, kernel, path="")
255        vendor_k_path = vendor_k.add_module([module, 'Kconfig'])
256        linux_level_config_file_path[module + "_Kconfig"] = vendor_k_path
257
258        # Modify Makefile file
259        vendor_mk = HdfVendorMakeFile(root, vendor, kernel, path='')
260        vendor_mk_path = vendor_mk.add_module(data_model)
261        linux_level_config_file_path[module + "_Makefile"] = vendor_mk_path
262        # device_info.hcs
263        device_info = HdfDeviceInfoHcsFile(
264            root, vendor, module, board, driver, path="")
265        hcs_file_path = device_info.add_model_hcs_file_config()
266        linux_file_path["devices_info.hcs"] = hcs_file_path
267
268        # dot_configs config file
269        template_string = "CONFIG_DRIVERS_HDF_${module_upper_case}=y\n"
270        new_demo_config = [Template(template_string).substitute(data_model)]
271        defconfig_patch = HdfDefconfigAndPatch(
272            root, vendor, kernel, board,
273            data_model, new_demo_config)
274
275        config_path = defconfig_patch.get_config_config()
276        files = []
277        patch_list = defconfig_patch.add_module(config_path, files=files, codetype=None)
278        config_path = defconfig_patch.get_config_patch()
279        files1 = []
280        defconfig_list = defconfig_patch.add_module(config_path, files=files1, codetype=None)
281        linux_level_config_file_path[module + "_dot_configs"] = \
282            list(set(patch_list + defconfig_list))
283        return linux_file_path, linux_level_config_file_path
284
285    def _add_module_handler_linux_user(self, framework_hdf, driver_file_path,
286                                       *args_tuple):
287        linux_file_path = {}
288        linux_level_config_file_path = {}
289        # create user build.gn files
290        root, vendor, module, driver, board, kernel, _ = args_tuple
291        relative_path = self.hdf_tool.get_user_adapter_path()
292        user_model_path = os.path.join(root, relative_path, module)
293        if not os.path.exists(user_model_path):
294            os.makedirs(user_model_path)
295        user_model_file_path = os.path.join(user_model_path, "BUILD.gn")
296        temp_path = self.hdf_tool.get_template_path()
297        template_path = os.path.join(framework_hdf, temp_path)
298        user_file_path = driver_file_path.split(root)[-1].replace("\\", "/")
299        if user_file_path.startswith("/"):
300            driver_file_name = "".join(["/", user_file_path.replace("\\", "/")])
301        else:
302            driver_file_name = "".join(["//", user_file_path.replace("\\", "/")])
303        data_model = {
304            "model_path": "/".join(["/", relative_path, module]),
305            "driver_file_name": driver_file_name,
306            "model_name": module,
307        }
308        for file_name in os.listdir(template_path):
309            if file_name.startswith("User_build"):
310                user_template_build = os.path.join(template_path, file_name)
311                if os.path.exists(user_template_build):
312                    self._render(user_template_build,
313                                 user_model_file_path, data_model)
314                    linux_file_path["BUILD.gn"] = user_model_file_path
315        linux_file_path = self.revise_passwd_group_file(
316            root_path=root, linux_file_path=linux_file_path, model_name=module)
317        # build.gn file add path
318        ohos_relative_path = '/'.join(relative_path.split("/")[:-1])
319        ohos_path = os.path.join(root, ohos_relative_path, 'BUILD.gn')
320        user_build_info = hdf_utils.read_file_lines(ohos_path)
321        ohos_template_line = "${model_path}:libhdf_${model_name}_hotplug"
322        need_add_line = Template(ohos_template_line).substitute(data_model)
323        temp_line = ('      "%s",' % need_add_line) + '\n'
324        for index, info in enumerate(user_build_info):
325            if info.find("else") > 0 and temp_line not in user_build_info:
326                user_build_info.insert(index + 3, temp_line)
327                hdf_utils.write_file_lines(ohos_path, user_build_info)
328                break
329        linux_file_path["adapter_build.gn"] = ohos_path
330        # add hcs file
331        device_info = HdfDeviceInfoHcsFile(
332            root, vendor, module, board, driver, path="")
333        hcs_file_path = device_info.add_model_hcs_file_config_user()
334        linux_file_path["devices_info.hcs"] = hcs_file_path
335        return linux_file_path, linux_level_config_file_path
336
337    def _add_driver(self, *args_tuple):
338        root, vendor, module, driver, board, kernel, _ = args_tuple
339        drv_converter = hdf_utils.WordsConverter(self.args.driver_name)
340        drv_src_dir = hdf_utils.get_drv_src_dir(root, module)
341
342        parent_depth = len(drv_src_dir.split(os.path.sep))
343        dir_dict = {}
344        for root, dirs, _ in os.walk(drv_src_dir, topdown=True):
345            for dir_name in dirs:
346                dir_path = os.path.join(root, dir_name)
347                if len(dir_path.split(os.path.sep)) == parent_depth + 1:
348                    dir_dict[dir_name] = []
349                if len(dir_path.split(os.path.sep)) == parent_depth + 2:
350                    dir_dict.get(dir_path.split(os.path.sep)[-2]).append(dir_name)
351
352        common_dir_list = dir_dict.get("common")
353        if common_dir_list is not None:
354            if "src" in common_dir_list:
355                driver_file_path = os.path.join(drv_src_dir, "common", "src")
356            else:
357                driver_file_path = os.path.join(drv_src_dir, "common")
358        else:
359            driver_file_path = drv_src_dir
360
361        if not os.path.exists(driver_file_path):
362            raise HdfToolException(
363                '"%s" is path not exist' %
364                driver_file_path, CommandErrorCode.TARGET_NOT_EXIST)
365        data_model = {
366            'driver_lower_case': drv_converter.lower_case(),
367            'driver_upper_camel_case': drv_converter.upper_camel_case(),
368            'driver_lower_camel_case': drv_converter.lower_camel_case(),
369            'driver_upper_case': drv_converter.upper_case()
370        }
371        self._file_gen_lite('hdf_driver.c.template', driver_file_path,
372                            '%s_driver.c' % driver, data_model)
373        result_path = os.path.join(driver_file_path, '%s_driver.c' % driver)
374        return True, result_path
375
376    def _add_driver_handler(self):
377        self.check_arg_raise_if_not_exist("vendor_name")
378        self.check_arg_raise_if_not_exist("module_name")
379        self.check_arg_raise_if_not_exist("kernel_name")
380        self.check_arg_raise_if_not_exist("board_name")
381        self.check_arg_raise_if_not_exist("driver_name")
382        self.check_arg_raise_if_not_exist("device_name")
383        args_tuple = self.get_args()
384        root, vendor, module, driver, board, kernel, device = args_tuple
385        board_list = self.hdf_tool.get_create_board_type()
386        if board not in board_list:
387            raise HdfToolException(
388                'supported boards name : %s not exits ' % board,
389                CommandErrorCode.TARGET_NOT_EXIST)
390        if "--runmode" in self.args_original:
391            runmode_index = self.args_original.index("--runmode")
392            temp_args = self.args_original[:runmode_index] + \
393                        self.args_original[runmode_index + 2:]
394        else:
395            temp_args = self.args_original
396        get_board = HdfGetHandler(temp_args)
397        temp_board_dict = json.loads(get_board.judge_create_driver_exist())
398        if temp_board_dict:
399            temp_board_device_dict = temp_board_dict.get(board)
400            if temp_board_device_dict is not None and\
401                    temp_board_device_dict.get("driver_file_list") is not None:
402                driver_name_list = temp_board_device_dict.get("driver_file_list").get(device, [])
403                if driver_name_list and driver in driver_name_list:
404                    raise HdfToolException(
405                        'driver name %s exist in device %s' %
406                        (driver, device),
407                        CommandErrorCode.TARGET_ALREADY_EXIST)
408        framework_hdf = hdf_utils.get_vendor_hdf_dir_framework(root)
409        hdf_utils.judge_file_path_exists(framework_hdf)
410        framework_drv_root_dir = hdf_utils.get_module_dir(root, module)
411        hdf_utils.judge_file_path_exists(framework_drv_root_dir)
412        add_driver = HdfAddDriver(args=args_tuple)
413        # create driver Source File (.c 、.h)
414        resources_path, temp_file_name, config_item, file_path =\
415            self._add_create_common(add_driver, args_tuple)
416        model_driver_file_path = os.path.join(resources_path, temp_file_name)
417        config_file = hdf_utils.read_file(model_driver_file_path)
418        config_file_json = json.loads(config_file)
419        result_config_file_json = add_driver.driver_create_info_format(
420            config_file_json, config_item, file_path)
421        hdf_utils.write_config(root_path=root,
422                               config_file_json=result_config_file_json,
423                               config_name=temp_file_name)
424        return json.dumps(config_item, indent=4)
425
426    def _add_create_common(self, add_driver, args_tuple):
427        _, _, module, driver, board, _, device = args_tuple
428        state, file_list, head_list = add_driver.add_driver(*args_tuple)
429        if board == "hispark_taurus":
430            file_path = add_driver.add_liteos(file_list, head_list)
431        elif board.endswith("linux"):
432            file_path = add_driver.add_linux(file_list, head_list)
433        elif board.startswith("rk3568_kernel"):
434            file_path = add_driver.add_kernel(file_list, head_list)
435        elif board.startswith("hispark_taurus_standard_kernel"):
436            file_path = add_driver.add_kernel(file_list, head_list)
437
438        config_item = {
439            'module_name': module,
440            'module_path': file_path,
441            'driver_name': "-".join([device, driver]),
442            'driver_file_path': file_list,
443            'head_file_path': head_list,
444            'enabled': "true"
445        }
446        resources_path = self.hdf_tool.get_resources_file_path()
447        config_setting_dict = self.hdf_tool.get_config_setting_info()
448        temp_file_name = config_setting_dict["create_driver_file"]
449        return resources_path, temp_file_name, config_item, file_path
450
451    def _add_config_handler(self):
452        self.check_arg_raise_if_not_exist("module_name")
453        self.check_arg_raise_if_not_exist("driver_name")
454        self.check_arg_raise_if_not_exist("board_name")
455        root, _, module, driver, board, kernel = self.get_args()
456        drv_config = HdfDriverConfigFile(root, board, module, driver, kernel)
457        drv_config.create_driver()
458        return drv_config.get_drv_config_path()
459
460    def revise_passwd_group_file(self, root_path, linux_file_path, model_name):
461        group_passwd = OperateGroupPasswd(tool_settings=self.hdf_tool, root_path=root_path)
462        # group
463        peripheral_name = "_".join([model_name, "user"])
464        group_file_path = group_passwd.operate_group(name=peripheral_name)
465        # passwd
466        passwd_file_path = group_passwd.operate_passwd(name=peripheral_name)
467        linux_file_path["passwd"] = passwd_file_path
468        linux_file_path["group"] = group_file_path
469        return linux_file_path
470