[Feature][ZXW-130]merge P50U02 version

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: I4f29ec5bb7c59385f23738d2b7ca84e67c100f69
diff --git a/ap/os/linux/linux-3.4.x/lib/Kconfig b/ap/os/linux/linux-3.4.x/lib/Kconfig
old mode 100644
new mode 100755
index ad77fbc..9bbc37c
--- a/ap/os/linux/linux-3.4.x/lib/Kconfig
+++ b/ap/os/linux/linux-3.4.x/lib/Kconfig
@@ -389,4 +389,12 @@
 	  Digital signature verification. Currently only RSA is supported.
 	  Implementation is done using GnuPG MPI library
 
+source "lib/flags_utils/Kconfig"
+
+config TEST_FLAGS_UTILS
+	bool "Test Flags Utils"
+	default n
+	help
+	  Enable test flags utils
+
 endmenu
diff --git a/ap/os/linux/linux-3.4.x/lib/Makefile b/ap/os/linux/linux-3.4.x/lib/Makefile
old mode 100644
new mode 100755
index 0d8821a..519d5e1
--- a/ap/os/linux/linux-3.4.x/lib/Makefile
+++ b/ap/os/linux/linux-3.4.x/lib/Makefile
@@ -149,3 +149,6 @@
 
 $(obj)/crc32table.h: $(obj)/gen_crc32table
 	$(call cmd,crc32)
+
+obj-$(CONFIG_FLAGS_UTILS) += flags_utils/
+obj-$(CONFIG_TEST_FLAGS_UTILS) += test_flags_utils.o
\ No newline at end of file
diff --git a/ap/os/linux/linux-3.4.x/lib/flags_utils/Kconfig b/ap/os/linux/linux-3.4.x/lib/flags_utils/Kconfig
new file mode 100755
index 0000000..232257f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/lib/flags_utils/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+
+config FLAGS_UTILS
+	bool "Flags Utils"
+	default n
+	help
+	  Enable flags utils
diff --git a/ap/os/linux/linux-3.4.x/lib/flags_utils/Makefile b/ap/os/linux/linux-3.4.x/lib/flags_utils/Makefile
new file mode 100755
index 0000000..ce02e6e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/lib/flags_utils/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_FLAGS_UTILS) += flags_utils.o
diff --git a/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_log.h b/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_log.h
new file mode 100755
index 0000000..daddd41
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_log.h
@@ -0,0 +1,11 @@
+#ifndef FLAGS_LOG_H
+#define FLAGS_LOG_H
+
+
+#define flags_debug(fmt, args...) printk(KERN_DEBUG "[flags] [%s-%d] Debug: " fmt "\n", __FUNCTION__, __LINE__, ##args)
+#define flags_info(fmt, args...) printk(KERN_INFO "[flags] [%s-%d] Info: " fmt "\n", __FUNCTION__, __LINE__, ##args)
+#define flags_err(fmt, args...) printk(KERN_ERR "[flags] [%s-%d] Err: " fmt "\n", __FUNCTION__, __LINE__, ##args)
+
+
+#endif  // FLAGS_LOG_H
+
diff --git a/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_utils.c b/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_utils.c
new file mode 100755
index 0000000..1ad9702
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/lib/flags_utils/flags_utils.c
@@ -0,0 +1,476 @@
+/**

+ * @file flags_utils.c

+ * @brief flags·ÖÇø½Ó¿ÚʵÏÖ

+ *

+ * Copyright (C) 2023 Sanechips Technology Co., Ltd.

+ * @author 

+ * 

+ * This program is free software; you can redistribute it and/or modify

+ * it under the terms of the GNU General Public License version 2 as

+ * published by the Free Software Foundation. £¨±ØÑ¡£ºGPLv2 Licence£©

+ *

+ */

+

+

+/*******************************************************************************

+ *                           Include header files                              *

+ ******************************************************************************/

+#include <linux/err.h>

+#include <linux/slab.h>

+#include <linux/mtd/mtd.h>

+

+#include "pub_flags.h"

+

+#include "flags_log.h"

+

+

+extern g_zload_read_only_flag;

+

+

+/*******************************************************************************

+ *                             Macro definitions                               *

+ ******************************************************************************/

