/* notify work */
static void dsm_work_func(struct work_struct *work)
{
	int i;
	struct dsm_client *client;

	DSM_LOG_DEBUG("%s enter\n", __func__);
	mutex_lock(&g_dsm_server.mtx_lock);
	smp_rmb();
	for(i=0; i<CLIENT_SIZE; i++){
		/* whether it is a valid client */
		if(test_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i])){
			DSM_LOG_DEBUG("No.%d client name %s flag 0x%lx\n", i,
				g_dsm_server.client_list[i]->client_name, g_dsm_server.client_flag[i]);
			/* whether the client report error msg, clear a bit and return its old value */
			if(!test_and_clear_bit(DSM_CLIENT_NOTIFY_BIT, &g_dsm_server.client_flag[i]))
				continue;

			client = g_dsm_server.client_list[i];
			if(client == NULL){
				DSM_LOG_INFO("%d client is null client.\n",i);
				continue;
			}
			/* wake up wait queue */
			wake_up_interruptible_all(&client->waitq);
			DSM_LOG_INFO("%s finish notify\n", client->client_name);
		}
	}
	mutex_unlock(&g_dsm_server.mtx_lock);
	DSM_LOG_DEBUG("%s exit\n", __func__);

	return;
}
//cnotify format: |client name|,|error no|,|contents|
static ssize_t dsm_cnotify_store(struct device *dev,
                                 struct device_attribute *attr, const char *buf, size_t count)
{
    char client_name[CLIENT_NAME_LEN]= {0};
    int size;
    int error_no = 0;
    struct dsm_client *client = NULL;
    char *strings = NULL;
    char *ptr;

    DSM_LOG_DEBUG("%s enter\n",__func__);
    strings = (char *)kzalloc(count, GFP_KERNEL);
    if(!strings) {
        DSM_LOG_ERR("dsm write malloc failed\n");
        goto out;
    }

    memcpy(strings, buf, count);
    /*get client name*/
    ptr = dsm_strtok(strings, ",");
    if(ptr) {
        size = strlen(ptr);
        size = (size < CLIENT_NAME_LEN) ? size : (CLIENT_NAME_LEN - 1);
        memcpy(client_name, ptr, size);
    }
    /*get error no*/
    ptr = dsm_strtok(NULL, ",");
    if(ptr)
        error_no = dsm_atoi(ptr);
    /*get notify content*/
    ptr = dsm_strtok(NULL, NULL);

    DSM_LOG_INFO("client name - %s, error no - %d\n", client_name, error_no);
    if(ptr)
        DSM_LOG_INFO("content - %s\n", ptr);

