Android与Kernel使用结点进行通信.
Android与Kernel通信
Linux中一切皆文件, Android上层与Kernel通信可以使用结点.
public boolean writeProcCmd(int cmd, int data) {
String new_proc_path = "/proc/mydebug";
File procFile = new File(new_proc_path);
try {
FileOutputStream fw = new FileOutputStream(procFile);
byte buf[] = new byte[2];
buf[0] = (byte)cmd;
buf[1] = (byte)data;
fw.write(buf);
fw.flush();
fw.close();
return true;
} catch (IOException e) {
Log.e(MYTAG, "open output or input stream error");
return false;
}
}
public int readProcCmd(byte[] buf) {
String new_proc_path = "/proc/mydebug";
File procFile = new File(new_proc_path);
try {
FileInputStream fr = new FileInputStream(procFile);
int ret = fr.read(buf);
fr.close();
if (ret < 0) {
Log.e(MYTAG, "proc read input stream error");
return -1 ;
}
return ret;
} catch (IOException e) {
Log.e(MYTAG, "open output or input stream error");
return -1;
}
}
Kernel模块与模块通信
同理kernel间模块之间通讯也可以采用结点通讯的方法进行通讯.
#define IDC_BOARD_PROC "/proc/mydebug"
#define PROC_MYCMD_1 30
#define PROC_MYCMD_2 31
static int fts_send_sd_cmd(int present)
{
struct file *idcfile;
mm_segment_t old_fs;
char opbuf[2] = {0};
int ret;
idcfile = filp_open(IDC_BOARD_PROC, O_RDWR, 0);
if (IS_ERR(idcfile)) {
pr_err("Open %s failed!\n", IDC_BOARD_PROC);
return -EINVAL;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
if (present) {
opbuf[0] = PROC_MYCMD_1;
} else {
opbuf[0] = PROC_MYCMD_2;
}
ret = idcfile->f_op->write(idcfile, opbuf, 1, &idcfile->f_pos);
filp_close(idcfile, NULL);
set_fs(old_fs);
if (ret < 0) {
pr_err("send cmd failed, ret: %d\n", ret);
return -EINVAL;
}
pr_err("send cmd success\n");
return 0;
}
Kernel端创建结点
1. 创建proc结点(注意有linux版本区别)
struct kernel_ts_data {
struct i2c_client *client;
struct proc_dir_entry *proc;
};
#define PROC_NAME "mydebug"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
/************************************************************************
* Name: kernel_debug_write
* Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static ssize_t kernel_debug_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
int buflen = count;
int writelen = 0;
int ret = 0;
char tmp[25];
struct kernel_ts_data *ts_data = kernel_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
pr_err("proc wirte count(%d) fail", (int)count);
return -EINVAL;
}
if (copy_from_user(&writebuf, buff, count)) {
pr_err("copy from user error!!");
return -EFAULT;
}
ts_data->proc_opmode = writebuf[0];
switch (ts_data->proc_opmode) {
case PROC_READ_DATA:
case PROC_WRITE_DATA:
writelen = buflen - 1;
if (writelen > 0) {
ret = kernel_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
pr_err("write iic error!!");
}
}
break;
default:
break;
}
if (ret < 0) {
return ret;
} else {
return count;
}
}
/************************************************************************
* Name: kernel_debug_read
* Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static ssize_t kernel_debug_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos)
{
int ret = 0;
int num_read_chars = 0;
int readlen = 0;
u8 buf[PROC_READ_BUF_SIZE] = { 0 };
struct kernel_ts_data *ts_data = kernel_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
pr_err("proc read count(%d) fail", (int)count);
return -EINVAL;
}
switch (ts_data->proc_opmode) {
case PROC_READ_DATA:
readlen = count;
ret = kernel_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
pr_err("read iic error!!");
return ret;
}
num_read_chars = readlen;
break;
case PROC_WRITE_DATA:
break;
default:
break;
}
if (copy_to_user(buff, buf, num_read_chars)) {
pr_err("copy to user error!!");
return -EFAULT;
}
return num_read_chars;
}
static const struct file_operations kernel_proc_fops = {
.owner = THIS_MODULE,
.read = kernel_debug_read,
.write = kernel_debug_write,
};
#else
/* interface of write proc */
/************************************************************************
* Name: kernel_debug_write
* Brief:interface of write proc
* Input: file point, data buf, data len, no use
* Output: no
* Return: data len
***********************************************************************/
static int kernel_debug_write(struct file *filp,
const char __user *buff, unsigned long len, void *data)
{
int ret = 0;
u8 writebuf[PROC_WRITE_BUF_SIZE] = { 0 };
int buflen = len;
int writelen = 0;
struct kernel_ts_data *ts_data = kernel_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_WRITE_BUF_SIZE)) {
pr_err("proc wirte count(%d) fail", (int)count);
return -EINVAL;
}
if (copy_from_user(&writebuf, buff, buflen)) {
pr_err("copy from user error!!");
return -EFAULT;
}
ts_data->proc_opmode = writebuf[0];
switch (ts_data->proc_opmode) {
case PROC_READ_DATA:
case PROC_WRITE_DATA:
writelen = len - 1;
if (writelen > 0) {
ret = kernel_i2c_write(client, writebuf + 1, writelen);
if (ret < 0) {
pr_err(" write iic error!!");
}
}
break;
default:
break;
}
if (ret < 0) {
return ret;
} else {
return len;
}
}
/* interface of read proc */
/************************************************************************
* Name: kernel_debug_read
* Brief:interface of read proc
* Input: point to the data, no use, no use, read len, no use, no use
* Output: page point to data
* Return: read char number
***********************************************************************/
static int kernel_debug_read( char *page, char **start,
off_t off, int count, int *eof, void *data )
{
int ret = 0;
u8 buf[PROC_READ_BUF_SIZE] = { 0 };
int num_read_chars = 0;
int readlen = 0;
struct kernel_ts_data *ts_data = kernel_data;
struct i2c_client *client = ts_data->client;
if ((count == 0) || (count > PROC_READ_BUF_SIZE)) {
pr_err("proc read count(%d) fail", (int)count);
return -EINVAL;
}
switch (ts_data->proc_opmode) {
case PROC_READ_DATA:
readlen = count;
ret = kernel_i2c_read(client, NULL, 0, buf, readlen);
if (ret < 0) {
pr_err("read iic error!!");
return ret;
}
num_read_chars = readlen;
break;
case PROC_WRITE_DATA:
break;
default:
break;
}
memcpy(page, buf, num_read_chars);
return num_read_chars;
}
#endif
/************************************************************************
* Name: kernel_create_apk_debug_channel
* Brief: create apk debug channel
* Input: i2c info
* Output:
* Return: return 0 if success
***********************************************************************/
int kernel_create_apk_debug_channel(struct kernel_ts_data *ts_data)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
ts_data->proc = proc_create(PROC_NAME, 0777, NULL, &kernel_proc_fops);
#else
ts_data->proc = create_proc_entry(PROC_NAME, 0777, NULL);
#endif
if (NULL == ts_data->proc) {
pr_err("create proc entry fail");
return -ENOMEM;
} else {
pr_info("Create proc entry success!");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0))
ts_data->proc->write_proc = kernel_debug_write;
ts_data->proc->read_proc = kernel_debug_read;
#endif
}
return 0;
}
/************************************************************************
* Name: kernel_release_apk_debug_channel
* Brief: release apk debug channel
* Input:
* Output:
* Return:
***********************************************************************/
void kernel_release_apk_debug_channel(struct kernel_ts_data *ts_data)
{
if (ts_data->proc) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
proc_remove(ts_data->proc);
#else
remove_proc_entry(PROC_NAME, NULL);
#endif
}
}
2. 创建sysfs结点, 以I2C设备为例
enum {
RWREG_OP_READ = 0,
RWREG_OP_WRITE = 1,
};
static struct rwreg_operation_t {
int type; /* 0: read, 1: write */
int reg; /* register */
int len; /* read/write length */
int val; /* length = 1; read: return value, write: op return */
int res; /* 0: success, otherwise: fail */
char *opbuf; /* length >= 1, read return value, write: op return */
} rw_op;
/************************************************************************
* Name: kernel_rwreg_show
* Brief: no
* Input: device, device attribute, char buf
* Output: no
* Return: EPERM
***********************************************************************/
static ssize_t kernel_rwreg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int count;
int i;
struct input_dev *input_dev = kernel_data->input_dev;
mutex_lock(&input_dev->mutex);
if (rw_op.len < 0) {
count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
} else if (rw_op.len == 1) {
if (RWREG_OP_READ == rw_op.type) {
if (rw_op.res == 0) {
count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
} else {
count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
}
} else {
if (rw_op.res == 0) {
count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val);
} else {
count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res);
}
}
} else {
if (RWREG_OP_READ == rw_op.type) {
count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
count += snprintf(buf + count, PAGE_SIZE, "Result: ");
if (rw_op.res) {
count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
} else {
if (rw_op.opbuf) {
for (i = 0; i < rw_op.len; i++) {
count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
}
}
} else {
;
count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
if (rw_op.opbuf) {
for (i = 1; i < rw_op.len; i++) {
count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
}
if (rw_op.res) {
count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
} else {
count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
}
}
/*if (rw_op.opbuf) {
kernel_kfree(rw_op.opbuf);
rw_op.opbuf = NULL;
}*/
}
mutex_unlock(&input_dev->mutex);
return count;
}
static int shex_to_int(const char *hex_buf, int size)
{
int i;
int base = 1;
int value = 0;
char single;
for (i = size - 1; i >= 0; i--) {
single = hex_buf[i];
if ((single >= '0') && (single <= '9')) {
value += (single - '0') * base;
} else if ((single >= 'a') && (single <= 'z')) {
value += (single - 'a' + 10) * base;
} else if ((single >= 'A') && (single <= 'Z')) {
value += (single - 'A' + 10) * base;
} else {
return -EINVAL;
}
base *= 16;
}
return value;
}
static u8 shex_to_u8(const char *hex_buf, int size)
{
return (u8)shex_to_int(hex_buf, size);
}
/*
* Format buf:
* [0]: '0' write, '1' read(reserved)
* [1-2]: addr, hex
* [3-4]: length, hex
* [5-6]...[n-(n+1)]: data, hex
*/
static int kernel_parse_buf(const char *buf, size_t cmd_len)
{
int length;
int i;
char *tmpbuf;
rw_op.reg = shex_to_u8(buf + 1, 2);
length = shex_to_int(buf + 3, 2);
if (buf[0] == '1') {
rw_op.len = length;
rw_op.type = RWREG_OP_READ;
kernel_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
} else {
if (cmd_len < (length * 2 + 5)) {
pr_err("data invalided!\n");
return -EINVAL;
}
kernel_DEBUG("write %02X, %d bytes", rw_op.reg, length);
/* first byte is the register addr */
rw_op.type = RWREG_OP_WRITE;
rw_op.len = length + 1;
}
if (rw_op.len > 0) {
tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
if (!tmpbuf) {
pr_err("allocate memory failed!\n");
return -ENOMEM;
}
if (RWREG_OP_WRITE == rw_op.type) {
tmpbuf[0] = rw_op.reg & 0xFF;
kernel_DEBUG("write buffer: ");
for (i = 1; i < rw_op.len; i++) {
tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
kernel_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
}
}
rw_op.opbuf = tmpbuf;
}
return rw_op.len;
}
/************************************************************************
* Name: kernel_rwreg_store
* Brief: read/write register
* Input: device, device attribute, char buf, char count
* Output: print register value
* Return: char count
***********************************************************************/
static ssize_t kernel_rwreg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct input_dev *input_dev = kernel_data->input_dev;
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
ssize_t cmd_length = 0;
mutex_lock(&input_dev->mutex);
cmd_length = count - 1;
if (rw_op.opbuf) {
kernel_kfree(rw_op.opbuf);
rw_op.opbuf = NULL;
}
kernel_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
/* compatible old ops */
if (2 == cmd_length) {
rw_op.type = RWREG_OP_READ;
rw_op.len = 1;
rw_op.reg = shex_to_int(buf, 2);
} else if (4 == cmd_length) {
rw_op.type = RWREG_OP_WRITE;
rw_op.len = 1;
rw_op.reg = shex_to_int(buf, 2);
rw_op.val = shex_to_int(buf + 2, 2);
} else if (cmd_length < 5) {
pr_err("Invalid cmd buffer");
mutex_unlock(&input_dev->mutex);
return -EINVAL;
} else {
rw_op.len = kernel_parse_buf(buf, cmd_length);
}
if (rw_op.len < 0) {
pr_err("cmd buffer error!");
} else {
if (RWREG_OP_READ == rw_op.type) {
if (rw_op.len == 1) {
u8 reg, val;
reg = rw_op.reg & 0xFF;
rw_op.res = kernel_i2c_read_reg(client, reg, &val);
rw_op.val = val;
} else {
char reg;
reg = rw_op.reg & 0xFF;
rw_op.res = kernel_i2c_read(client, ®, 1, rw_op.opbuf, rw_op.len);
}
if (rw_op.res < 0) {
pr_err("Could not read 0x%02x", rw_op.reg);
} else {
pr_info("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
rw_op.res = 0;
}
} else {
if (rw_op.len == 1) {
u8 reg, val;
reg = rw_op.reg & 0xFF;
val = rw_op.val & 0xFF;
rw_op.res = kernel_i2c_write_reg(client, reg, val);
} else {
rw_op.res = kernel_i2c_write(client, rw_op.opbuf, rw_op.len);
}
if (rw_op.res < 0) {
pr_err("Could not write 0x%02x", rw_op.reg);
} else {
pr_info("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
rw_op.res = 0;
}
}
}
mutex_unlock(&input_dev->mutex);
return count;
}
/* add your attr in here*/
static struct attribute *fts_attributes[] = {
&dev_attr_rw_reg.attr,
NULL
};
static struct attribute_group fts_attribute_group = {
.attrs = fts_attributes
};
/************************************************************************
* Name: kernel_create_sysfs
* Brief: create sysfs interface
* Input:
* Output:
* Return: return 0 if success
***********************************************************************/
int kernel_create_sysfs(struct i2c_client *client)
{
int ret = 0;
ret = sysfs_create_group(&client->dev.kobj, &kernel_attribute_group);
if (ret) {
pr_err("[EX]: sysfs_create_group() failed!!");
sysfs_remove_group(&client->dev.kobj, &kernel_attribute_group);
return -ENOMEM;
} else {
pr_info("[EX]: sysfs_create_group() succeeded!!");
}
return ret;
}
/************************************************************************
* Name: kernel_remove_sysfs
* Brief: remove sysfs interface
* Input:
* Output:
* Return:
***********************************************************************/
int kernel_remove_sysfs(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &kernel_attribute_group);
return 0;
}