+#define MAX_PATH_LEN (256)

+

+#define MTD_NAME_FLAGS "flags"

+

+#define FLAGS_INIT_VALID_BLOCKS_NUM	(8)

+

+#define GOOD_BLOCK (0)

+#define BAD_BLOCK (1)

+

+#define FILE_PATH_PROC_CMDLINE "/proc/cmdline"

+

+#define PROC_CMDLINE_SYSTEM_A_FLAG   "system=system_a"

+#define PROC_CMDLINE_SYSTEM_B_FLAG   "system=system_b"

+

+#define SYSTEM_INDEX_UNKNOWN (-1)

+#define SYSTEM_INDEX_1 (1)

+#define SYSTEM_INDEX_2 (2)

+

+

+/*******************************************************************************

+ *                             Type definitions                                *

+ ******************************************************************************/

+typedef struct

+{

+    unsigned int mtd_totalsize;				// mtd device total size

+    unsigned int mtd_pageperblock;			// mtd device page per block

+    unsigned int mtd_blocksize;				// mtd device block size

+    unsigned int mtd_pagesize;				// mtd device page size

+    unsigned int mtd_oobsize;				// mtd device oob size

+    int parti_file_desc;					// partition update file description

+} partition_mtd_info_t;

+

+

+typedef enum

+{

+    DEVICE_MTD = 0,

+    DEVICE_ZFTL = 1,

+    DEVICE_MTD_BLOCK,

+} device_type_t;

+

+

+/*******************************************************************************

+ *						   Local variable definitions						   *

+ ******************************************************************************/

+

+

+/*******************************************************************************

+ *						  Global variable definitions						   *

+ ******************************************************************************/

+

+

+/*******************************************************************************

+ * 					   Local function declarations							   *

+ ******************************************************************************/

+static int write_flags_info(struct mtd_info *p_mtd_info, int index, unsigned char *content, int len);

+

+static int get_flags_info(T_FLAGS_INFO *p_main, int *p_main_index, T_FLAGS_INFO *p_backup, int *p_backup_index);

+static int set_flags_info(T_FLAGS_INFO *p_flags_info, int *p_main_index, int *p_backup_index);

+

+static void copy_flags_info(T_FLAGS_INFO *dst, T_FLAGS_INFO *src);

+

+

+/*******************************************************************************

+ * 					 Local function implementations 						   *

+ ******************************************************************************/

+static int write_flags_info(struct mtd_info *p_mtd_info, int index, unsigned char *content, int len)

+{

+	int ret = -1;

+    size_t write_len = 0;

+	

+    struct erase_info eraseInfo = {0};

+    loff_t offs = 0;

+	

+    eraseInfo.mtd  = p_mtd_info;

+    eraseInfo.addr = index * p_mtd_info->erasesize;

+    eraseInfo.len  = p_mtd_info->erasesize;

+	

+    offs = (loff_t)index * p_mtd_info->erasesize;

+

+	g_zload_read_only_flag = 1;

+

+	if (0 != mtd_block_isbad(p_mtd_info, offs))

+	{

+		flags_err("block[%d] bad", index);

+		g_zload_read_only_flag = 0;

+		

+		return BAD_BLOCK;

+	}

+

+	if (0 != mtd_erase(p_mtd_info, &eraseInfo))

+	{

+		flags_err("block[%d] erase fail", index);

+		g_zload_read_only_flag = 0;

+		

+		return -1;

+	}

+

+	if (0 != mtd_block_isbad(p_mtd_info, offs))

+	{

+		flags_err("block[%d] bad", index);

+		g_zload_read_only_flag = 0;

+		

+		return BAD_BLOCK;

+	}

+

+	ret = mtd_write(p_mtd_info, index * p_mtd_info->erasesize, len, &write_len, content);

+	if (ret || write_len != len) {

+		flags_err("write failed");

+		g_zload_read_only_flag = 0;

+	

+		return -1;

+	}

+	

+	g_zload_read_only_flag = 0;

+    return 0;

+}

+

+

+static int get_flags_info(T_FLAGS_INFO *p_main, int *p_main_index, T_FLAGS_INFO *p_backup, int *p_backup_index)