    client = dsm_find_client(client_name);
    if(client && (!dsm_client_ocuppy(client))) {
        DSM_LOG_DEBUG("dsm write find client - %s\n", client_name);
        if(ptr)
            dsm_client_copy(client, ptr, strlen(ptr));
        dsm_client_notify(client, error_no);
    } else
        DSM_LOG_INFO("dsm notify can't find client - %s\n", client_name);

out:
    if(strings)
        kfree(strings);
    DSM_LOG_DEBUG("%s exit\n",__func__);
    return count;
}
/* dsm module init */
static int __init dsm_init(void)
{
	int ret = -EIO;

	memset(&g_dsm_server, 0, sizeof(struct dsm_server));
	g_dsm_server.server_state = DSM_SERVER_UNINIT;
	mutex_init(&g_dsm_server.mtx_lock);

	g_dsm_server.dsm_wq = create_singlethread_workqueue("dsm_wq");
	if (IS_ERR(g_dsm_server.dsm_wq)){
		DSM_LOG_ERR("alloc workqueue failed\n");
		goto out;
	}

	INIT_WORK(&dsm_work, dsm_work_func);

	ret = misc_register(&dsm_miscdev);
	if(ret){
		DSM_LOG_ERR("misc register failed\n");
		goto out;
	}

	/* set server status as ready, client can registe */
	g_dsm_server.server_state = DSM_SERVER_INITED;

	/* registe public client */
	dsm_register_public_client();

	/* init write semaphore for write func*/
	sema_init(&dsm_wbs, 1);

out:
	DSM_LOG_INFO("%s called, ret %d\n", __func__, ret);
	return ret;
}
static ssize_t dsm_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char client_name[CLIENT_NAME_LEN]={0};
	int size;
	struct dsm_client *client = NULL;
	char *buff = NULL;
	char *ptr;

	DSM_LOG_DEBUG("%s enter\n",__func__);
	buff = (char *)kzalloc(count, GFP_KERNEL);
	if(!buff){
		DSM_LOG_ERR("dsm write malloc failed\n");
		goto out;
	}

	if(copy_from_user(buff, buf, count)){
		DSM_LOG_ERR("dsm write copy failed\n");
		goto out;
	}

	ptr = buff;
	while(*ptr){
		if(*ptr == '\n')
			break;
		ptr++;
	}

	if(*ptr == '\n'){
		size = ptr - buff;
		size = (size < CLIENT_NAME_LEN) ? size : (CLIENT_NAME_LEN - 1);
		memcpy(client_name, buff, size);
		client = dsm_find_client(client_name);
		if(client && (!dsm_client_ocuppy(client))){
			DSM_LOG_DEBUG("dsm write find client - %s\n", client_name);
			ptr++;//ignore '/n'
			size = count - (ptr - buff);
			dsm_client_copy(client, ptr, size);
			dsm_client_notify(client, 20203);
		}else
			DSM_LOG_INFO("dsm write can't find client - %s\n", client_name);
	}else
		DSM_LOG_ERR("dsm write can't find client name\n");

