/* * drivers/param_read_write/param_read_write.c * * hefaxi@filesystems,2015/04/30 * * This program is used to read/write param partition in kernel */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PARAM_PARTITION "/dev/block/bootdevice/by-name/param" #define READ_CHUNK_MAX_SIZE (1024) #define WRITE_CHUNK_MAX_SIZE (1024) static uint default_param_data_dump_size= DEFAULT_PARAM_DUMP_SIZE; typedef struct{ phys_addr_t paddr; size_t size; void *vaddr; void *buffer; struct mutex mutex; }param_ram_zone_t; static DEFINE_MUTEX(param_lock); static bool param_init_done = 0; static param_ram_zone_t param_ram_zone; static int write_param_partition(const char *buf, unsigned long count, loff_t offset) { struct file *filp; mm_segment_t fs; int ret = 0; filp = filp_open(PARAM_PARTITION,O_RDWR|O_SYNC,0); if(IS_ERR(filp)) { ret = PTR_ERR(filp); pr_err("open file %s failed.(%d)\n",PARAM_PARTITION,ret); return ret; } fs = get_fs(); set_fs(get_ds()); ret = filp->f_op->llseek(filp, offset, SEEK_SET); if(ret < 0){ pr_err("%s: llseek failed.(%d)\n",__func__,ret); goto out; } //ret = filp->f_op->write(filp,(char __user *)buf,count,&filp->f_pos); ret = __vfs_write(filp, (char __user *)buf, count, &filp->f_pos); out: set_fs(fs); filp_close(filp,NULL); return ret; } int get_param_by_index_and_offset(uint32 sid_index, uint32 offset, void * buf, int length) { int ret = length; uint32 file_offset; mutex_lock(¶m_ram_zone.mutex); pr_info("%s[%d] sid_index = %d offset = %d buf = %p length = %d\n", __func__, __LINE__,sid_index,offset,buf,length); file_offset = PARAM_SID_LENGTH*sid_index+ offset; if (buf && ((offset + length) <= PARAM_SID_LENGTH) && (file_offset + length) <= param_ram_zone.size) memcpy(buf,(param_ram_zone.buffer +file_offset), length); else{ pr_info("%s:invaild argument, sid_index=%d offset=%d buf=%p length=%d\n", __func__, sid_index, offset, buf, length); ret = -EINVAL; } mutex_unlock(¶m_ram_zone.mutex); return ret; } EXPORT_SYMBOL(get_param_by_index_and_offset); int set_param_by_index_and_offset(uint32 sid_index, uint32 offset, void * buf, int length) { int ret; uint32 file_offset; mutex_lock(¶m_ram_zone.mutex); pr_info("%s[%d]sid_index = %d offset = %d buf = %p length = %d\n", __func__, __LINE__,sid_index,offset,buf,length); file_offset = PARAM_SID_LENGTH*sid_index + offset; if (buf && ((offset + length) <= PARAM_SID_LENGTH) && (file_offset + length) <= param_ram_zone.size) memcpy((param_ram_zone.buffer+file_offset),buf,length); else{ pr_info("%s:invaild argument,sid_index=%d offset=%d buf=%p length=%d\n", __func__,sid_index,offset,buf,length); ret = -EINVAL; goto out; } ret = write_param_partition((param_ram_zone.buffer+file_offset), length,file_offset); if ( ret < 0){ pr_info("Error write param partition.(%d)\n",ret); } out: mutex_unlock(¶m_ram_zone.mutex); return ret; } EXPORT_SYMBOL(set_param_by_index_and_offset); static void *persistent_ram_vmap(phys_addr_t start, size_t size) { struct page **pages; phys_addr_t page_start; unsigned int page_count; pgprot_t prot; unsigned int i; void *vaddr; page_start = start - offset_in_page(start); page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); /* prot = pgprot_noncached(PAGE_KERNEL); */ prot = pgprot_writecombine(PAGE_KERNEL); pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL); if (!pages) { pr_err("%s: Failed to allocate array for %u pages\n", __func__, page_count); return NULL; } for (i = 0; i < page_count; i++) { phys_addr_t addr = page_start + i * PAGE_SIZE; pages[i] = pfn_to_page(addr >> PAGE_SHIFT); } vaddr = vmap(pages, page_count, VM_MAP, prot); kfree(pages); return vaddr; } static int param_ram_buffer_map(phys_addr_t start, phys_addr_t size, param_ram_zone_t *prz) { prz->paddr = start; prz->size = size; prz->vaddr = persistent_ram_vmap(start, size); if (!prz->vaddr) { pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, (unsigned long long)size, (unsigned long long)start); return -ENOMEM; } prz->buffer = prz->vaddr + offset_in_page(start); return 0; } static ssize_t param_read(struct file *file, char __user *buff, size_t count, loff_t *pos) { void * temp_buffer; int chunk_sz; int copied; int left; int ret; if (mutex_lock_interruptible(¶m_lock)) return -ERESTARTSYS; chunk_sz = count < READ_CHUNK_MAX_SIZE ? count : READ_CHUNK_MAX_SIZE; temp_buffer = kzalloc(chunk_sz, GFP_KERNEL); if (temp_buffer == NULL) return -ENOMEM; left = count; copied = 0; while (left) { chunk_sz = (left <= READ_CHUNK_MAX_SIZE) ? left : READ_CHUNK_MAX_SIZE; ret = get_param_by_index_and_offset(*pos/PARAM_SID_LENGTH, *pos%PARAM_SID_LENGTH, temp_buffer, chunk_sz); if (ret < 0) { pr_err("get_param_by_index_and_offset fail %d\n", ret); goto out; } if (copy_to_user(buff + copied, temp_buffer, chunk_sz)) { ret = -EFAULT; pr_info("copy_to_user failure\n"); goto out; } *pos += chunk_sz; left -= chunk_sz; copied += chunk_sz; } out: kfree(temp_buffer); mutex_unlock(¶m_lock); return copied; } static ssize_t param_write(struct file *file, const char __user *buff, size_t count, loff_t *pos) { void * temp_buffer; int chunk_sz; int written; int left; int ret; if (mutex_lock_interruptible(¶m_lock)) return -ERESTARTSYS; chunk_sz = count < WRITE_CHUNK_MAX_SIZE ? count : WRITE_CHUNK_MAX_SIZE; temp_buffer = kzalloc(chunk_sz, GFP_KERNEL); if (temp_buffer == NULL) return -ENOMEM; left = count; written = 0; while (left > 0) { chunk_sz = (left <= WRITE_CHUNK_MAX_SIZE) ? left : WRITE_CHUNK_MAX_SIZE; ret = copy_from_user(temp_buffer, buff + written, chunk_sz); if (ret < 0) { pr_info("copy_from_user failure %d\n", ret); goto out; } ret = set_param_by_index_and_offset(*pos / PARAM_SID_LENGTH, *pos % PARAM_SID_LENGTH, temp_buffer, chunk_sz); if (ret < 0) { pr_err("set_param_by_index_and_offset failure %d\n", ret); goto out; } *pos += chunk_sz; left -= chunk_sz; written += chunk_sz; } out: kfree(temp_buffer); mutex_unlock(¶m_lock); return written; } static const struct file_operations param_fops = { .owner = THIS_MODULE, .read = param_read, .write = param_write, .llseek = default_llseek, }; struct miscdevice param_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "param", .fops = ¶m_fops, }; static int __init param_core_init(void) { int i; if(param_ram_buffer_map((phys_addr_t)param_ram_zone.paddr, param_ram_zone.size, (param_ram_zone_t *)¶m_ram_zone)){ pr_err("param_ram_buffer_map failred\n"); return -1; } mutex_init(¶m_ram_zone.mutex); for (i = 0; i < NUM_PARAM_PLAINTEXT_SEGMENT; i++) { break;//do not dump param printk("===dump chunk %d===\n", i); print_hex_dump (KERN_ERR, "",DUMP_PREFIX_OFFSET,16, 4, param_ram_zone.buffer +1024*i, default_param_data_dump_size,1); } param_init_done= 1; return 0; } pure_initcall(param_core_init); static int __init param_device_init(void) { int ret; ret = misc_register(¶m_misc); if(ret){ pr_err("misc_register failure %d\n",ret); return -1; } return ret; } device_initcall(param_device_init); void init_param_mem_base_size(phys_addr_t base, unsigned long size) { param_ram_zone.paddr = base; param_ram_zone.size = size; } EXPORT_SYMBOL(init_param_mem_base_size); /* *Add more function here * */ int restart_08_count; int add_restart_08_count(void) { int ret; ret = get_param_by_index_and_offset(9, 0x15c, &restart_08_count, sizeof(restart_08_count)); restart_08_count = restart_08_count + 1; ret = set_param_by_index_and_offset(9, 0x15c, &restart_08_count, sizeof(restart_08_count)); if (ret < 0) pr_info("%s[%d] failed!\n", __func__, __LINE__); return ret; } EXPORT_SYMBOL(add_restart_08_count); static int param_get_restart_08_count(char *val, struct kernel_param *kp) { int cnt = 0; int ret; ret = get_param_by_index_and_offset(9, 0x15c, &restart_08_count, sizeof(restart_08_count)); if (ret < 0) pr_info("%s[%d] failed!\n", __func__, __LINE__); cnt = snprintf(val, 4, "%d", restart_08_count); return cnt; } module_param_call(restart_08_count, NULL, param_get_restart_08_count, &restart_08_count, 0644); int restart_other_count=0; int add_restart_other_count(void) { int ret; ret = get_param_by_index_and_offset(9, 0x160, &restart_other_count, sizeof(restart_other_count)); restart_other_count = restart_other_count + 1; ret = set_param_by_index_and_offset(9, 0x160, &restart_other_count, sizeof(restart_other_count)); if (ret < 0) pr_info("%s[%d] failed!\n", __func__, __LINE__); return ret; } EXPORT_SYMBOL(add_restart_other_count); static int param_get_restart_other_count(char *val, struct kernel_param *kp) { int cnt = 0; int ret; ret = get_param_by_index_and_offset(9, 0x160, &restart_other_count, sizeof(restart_other_count)); if (ret < 0) pr_info("%s[%d] failed!\n", __func__, __LINE__); cnt = snprintf(val, 4, "%d", restart_other_count); return cnt; } module_param_call(restart_other_count, NULL, param_get_restart_other_count, &restart_other_count, 0644); //end