+{

+	int index = 0;

+    int good_index = 0;

+	

+    loff_t offs = 0;

+    int block_flag = GOOD_BLOCK;

+	

+	uint32_t block_size = -1;

+	struct mtd_info *mtdInfo = NULL;

+

+	size_t retlen = 0;

+	

+	mtdInfo = get_mtd_device_nm(MTD_NAME_FLAGS);

+	if ((NULL == mtdInfo) || IS_ERR(mtdInfo))

+	{

+		flags_err("get mtd device fail");

+		goto error_close;

+	}

+

+	block_size = mtdInfo->erasesize;

+	

+	flags_debug("erasesize=%u, block_size=%u", mtdInfo->erasesize, block_size);

+

+	for (index = 0; (good_index < 2) && (index * block_size < mtdInfo->size); index++)

+    {

+

+        offs = index * block_size;

+        if (0 == mtd_block_isbad(mtdInfo, offs))

+        {

+            block_flag = GOOD_BLOCK;

+        }

+        else

+        {

+            flags_err("flags block [%d] is bad", index);

+            block_flag =  BAD_BLOCK;

+        }

+

+        if (block_flag == GOOD_BLOCK)

+        {

+        	if (good_index == 0)

+            {

+				*p_main_index = index;

+				

+				if(mtd_read(mtdInfo, offs, sizeof(T_FLAGS_INFO), &retlen, (u_char *)p_main))

+				{

+	                flags_err("main mtd read error");

+					goto error_close;

+				}

+        	}

+            else if (good_index == 1)

+            {

+                *p_backup_index = index;

+				

+				if(mtd_read(mtdInfo, offs, sizeof(T_FLAGS_INFO), &retlen, (u_char *)p_backup))

+				{

+	                flags_err("backup mtd read error");

+					goto error_close;

+				}

+				

+            }

+            else

+            {

+                break;

+            }

+

+            if (retlen != sizeof(T_FLAGS_INFO))

+            {

+                flags_err("read len (%d) != need len (%d)", retlen, sizeof(T_FLAGS_INFO));

+                goto error_close;

+            }

+

+            good_index++;

+        }

+    }

+	

+	if (NULL != mtdInfo)

+	{

+	    put_mtd_device(mtdInfo);

+		mtdInfo = NULL;

+	}

+	

+	return 0;

+

+error_close:

+	if (NULL != mtdInfo)

+	{

+	    put_mtd_device(mtdInfo);

+		mtdInfo = NULL;

+	}

+	

+    return -1;

+}

+

+

+static int set_flags_info(T_FLAGS_INFO *p_flags_info, int *p_main_index, int *p_backup_index)