out:
	if(buff)
		kfree(buff);
	DSM_LOG_DEBUG("%s exit\n",__func__);
	return count;
}
static int __init dsm_init(void)
{
	int ret = -EIO;
	int i;

	memset(&g_dsm_server, 0, sizeof(struct dsm_server));
	g_dsm_server.server_state = DSM_SERVER_UNINIT;
	mutex_init(&g_dsm_server.mtx_lock);

	g_dsm_server.dsm_wq = create_singlethread_workqueue("dsm_wq");
	if (IS_ERR(g_dsm_server.dsm_wq)){
		DSM_LOG_ERR("alloc workqueue failed\n");
		goto out;
	}

	INIT_WORK(&dsm_work, dsm_work_func);

	ret = misc_register(&dsm_miscdev);
	if(ret){
		DSM_LOG_ERR("misc register failed\n");
		goto out;
	}

	g_dsm_server.server_state = DSM_SERVER_INITED;

	for (i = 0; i < ARRAY_SIZE(dsm_interface_attrs); i++) {
		ret = device_create_file(dsm_miscdev.this_device,
				&dsm_interface_attrs[i]);
		if (ret < 0)
			DSM_LOG_ERR("creating sysfs attribute %s failed: %d\n",
					dsm_interface_attrs[i].attr.name, ret);
	}

	dsm_register_public_client();

out:
	DSM_LOG_INFO("%s called, ret %d\n", __func__, ret);
	return ret;
}
static void dsm_work_func(struct work_struct *work)
{
    int i;
    struct dsm_client *client;

    DSM_LOG_DEBUG("%s enter\n", __func__);
    mutex_lock(&g_dsm_server.mtx_lock);
    smp_rmb();
    for(i=0; i<CLIENT_SIZE; i++) {
        if(test_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i])) {
            DSM_LOG_DEBUG("No.%d client name %s flag 0x%lx\n", i,
                          g_dsm_server.client_list[i]->client_name, g_dsm_server.client_flag[i]);
            if(!test_and_clear_bit(DSM_CLIENT_NOTIFY_BIT, &g_dsm_server.client_flag[i]))
                continue;
            client = g_dsm_server.client_list[i];
            wake_up_interruptible_all(&client->waitq);
            DSM_LOG_INFO("%s finish notify\n", client->client_name);
        }
    }
    mutex_unlock(&g_dsm_server.mtx_lock);
    DSM_LOG_DEBUG("%s exit\n", __func__);

    return;
}
static long dsm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    void __user *argp = (void __user *)arg;
    struct dsm_client *client = (struct dsm_client *)file->private_data;
    long ret = 0;
    int error = 0;
    char buff[CLIENT_NAME_LEN]= {0};
    struct dsm_extern_client tmp_ext_client;

    DSM_LOG_DEBUG("%s enter,\n",__func__);

    switch (cmd) {
    case DSM_IOCTL_GET_CLIENT_COUNT:
        mutex_lock(&g_dsm_server.mtx_lock);
        error = g_dsm_server.client_count;
        mutex_unlock(&g_dsm_server.mtx_lock);
        DSM_LOG_INFO("client count :%d\n", error);
        ret = copy_int_to_user(argp, error);
        break;
    case DSM_IOCTL_BIND:
        if (copy_from_user(buff, argp, CLIENT_NAME_LEN)) {
            DSM_LOG_ERR("copy from user failed\n");
            ret = -EFAULT;
        } else {
            DSM_LOG_DEBUG("try bind client %s\n", buff);
            client = dsm_find_client(buff);
            if(client) {
                dsm_bind_client(client);
                file->private_data = (void *)client;
            } else {
                ret = -ENXIO;
            }
        }
        break;
    case DSM_IOCTL_POLL_CLIENT_STATE:
        if(client && client->cops && client->cops->poll_state) {
            error = client->cops->poll_state();
            DSM_LOG_INFO("poll %s state result :%d\n", client->client_name, error);
            ret = copy_int_to_user(argp, error);
        }
        else {
            DSM_LOG_ERR("dsm client not bound or poll not support\n");
            ret = -ENXIO;
        }
        break;
    case DSM_IOCTL_FORCE_DUMP:
        if (copy_from_user(buff, argp, UINT_BUF_MAX)) {
            DSM_LOG_ERR("copy from user failed\n");
            ret = -EFAULT;
        } else {
            if(client && client->cops && client->cops->dump_func) {
                if(!dsm_client_ocuppy(client)) {
                    client->error_no = dsm_atoi(buff);
                    client->used_size = client->cops->dump_func(client->error_no,
                                        (void *)client->dump_buff, (int)client->buff_size);
                    set_bit(CBUFF_READY_BIT, &client->buff_flag);
                }
                else {
                    DSM_LOG_INFO("client %s's buff ocuppy failed\n", client->client_name);
                    ret = -EBUSY;
                }
            }
            else {
                DSM_LOG_ERR("dsm client not bound or dump not support\n");
                ret = -ENXIO;
            }
        }
        break;
    case DSM_IOCTL_GET_CLIENT_ERROR:
        if(client)
            ret = copy_int_to_user(argp, client->error_no);
        else {
            DSM_LOG_ERR("dsm find client failed\n");
            ret = -ENXIO;
        }
        break;
    case DSM_IOCTL_GET_DEVICE_NAME:
        if (client && client->device_name) {
            ret = copy_to_user(argp, client->device_name, DSM_MAX_DEVICE_NAME_LEN);
        } else {
            ret = -ENXIO;
        }
        break;
    case DSM_IOCTL_GET_IC_NAME:
        if (client && client->ic_name) {
            ret = copy_to_user(argp, client->ic_name, DSM_MAX_IC_NAME_LEN);
        } else {
            ret = -ENXIO;
        }
        break;
    case DSM_IOCTL_GET_MODULE_NAME:
        if (client && client->module_name) {
            ret = copy_to_user(argp, client->module_name, DSM_MAX_MODULE_NAME_LEN);
        } else {
            ret = -ENXIO;
        }
        break;
    case DSM_IOCTL_REGISTER_EXTERN_CLIENT:
        if (copy_from_user(&tmp_ext_client, (struct dsm_extern_client *)arg,
                           sizeof(struct dsm_extern_client))) {
            ret = -EFAULT;
        } else {
            dsm_register_extern_client(&tmp_ext_client);
        }
        break;
    default:
        DSM_LOG_ERR("unknown ioctl command :%d\n", cmd);
        ret = -EINVAL;
        break;
    }

    DSM_LOG_DEBUG("%s exit\n",__func__);
    return ret;
}
static ssize_t dsm_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    char client_name[CLIENT_NAME_LEN]= {'\0'};
    int size, error_no = 0;
    struct dsm_client *client = NULL;
    char *buff = NULL;
    char *ptr = NULL;
