[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);
+