+{

+    int ret = -1;

+

+	int index = 0;

+    int good_index = 0;

+	

+    int main_index = *p_main_index;

+    int back_index = *p_backup_index;

+	

+    loff_t offs = 0;

+    int block_flag = GOOD_BLOCK;

+	

+	uint32_t block_size = -1;

+	struct mtd_info *mtdInfo = NULL;

+

+    unsigned char *real_write_content = NULL;

+	

+	mtdInfo = get_mtd_device_nm(MTD_NAME_FLAGS);

+	if ((NULL == mtdInfo) || IS_ERR(mtdInfo))

+	{

+		flags_err("get mtd device fail");

+		

+		ret = -1;

+		goto end;

+	}

+

+	block_size = mtdInfo->erasesize;

+	

+	flags_debug("erasesize=%u, block_size=%u", mtdInfo->erasesize, block_size);

+	

+	real_write_content = (unsigned char *)kmalloc(block_size, GFP_KERNEL);

+	if (NULL == real_write_content)

+	{

+		flags_err("malloc block fail");

+		

+		ret = -1;

+		goto end;

+	}

+

+	memset(real_write_content, 0xFF, block_size);

+	memcpy(real_write_content, (char *)p_flags_info, sizeof(T_FLAGS_INFO));

+	

+	flags_info(">>>>> begin to write main flags <<<<<");

+

+	for (index = 0; index * block_size < mtdInfo->size; index++)

+	{

+		if (index == back_index)

+		{

+			continue;

+		}

+

+		ret = write_flags_info(mtdInfo, index, real_write_content, block_size);

+		if (ret == 0)

+		{

+			// д³É¹¦£¬Í˳ö£¬²¢¸üеÚÒ»¿éλÖÃ

+			flags_info("main flags location: [%d]->[%d]", main_index, index);

+			main_index = index;

+			break;

+		}

+		else if (ret == BAD_BLOCK)

+		{

+			// Óöµ½»µ¿é£¬ÏòºóÌøÒ»¿é

+			flags_info("flags block index [%d] is bad", index);

+			continue;

+		}

+		else

+		{

+			flags_err("write main flags fail");

+			main_index = -1;

+			break;

+		}

+	}

+

+	flags_info(">>>>> begin to write backup flags <<<<<");

+

+	for (index = 0; index * block_size < mtdInfo->size; index++)

+	{

+		if (index == main_index)

+		{

+			continue;

+		}

+

+		ret = write_flags_info(mtdInfo, index, real_write_content, block_size);

+		if (ret == 0)

+		{

+			// д³É¹¦£¬Í˳ö£¬²¢¸üеÚÒ»¿éλÖÃ

+			flags_info("backup flags location: [%d]->[%d]", back_index, index);

+			back_index = index;

+			break;

+		}

+		else if (ret == BAD_BLOCK)

+		{

+			// Óöµ½»µ¿é£¬ÏòºóÌøÒ»¿é

+			continue;

+		}

+		else

+		{

+			flags_err("write backup flags fail");

+			back_index = -1;

+			break;

+		}

+	}

+

+	if (main_index == -1 && back_index == -1)

+	{

+		ret = -1;

+		goto end;

+	}

+	else

+	{

+		ret = 0;

+		goto end;

+	}

+

+end:

+	if (NULL != mtdInfo)

+	{

+	    put_mtd_device(mtdInfo);

+		mtdInfo = NULL;

+	}

+	

+	if(NULL != real_write_content)

+	{

+		kfree(real_write_content);

+		real_write_content = NULL;

+	}

+	

+    return ret;

+}

+

+

+static void copy_flags_info(T_FLAGS_INFO *dst, T_FLAGS_INFO *src)

+{

+	dst->magic_start = src->magic_start;

+	

+    dst->boot_fota_flag.boot_to = src->boot_fota_flag.boot_to;

+    dst->boot_fota_flag.fota_status = src->boot_fota_flag.fota_status;

+    dst->boot_fota_flag.system.status = src->boot_fota_flag.system.status;

+    dst->boot_fota_flag.system.try_cnt = src->boot_fota_flag.system.try_cnt;

+    dst->boot_fota_flag.system2.status = src->boot_fota_flag.system2.status;

+    dst->boot_fota_flag.system2.try_cnt = src->boot_fota_flag.system2.try_cnt;

+	

+    dst->boot_env.dualsys_type = src->boot_env.dualsys_type;

+	strncpy(dst->boot_env.system_boot_env, src->boot_env.system_boot_env, sizeof(dst->boot_env.system_boot_env));

+	strncpy(dst->boot_env.system2_boot_env, src->boot_env.system2_boot_env, sizeof(dst->boot_env.system2_boot_env));

+

+	dst->ubifs_status.fs_status = src->ubifs_status.fs_status;

+	strncpy(dst->ubifs_status.fs_mtd_name, src->ubifs_status.fs_mtd_name, sizeof(dst->ubifs_status.fs_mtd_name));

+	strncpy(dst->ubifs_status.fs_ubi_vol_name, src->ubifs_status.fs_ubi_vol_name, sizeof(dst->ubifs_status.fs_ubi_vol_name));

+	

+	dst->magic_end = src->magic_end;

+

+	return;

+}

+

+

+/*******************************************************************************

+ * 					 Global function implementations						   *

+ ******************************************************************************/

+int flags_get(T_FLAGS_INFO *p_flags_info)