#ifdef CONFIG_HUAWEI_SDCARD_VOLD
    char  err_string[20] = {0};
    int   err;
#endif
    DSM_LOG_DEBUG("%s enter\n",__func__);
    buff = (char *)kzalloc(count, GFP_KERNEL);
    if(!buff) {
        DSM_LOG_ERR("dsm write malloc failed\n");
        goto out;
    }

    if(copy_from_user(buff, buf, count)) {
        DSM_LOG_ERR("dsm write copy failed\n");
        goto out;
    }

    /*get client name*/
    ptr = dsm_strtok(buff, ",");
    if(ptr) {
        size = strlen(ptr);
        size = (size < CLIENT_NAME_LEN) ? size : (CLIENT_NAME_LEN - 1);
        memcpy(client_name, ptr, size);
    }
    /*get error no*/
    ptr = dsm_strtok(NULL, ",");
    if(ptr) {
        error_no = dsm_atoi(ptr);
    }

    /*get notify content*/
    ptr = dsm_strtok(NULL, NULL);
    DSM_LOG_INFO("client name - %s, error no - %d\n", client_name, error_no);
    if(ptr) {
        DSM_LOG_INFO("content - %s\n", ptr);
    }

    client = dsm_find_client(client_name);
#ifdef CONFIG_HUAWEI_SDCARD_VOLD
    if(client && !strncasecmp("sdcard_vold",client_name,size)&&(!dsm_client_ocuppy(client)))
    {
        DSM_LOG_DEBUG("dsm write find sdcard_vold\n");
        ptr++;
        while(*ptr) {
            if(*ptr == '\n')
                break;
            ptr++;
        }

        if(*ptr == '\n')
        {

            memcpy(err_string,ptr-SDCARD_ERR_LEN,SDCARD_ERR_LEN);
            err_string[SDCARD_ERR_LEN] = '\0';
            sscanf(err_string,"%d",&err);
            dsm_client_copy(client, ptr+1, (count - (ptr+1-buff)));
            dsm_client_notify(client, err);
        }

    }
    else if(client && (!dsm_client_ocuppy(client))) {
#else
    if(client && (!dsm_client_ocuppy(client))) {
#endif
        DSM_LOG_DEBUG("dsm write find client - %s\n", client_name);
        if(ptr)
            dsm_client_copy(client, ptr, strlen(ptr));
        dsm_client_notify(client, error_no);
    } else {
        DSM_LOG_INFO("dsm notify can't find client - %s\n", client_name);
    }

out:
    if(buff)
        kfree(buff);

    DSM_LOG_DEBUG("%s exit\n",__func__);
    return count;
}

static unsigned int dsm_poll(struct file *file, poll_table *wait)
{
    struct dsm_client *client = file->private_data;
    unsigned int mask = 0;

    DSM_LOG_DEBUG("%s enter\n",__func__);
    if(!client) {
        DSM_LOG_ERR("dsm can't poll without client\n");
        goto out;
    }
    DSM_LOG_DEBUG("client name :%s\n", client->client_name);
    poll_wait(file, &client->waitq, wait);
    if(test_bit(CBUFF_READY_BIT, &client->buff_flag))
        mask = POLLIN | POLLRDNORM;

out:
    DSM_LOG_DEBUG("%s exit, mask:%d\n",__func__, mask);
    return mask;
}

static int dsm_open(struct inode *inode, struct file *file)
{
    DSM_LOG_DEBUG("%s enter\n",__func__);
    file->private_data = NULL;
    DSM_LOG_DEBUG("%s exit\n",__func__);
    return 0;
}
struct dsm_client *dsm_register_client (struct dsm_dev *dev)
{
    int i;
    int size;
    int conflict = -1;
    struct dsm_client *ptr = NULL;

