| /* |
| * sec_battery_misc.c |
| * Samsung Mobile Battery Misc Driver |
| * Author: Yeongmi Ha <yeongmi86.ha@samsung.com> |
| * Copyright (C) 2018 Samsung Electronics |
| * |
| * |
| * 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. |
| */ |
| #include <linux/miscdevice.h> |
| #include <linux/fs.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <linux/device.h> |
| #include <linux/poll.h> |
| |
| #include "include/sec_battery.h" |
| #include "include/sec_battery_misc.h" |
| |
| static struct sec_bat_misc_dev *c_dev; |
| |
| #define SEC_BATT_MISC_DBG 1 |
| #define MAX_BUF 255 |
| #define NODE_OF_MISC "batt_misc" |
| #define BATT_IOCTL_SWAM _IOWR('B', 0, struct swam_data) |
| |
| static inline int _lock(atomic_t *excl) |
| { |
| if (atomic_inc_return(excl) == 1) |
| return 0; |
| else { |
| atomic_dec(excl); |
| return -1; |
| } |
| } |
| |
| static inline void _unlock(atomic_t *excl) |
| { |
| atomic_dec(excl); |
| } |
| |
| static int sec_bat_misc_open(struct inode *inode, struct file *file) |
| { |
| int ret = 0; |
| |
| pr_info("%s %s + open success\n",WC_AUTH_MSG, __func__); |
| if (!c_dev) { |
| pr_err("%s %s - error : c_dev is NULL\n", WC_AUTH_MSG, __func__); |
| ret = -ENODEV; |
| goto err; |
| } |
| |
| if (_lock(&c_dev->open_excl)) { |
| pr_err("%s %s - error : device busy\n", WC_AUTH_MSG, __func__); |
| ret = -EBUSY; |
| goto err; |
| } |
| |
| #if 0 |
| /* check if there is some connection */ |
| if (!c_dev->swam_ready()) { |
| _unlock(&c_dev->open_excl); |
| pr_err("%s - error : swam is not ready\n", __func__); |
| ret = -EBUSY; |
| goto err; |
| } |
| #endif |
| pr_info("%s %s- open success\n", WC_AUTH_MSG, __func__); |
| |
| return 0; |
| err: |
| return ret; |
| } |
| |
| static int sec_bat_misc_close(struct inode *inode, struct file *file) |
| { |
| if (c_dev) |
| _unlock(&c_dev->open_excl); |
| //c_dev->swam_close(); |
| pr_info("%s %s - close success\n", WC_AUTH_MSG, __func__); |
| return 0; |
| } |
| |
| static int send_swam_message(void *data, int size) |
| { |
| int ret; |
| |
| ret = c_dev->swam_write(data, size); |
| pr_info("%s %s - size : %d, ret : %d\n", WC_AUTH_MSG, __func__, size, ret); |
| return ret; |
| } |
| |
| static int receive_swam_message(void *data, int size) |
| { |
| int ret; |
| |
| ret = c_dev->swam_read(data); |
| pr_info("%s %s - size : %d, ret : %d\n", WC_AUTH_MSG, __func__, size, ret); |
| return ret; |
| } |
| |
| static long |
| sec_bat_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| int ret = 0; |
| void *buf = NULL; |
| #if SEC_BATT_MISC_DBG |
| uint8_t *p_buf; |
| int i; |
| #endif |
| |
| if (_lock(&c_dev->ioctl_excl)) { |
| pr_err("%s %s - error : ioctl busy - cmd : %d\n", WC_AUTH_MSG, __func__, cmd); |
| return -EBUSY; |
| } |
| |
| switch (cmd) { |
| case BATT_IOCTL_SWAM: |
| pr_info("%s %s - BATT_IOCTL_SWAM cmd\n", WC_AUTH_MSG, __func__); |
| if (copy_from_user(&c_dev->u_data, (void __user *) arg, |
| sizeof(struct swam_data))) { |
| ret = -EIO; |
| pr_err("%s %s - copy_from_user error\n", WC_AUTH_MSG, __func__); |
| goto err; |
| } |
| buf = kzalloc(MAX_BUF, GFP_KERNEL); |
| if (!buf) |
| return -EINVAL; |
| if (c_dev->u_data.size > MAX_BUF) { |
| ret = -ENOMEM; |
| pr_err("%s %s - user data size is %d error\n", WC_AUTH_MSG, __func__, c_dev->u_data.size); |
| goto err; |
| } |
| |
| if (c_dev->u_data.dir == DIR_OUT) { |
| if (copy_from_user(buf, c_dev->u_data.pData, c_dev->u_data.size)) { |
| ret = -EIO; |
| pr_err("%s %s - copy_from_user error\n", WC_AUTH_MSG, __func__); |
| goto err; |
| } |
| #if SEC_BATT_MISC_DBG |
| pr_info("%s %s = send_swam_message - size : %d\n", WC_AUTH_MSG, __func__, c_dev->u_data.size); |
| p_buf = buf; |
| for (i = 0 ; i < c_dev->u_data.size ; i++) |
| pr_info("%x ", (uint32_t)p_buf[i]); |
| pr_info("\n"); |
| #endif |
| ret = send_swam_message(buf, c_dev->u_data.size); |
| if (ret < 0) { |
| pr_err("%s %s- send_swam_message error\n", WC_AUTH_MSG, __func__); |
| ret = -EINVAL; |
| goto err; |
| } |
| } else { |
| #if SEC_BATT_MISC_DBG |
| pr_info("%s %s = received_swam_message - size : %d\n", WC_AUTH_MSG, __func__, c_dev->u_data.size); |
| #endif |
| ret = receive_swam_message(buf, c_dev->u_data.size); |
| if (ret < 0) { |
| pr_err("%s %s - receive_swam_message error\n", WC_AUTH_MSG, __func__); |
| ret = -EINVAL; |
| goto err; |
| } |
| #if SEC_BATT_MISC_DBG |
| p_buf = buf; |
| pr_info("%s %s = received_swam_message - ret : %d\n", WC_AUTH_MSG, __func__, ret); |
| for (i = 0; i < ret ; i++) |
| pr_info("%x ", (uint32_t)p_buf[i]); |
| pr_info("\n"); |
| #endif |
| if (copy_to_user((void __user *)c_dev->u_data.pData, |
| buf, ret)) { |
| ret = -EIO; |
| pr_err("%s %s - copy_to_user error\n", WC_AUTH_MSG, __func__); |
| goto err; |
| } |
| } |
| break; |
| |
| default: |
| pr_err("%s %s - unknown ioctl cmd : %d\n", WC_AUTH_MSG, __func__, cmd); |
| ret = -ENOIOCTLCMD; |
| goto err; |
| } |
| err: |
| kfree(buf); |
| _unlock(&c_dev->ioctl_excl); |
| return ret; |
| } |
| |
| #if 0 |
| u8 htoi(const char *hexa) |
| { |
| char ch = 0; |
| u8 deci = 0; |
| const char *sp = hexa; |
| while (*sp) { |
| deci *= 16; |
| if ('0' <= *sp && *sp <= '9') |
| ch = *sp - '0'; |
| if ('A' <= *sp && *sp <= 'F') |
| ch = *sp - 'A' + 10; |
| if ('a' <= *sp && *sp <= 'f') |
| ch = *sp - 'a' + 10; |
| deci += ch; |
| sp++; |
| } |
| return deci; |
| } |
| |
| void itoh(u8 *dest, char *hex, int len) |
| { |
| int i = 0; |
| |
| while (i < len) { |
| snprintf(hex + i*2, len*2, "%x", dest[i]); |
| i++; |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_COMPAT |
| static long |
| sec_bat_misc_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| int ret = 0; |
| pr_info("%s %s - cmd : %d\n", WC_AUTH_MSG, __func__, cmd); |
| ret = sec_bat_misc_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); |
| |
| return ret; |
| } |
| #endif |
| |
| int sec_bat_swam_out_request_message(void *data, int size) |
| { |
| union power_supply_propval value = {0, }; |
| //int i = 0; |
| u8 *p_data; |
| |
| pr_info("%s %s : auth service writes data\n", WC_AUTH_MSG, __func__); |
| |
| if (data == NULL) { |
| pr_info("%s %s: given data is not valid !\n", WC_AUTH_MSG, __func__); |
| return -EINVAL; |
| } |
| |
| pr_info("%s %s : size = %d \n", WC_AUTH_MSG, __func__, size); |
| |
| /* clear received event */ |
| value.intval = WIRELESS_AUTH_SENT; |
| psy_do_property("wireless", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| |
| p_data = (u8 *)data; |
| //for(i=0; i< size; i++) |
| // pr_info("%s: auth read data = %x", __func__, p_data[i]); |
| |
| if(size > 1 ) { |
| /* set data size first */ |
| value.intval = size; |
| psy_do_property("mfc-charger", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_SIZE, value); |
| |
| value.strval = (u8 *)data; |
| /* set data */ |
| psy_do_property("mfc-charger", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA, value); |
| } else if (size == 1 ) { |
| if (p_data[0] == 0x1) { |
| pr_info("%s %s : auth has been passed \n", WC_AUTH_MSG, __func__); |
| value.intval = WIRELESS_AUTH_PASS; |
| psy_do_property("mfc-charger", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| } else if (p_data[0] == 0x2) { |
| pr_info("%s %s : auth has been failed \n", WC_AUTH_MSG, __func__); |
| value.intval = WIRELESS_AUTH_FAIL; |
| psy_do_property("mfc-charger", set, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS, value); |
| } else |
| pr_info("%s %s : invalid arg %d \n", WC_AUTH_MSG, __func__, p_data[0]); |
| } |
| return size; |
| } |
| |
| void sec_bat_swam_copy_data(u8 *src, u8 *dest, int size) |
| { |
| int i = 0; |
| |
| for(i=0; i < size; i++) { |
| dest[i] = src[i]; |
| pr_info("%s %s : auth read data (for debug) = %x", WC_AUTH_MSG, __func__, dest[i]); |
| } |
| } |
| |
| int sec_bat_swam_in_request_message(void *data) |
| { |
| union power_supply_propval value = {0, }; |
| int size = 0; |
| //int i = 0; |
| //u8 in_data[MAX_BUF] = {0, }; |
| |
| pr_info("%s %s : auth service reads data\n", WC_AUTH_MSG, __func__); |
| |
| if (data == NULL) { |
| pr_info("%s %s : given data is not valid !\n", WC_AUTH_MSG, __func__); |
| return -EINVAL; |
| } |
| pr_info("%s %s\n", WC_AUTH_MSG, __func__); |
| |
| /* get data size first */ |
| psy_do_property("mfc-charger", get, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_SIZE, value); |
| size = value.intval; |
| /* get data */ |
| psy_do_property("mfc-charger", get, |
| POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_DATA, value); |
| |
| if(value.intval == 0) { |
| pr_info("%s: data hasn't been received yet!\n", __func__); |
| return -EINVAL; |
| } |
| |
| //in_data = (u8 *)value.strval; |
| sec_bat_swam_copy_data((u8 *)value.strval, data, size); |
| |
| //for(i=0; i< size; i++) |
| // pr_info("%s: auth read data (for debug) = %x", __func__, in_data[i]); |
| |
| return size; |
| } |
| |
| static const struct file_operations sec_bat_misc_fops = { |
| .owner = THIS_MODULE, |
| .open = sec_bat_misc_open, |
| .release = sec_bat_misc_close, |
| .llseek = no_llseek, |
| .unlocked_ioctl = sec_bat_misc_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = sec_bat_misc_compat_ioctl, |
| #endif |
| }; |
| |
| static struct miscdevice sec_bat_misc_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = NODE_OF_MISC, |
| .fops = &sec_bat_misc_fops, |
| }; |
| |
| int sec_bat_misc_init(struct sec_battery_info *battery) |
| { |
| int ret = 0; |
| |
| ret = misc_register(&sec_bat_misc_device); |
| if (ret) { |
| pr_err("%s %s - return error : %d\n", WC_AUTH_MSG, __func__, ret); |
| goto err; |
| } |
| |
| c_dev = kzalloc(sizeof(struct sec_bat_misc_dev), GFP_KERNEL); |
| if (!c_dev) { |
| ret = -ENOMEM; |
| pr_err("%s %s - kzalloc failed : %d\n", WC_AUTH_MSG, __func__, ret); |
| goto err1; |
| } |
| atomic_set(&c_dev->open_excl, 0); |
| atomic_set(&c_dev->ioctl_excl, 0); |
| |
| battery->misc_dev = c_dev; |
| |
| c_dev->swam_read = sec_bat_swam_in_request_message; |
| c_dev->swam_write = sec_bat_swam_out_request_message; |
| //c_dev->swam_ready = ; |
| //c_dev->swam_close = ; |
| |
| pr_info("%s %s - register success\n", WC_AUTH_MSG, __func__); |
| return 0; |
| err1: |
| misc_deregister(&sec_bat_misc_device); |
| err: |
| return ret; |
| } |
| EXPORT_SYMBOL(sec_bat_misc_init); |
| |
| void sec_bat_misc_exit(void) |
| { |
| pr_info("%s %s() called\n", WC_AUTH_MSG, __func__); |
| if (!c_dev) |
| return; |
| kfree(c_dev); |
| misc_deregister(&sec_bat_misc_device); |
| } |
| EXPORT_SYMBOL(sec_bat_misc_exit); |