+{

+    T_FLAGS_INFO main_flag = {0};

+    T_FLAGS_INFO backup_flag = {0};

+    int main_index = 0;

+    int backup_index = 1;

+

+	if (NULL == p_flags_info)

+	{

+		flags_err("invalid param NULL");

+		return -1;

+	}

+

+    if (get_flags_info(&main_flag, &main_index, &backup_flag, &backup_index) != 0)

+    {

+    	flags_err("get flags info fail");

+        return -1;

+    }

+

+    if ((FLAGS_MAGIC == main_flag.magic_start) && (FLAGS_MAGIC == main_flag.magic_end))

+    {

+        copy_flags_info(p_flags_info, &main_flag);

+        return 0;

+    }

+	

+    if ((FLAGS_MAGIC == backup_flag.magic_start) && (FLAGS_MAGIC == backup_flag.magic_end))

+    {

+        copy_flags_info(p_flags_info, &backup_flag);

+        return 0;

+    }

+

+    flags_err("do not find valid flags info");

+    return -1;

+}

+EXPORT_SYMBOL(flags_get);

+

+int flags_set(T_FLAGS_INFO *p_flags_info)

+{

+	T_FLAGS_INFO main_flag = {0};

+    T_FLAGS_INFO backup_flag = {0};

+    int main_index = 0;

+    int backup_index = 1;

+	

+	if (NULL == p_flags_info)

+	{

+		flags_err("invalid param NULL");

+		return -1;

+	}

+

+	if ((FLAGS_MAGIC != p_flags_info->magic_start) || (FLAGS_MAGIC != p_flags_info->magic_end))

+	{

+		flags_err("invalid magic");

+		return -1;

+	}

+	

+    if (get_flags_info(&main_flag, &main_index, &backup_flag, &backup_index) != 0)

+    {

+    	flags_err("get flags info fail");

+        return -1;

+    }

+

+    if (set_flags_info(p_flags_info, &main_index, &backup_index) != 0)

+    {

+        flags_err("set ubifs status fail");

+        return -1;

+    }

+

+	return 0;

+}

+EXPORT_SYMBOL(flags_set);

+