    if(g_dsm_server.server_state != DSM_SERVER_INITED) {
        DSM_LOG_ERR("dsm server uninited\n");
        goto out;
    }

    if(dev == NULL) {
        DSM_LOG_ERR("dsm_dev is NULL\n");
        goto out;
    }

    smp_rmb();
    if(g_dsm_server.client_count < CLIENT_SIZE) {
        ptr = (struct dsm_client *)kzalloc((sizeof(struct dsm_client)+dev->buff_size), GFP_KERNEL);
        if(!ptr) {
            DSM_LOG_ERR("clients malloc failed\n");
            goto out;
        }

        mutex_lock(&g_dsm_server.mtx_lock);
        for(i=0; i<CLIENT_SIZE; i++) {
            if(!test_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i]))
                break;
            if (dev->name) {
                conflict = strncmp(g_dsm_server.client_list[i]->client_name, dev->name,
                                   CLIENT_NAME_LEN);
                if(!conflict) {
                    DSM_LOG_ERR("new client %s conflict with No.%d client %s\n",
                                dev->name, i, g_dsm_server.client_list[i]->client_name);
                    break;
                }
            } else {
                DSM_LOG_ERR("Please specify the dsm device name!\n");
                kfree(ptr);
                ptr = NULL;
                goto out;
            }
        }

        if(i < CLIENT_SIZE && conflict) {
            ptr->client_name = dev->name;
            if (dev->device_name) {
                ptr->device_name = dev->device_name;
            }
            if (dev->ic_name) {
                ptr->ic_name = dev->ic_name;
            }
            if (dev->module_name) {
                ptr->module_name = dev->module_name;
            }

            ptr->client_id = i;
            ptr->cops = dev->fops;
            ptr->buff_size = dev->buff_size;
            init_waitqueue_head(&ptr->waitq);
            g_dsm_server.client_list[i] = ptr;
            set_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i]);
            g_dsm_server.client_count++;
            DSM_LOG_INFO("client %s register success!\n", ptr->client_name);
            smp_wmb();
        } else {
            DSM_LOG_ERR("clients register failed, index %d, conflict %d\n", i, conflict);
            kfree(ptr);
            ptr = NULL;
        }
        mutex_unlock(&g_dsm_server.mtx_lock);
    }
    else
        DSM_LOG_INFO("clients has full\n");

out:
    return ptr;
}
/* sysfs write function */
static ssize_t dsm_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char client_name[CLIENT_NAME_LEN]={0};
	int size;
	struct dsm_client *client = NULL;
	char *buff = NULL;
	char *ptr;
	char  err_string[20] = {0};
	int   err;
	DSM_LOG_INFO("%s enter\n",__func__);


	/* try to get control of the write buffer */
	if (down_trylock(&dsm_wbs)) {
		/* somebody else has it now;
		 * if we're non-blocking, then exit...
		 */
		if (file->f_flags & O_NONBLOCK) {
			return -EAGAIN;
		}
		/* ...or if we want to block, then do so here */
		if (down_interruptible(&dsm_wbs)) {
			/* something went wrong with wait */
			return -ERESTARTSYS;
		}
	}


	buff = (char *)kzalloc(count, GFP_KERNEL);
	if(!buff){
		DSM_LOG_ERR("dsm write malloc failed\n");
		goto out;
	}

	if(copy_from_user(buff, buf, count)){
		DSM_LOG_ERR("dsm write copy failed\n");
		goto out;
	}

	buff[count-1] = '\0';
	ptr = buff;
	while(*ptr){
		if(*ptr == '\n')
			break;
		ptr++;
	}

	/* get the client name */
	if(*ptr == '\n')
	{
		size = ptr - buff;
		size = (size < CLIENT_NAME_LEN) ? size : (CLIENT_NAME_LEN - 1);
		memcpy(client_name, buff, size);
		DSM_LOG_INFO( "%s client name is: %s \n", __func__ ,client_name );
		client = dsm_find_client(client_name);
		if( client )
		{
			/* found client name */
			DSM_LOG_DEBUG("dsm write find client - %s\n", client_name);

			ptr++;
			while(*ptr)
			{
				if(*ptr == '\n')
					break;
				ptr++;
			}

			/* get error number */
			if(*ptr == '\n')
			{
				memcpy(err_string,ptr-DSM_ERR_LEN,DSM_ERR_LEN);
				err_string[DSM_ERR_LEN] = '\0';
				sscanf(err_string,"%d",&err);
				DSM_LOG_INFO( "%s error number is: %d \n", __func__ ,err );
				/* judge if the err number is legal */
				if( (err >= DMS_ERR_NUM_MIN ) && (err < DMS_ERR_NUM_MAX) )
				{
					/* report the error */
					dsm_client_copy(client, ptr+1, (count - (ptr+1-buff)));
					dsm_client_notify(client, err);
				}
				else
				{
					DSM_LOG_ERR("dsm write err number is not legal! err:%d\n", err);
				}
			}
		}
		else
		{
			DSM_LOG_INFO("dsm write can't find client - %s\n", client_name);
		}
	}
	else
	{
		DSM_LOG_ERR("dsm write can't find client name\n");
	}