diff --git a/ap/os/linux/linux-3.4.x/lib/test_flags_utils.c b/ap/os/linux/linux-3.4.x/lib/test_flags_utils.c
new file mode 100755
index 0000000..56ab552
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/lib/test_flags_utils.c
@@ -0,0 +1,218 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+
+#include "pub_flags.h"
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("flags_utils");
+
+
+#define DEVICE_NAME "chardevnode"
+#define DEVICE_MINOR_NUM 2
+#define DEV_MAJOR 0
+#define DEV_MINOR 0
+#define REGDEV_SIZE 3000
+#define LED_NUM 2
+
+#define FLAGS_UTILS_GET	_IOWR('b', 1, T_FLAGS_INFO)
+#define FLAGS_UTILS_SET	_IOWR('b', 2, T_FLAGS_INFO)
+
+
+extern int flags_get(T_FLAGS_INFO *p_flags_info);
+extern int flags_set(T_FLAGS_INFO *p_flags_info);
+
+
+int numdev_major = DEV_MAJOR;
+int numdev_minor = DEV_MINOR;
+
+module_param(numdev_major,int,S_IRUSR);
+module_param(numdev_minor,int,S_IRUSR);
+
+static struct class *myclass;
+
+struct reg_dev
+{
+    char *data;
+    unsigned long size;
+    
+    struct cdev cdev;
+};
+struct reg_dev *my_devices;
+
+
+static int chardevnode_open(struct inode *inodes, struct file *files)
+{
+    printk(KERN_EMERG "chardevnode_open success! \n");
+    return 0;
+}
+static int chardevnode_release (struct inode *inodes, struct file *files)
+{
+    printk(KERN_EMERG "chardevnode_release success! \n");
+    return 0;
+}
+
+static long chardevnode_unlocked_ioctl(struct file *files, unsigned int cmd, unsigned long arg)
+{
+	void __user *ubuf = (void __user *)arg;
+		
+	switch (cmd) {
+		case FLAGS_UTILS_GET:
+			{
+				T_FLAGS_INFO flags_info_get = {0};
+		
+				if (0 != flags_get(&flags_info_get))
+				{
+					printk(KERN_ERR "flags utils ioctl get fail \n");
+					return -EFAULT;
+				}
+				
+				if (copy_to_user(ubuf, &flags_info_get, sizeof(flags_info_get)))
+				{
+					printk(KERN_ERR "flags utils ioctl copy to fail \n");
+					return -EFAULT;
+				}
+				break;
+			}
+		case FLAGS_UTILS_SET:
+			{
+				T_FLAGS_INFO flags_info_set = {0};
+				
+				if (copy_from_user(&flags_info_set, ubuf, sizeof(flags_info_set)))
+				{
+					printk(KERN_ERR "flags utils ioctl copy from fail \n");
+					return -EFAULT;
+				}
+				
+				if (0 != flags_set(&flags_info_set))
+				{
+					printk(KERN_ERR "flags utils ioctl set fail \n");
+					return -EFAULT;
+				}
+				break;
+			}
+		default:
+			{
+				printk(KERN_ERR "flags utils ioctl does not support cmd[0x%x]\n", cmd);
+				return -EOPNOTSUPP;
+			}
+	}
+
+    return 0;
+}
+
+static ssize_t chardevnode_read(struct file *files, char __user *buf , size_t count, loff_t *f_ops)
+{
+    return 0;
+}
+static ssize_t chardevnode_write(struct file *files, const char __user *buf, size_t count, loff_t *f_ops)
+{
+    return 0;
+}
+static loff_t chardevnode_llseek(struct file *files, loff_t offset, int ence)
+{
+    return 0;
+}
+struct file_operations my_fops = {
+    .owner   = THIS_MODULE,
+    .open     = chardevnode_open,
+    .release = chardevnode_release,
+    .unlocked_ioctl = chardevnode_unlocked_ioctl,
+    .read     = chardevnode_read,
+    .write     = chardevnode_write,
+    .llseek     = chardevnode_llseek,
+};
+
+
+static void reg_init_cdev(struct reg_dev *dev,int index){
+    int err;
+    int devno = MKDEV(numdev_major,numdev_minor+index);
+    
+    cdev_init(&dev->cdev,&my_fops);
+    dev->cdev.owner = THIS_MODULE;
+    dev->cdev.ops = &my_fops;
+    
+    err = cdev_add(&dev->cdev,devno,1);
+    if(err){
+        printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);
+    }
+    else{
+        printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);
+    }
+}
+
+
+static int scdev_init(void)
+{
+    int ret = 0,i;
+    dev_t num_dev;
+    
+    printk(KERN_EMERG "numdev_major is %d\n", numdev_major);
+    printk(KERN_EMERG "numdev_minor is %d\n", numdev_minor);
+    
+    if(numdev_major){
+        num_dev = MKDEV(numdev_major,numdev_minor);
+        ret = register_chrdev_region(num_dev, DEVICE_MINOR_NUM, DEVICE_NAME);
+    }
+    else{
+        ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM,DEVICE_NAME);
+        numdev_major = MAJOR(num_dev);
+        printk(KERN_EMERG "adev_region req %d !\n", numdev_major);
+    }
+    if(ret<0){
+        printk(KERN_EMERG "register_chrdev_region req %d is failed!\n", numdev_major);        
+    }
+    myclass = class_create(THIS_MODULE, DEVICE_NAME);
+    
+    my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev), GFP_KERNEL);
+    if(!my_devices){
+        ret = -ENOMEM;
+        goto fail;
+    }
+    memset(my_devices, 0, DEVICE_MINOR_NUM * sizeof(struct reg_dev));
+    
+    for(i=0; i<DEVICE_MINOR_NUM; i++){
+        my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
+        memset(my_devices[i].data,0,REGDEV_SIZE);
+	
+        reg_init_cdev(&my_devices[i],i);
+        device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
+    }
+        
+    printk(KERN_EMERG "scdev_init!\n");
+    return 0;
+
+fail:
+    unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
+    printk(KERN_EMERG "kmalloc is fail!\n");
+    
+    return ret;
+}
+
+static void scdev_exit(void)
+{
+    int i;
+    printk(KERN_EMERG "scdev_exit!\n");
+    
+    for(i=0;i<DEVICE_MINOR_NUM;i++){
+        cdev_del(&(my_devices[i].cdev));
+        device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
+    }
+    class_destroy(myclass);
+    kfree(my_devices);
+
+    unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
+}
+
+
+module_init(scdev_init);
+module_exit(scdev_exit);
+