out:
	if(buff)
		kfree(buff);
	DSM_LOG_DEBUG("%s exit\n", __func__);
	/* release the write buffer and wake anyone who's waiting for it */
	up(&dsm_wbs);
	return count;
}
/* registe client on server */
struct dsm_client *dsm_register_client (struct dsm_dev *dev)
{
	int i;
	int size;
	int conflict = -1;
	struct dsm_client *ptr = NULL;

	if(g_dsm_server.server_state != DSM_SERVER_INITED){
		DSM_LOG_ERR("dsm server uninited\n");
		goto out;
	}

	if(dev == NULL){
		DSM_LOG_ERR("dsm_dev is NULL\n");
		goto out;
	}

	/* memory barrier */
	smp_rmb();
	/* whether client list is full */
	if(g_dsm_server.client_count < CLIENT_SIZE){
		/* malloc memory for this client */
		ptr = (struct dsm_client *)kzalloc((sizeof(struct dsm_client)+dev->buff_size), GFP_KERNEL);
		if(!ptr){
			DSM_LOG_ERR("clients malloc failed\n");
			goto out;
		}

		mutex_lock(&g_dsm_server.mtx_lock);
		/* try to find a free location on server */
		for(i=0; i<CLIENT_SIZE; i++){
			/* whether the client is free */
			if(!test_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i]))
				break;
			/* if client is not free,whether a same client is exist */
			conflict = strncmp(g_dsm_server.client_list[i]->client_name, dev->name, CLIENT_NAME_LEN);
			if(!conflict){
				DSM_LOG_ERR("new client %s conflict with No.%d client %s\n",
					dev->name, i, g_dsm_server.client_list[i]->client_name);
				break;
			}
		}

		/* init a client */
		if(i < CLIENT_SIZE && conflict){
			size = strlen(dev->name);
			size = (size < CLIENT_NAME_LEN) ? size : (CLIENT_NAME_LEN - 1);
			memcpy(ptr->client_name, dev->name, size);	// need add a end symbol? size+1?
			ptr->client_id = i;
			ptr->cops = dev->fops;
			ptr->buff_size = dev->buff_size;
			init_waitqueue_head(&ptr->waitq);
			g_dsm_server.client_list[i] = ptr;
			set_bit(DSM_CLIENT_VAILD_BIT, &g_dsm_server.client_flag[i]);
			g_dsm_server.client_count++;
			smp_wmb();
		}else{
			/* if a same client is exist, donot registe */
			DSM_LOG_ERR("clients register failed, index %d, conflict %d\n", i, conflict);
			kfree(ptr);
			ptr = NULL;
		}
		mutex_unlock(&g_dsm_server.mtx_lock);
	}
	else
		DSM_LOG_INFO("clients has full\n");

out:
	return ptr;
}