Android 通过IIC读取数据(JNI)

参考链接:http://blog.csdn.net/sweetsnow24/article/details/8634183

参考链接:http://blog.csdn.net/sweetsnow24/article/details/8634183

我的实现:

1.定义Native方法:

public class WakeUp {

      static {

           System.loadLibrary("WakeUp");

      }

      public static native int open(String path, int oFlag);

      public static native int getWakeUpState(int fid);

      public static native int getWakeUpDegree();

      public static native int close(int fId);

}

2.生成.h头文件

生成方法:进入项目文件夹, 生成JNI的头文件, 使用命令:

javah -classpath bin/classes -d jni com.robot.et.core.hardware.wakeup.WakeUp

生成后的头文件(com_robot_et_core_hardware_wakeup_WakeUp.h)如下:

/* DO NOT EDIT THIS FILE - it is machine generated *

/#include <jni.h>

/* Header for class com_robot_et_core_hardware_wakeup_WakeUp */


#ifndef _Included_com_robot_et_core_hardware_wakeup_WakeUp

#define _Included_com_robot_et_core_hardware_wakeup_WakeUp

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class:    com_robot_et_core_hardware_wakeup_WakeUp

* Method:    open

* Signature: (Ljava/lang/String;I)Ljava/io/FileDescriptor;

*/

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_open

(JNIEnv *, jobject, jstring, jint);

/*

* Class:    com_robot_et_core_hardware_wakeup_WakeUp

* Method:    getWakeUpState

* Signature: ()I

*/

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_getWakeUpState

(JNIEnv *, jobject,jint);

/*

* Class:    com_robot_et_core_hardware_wakeup_WakeUp

* Method:    getWakeUpDegree

* Signature: ()I

*/

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_getWakeUpDegree

(JNIEnv *, jobject);

/*

* Class:    com_robot_et_core_hardware_wakeup_WakeUp

* Method:    close

* Signature: (I)I

*/

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_close

(JNIEnv *, jobject, jint);

#ifdef __cplusplus

}

#endif

#endif

3.具体实现(.cpp文件)

#include <jni.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <unistd.h>

#include <fcntl.h>

#include <poll.h>

#include <sys/ioctl.h>

#include "i2c-dev.h"

#include <linux/i2c.h>

#include "com_robot_et_core_hardware_wakeup_WakeUp.h"


#ifdef __cplusplus

extern "C"

{

#endif

/****************************************************************

* Constants

****************************************************************/

#define SYSFS_GPIO_DIR                        "/sys/class/gpio"

#define POLL_TIMEOUT                          (3 * 1000) /* 3 seconds */

#define MAX_BUF 64

#define GPIO_C4                              (68)

#define WAKEUP_SUCCESS                        (0)

#define WAKEUP_FAIL                          (-1)

#define I2C_FILE_OPEN_ERR                    (-2)

#define REGISTER_SLAVE_ERR                    (-3)

#define SOUND_LOCALIZATION_OCCUR              (1)

unsigned int i2c_fileId;

int gpio_fileId;

unsigned int wakeup_degree;

/****************************************************************

* gpio_export

****************************************************************/

int gpio_export(unsigned int gpio)

{

int fd, len;

char buf[MAX_BUF];

fd = open("/sys/class/gpio/export", O_WRONLY);

if (fd < 0) {

perror("gpio/export");

return fd;

}

len = snprintf(buf, sizeof(buf), "%d", gpio);

write(fd, buf, len);

close(fd);

return 0;

}

/****************************************************************

* gpio_unexport

****************************************************************/

int gpio_unexport(unsigned int gpio)

{

int fd, len;

char buf[MAX_BUF];

fd = open("/sys/class/gpio/unexport", O_WRONLY);

if (fd < 0) {

perror("gpio/export");

return fd;

}

len = snprintf(buf, sizeof(buf), "%d", gpio);

write(fd, buf, len);

close(fd);

return 0;

}

/****************************************************************

* gpio_set_dir

****************************************************************/

int gpio_set_dir(unsigned int gpio, unsigned int out_flag)

{

int fd, len;

char buf[MAX_BUF];

len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);

fd = open(buf, O_WRONLY);

if (fd < 0) {

perror("gpio/direction");

return fd;

}

if (out_flag)

write(fd, "out", 4);

else

write(fd, "in", 3);

close(fd);

return 0;

}

/****************************************************************

* gpio_set_value

****************************************************************/

int gpio_set_value(unsigned int gpio, unsigned int value)

{

int fd, len;

char buf[MAX_BUF];

len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);

fd = open(buf, O_WRONLY);

if (fd < 0) {

perror("gpio/set-value");

return fd;

}

if (value)

write(fd, "1", 2);

else

write(fd, "0", 2);

close(fd);

return 0;

}

/****************************************************************

* gpio_get_value

****************************************************************/

int gpio_get_value(unsigned int gpio, unsigned int *value)

{

int fd, len;

char buf[MAX_BUF];

char ch;

len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);

fd = open(buf, O_RDONLY);

if (fd < 0) {

perror("gpio/get-value");

return fd;

}

read(fd, &ch, 1);

if (ch != '0') {

*value = 1;

} else {

*value = 0;

}

close(fd);

return 0;

}

/****************************************************************

* gpio_set_edge

****************************************************************/

int gpio_set_edge(unsigned int gpio, char *edge)

{

int fd, len;

char buf[MAX_BUF];

len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/edge", gpio);

fd = open(buf, O_WRONLY);

if (fd < 0) {

perror("gpio/set-edge");

return fd;

}

write(fd, edge, strlen(edge) + 1);

close(fd);

return 0;

}

/****************************************************************

* gpio_fd_open

****************************************************************/

int gpio_fd_open(unsigned int gpio)

{

int fd, len;

char buf[MAX_BUF];

len = snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);

fd = open(buf, O_RDONLY | O_NONBLOCK );

if (fd < 0) {

perror("gpio/fd_open");

}

return fd;

}

/****************************************************************

* gpio_fd_close

****************************************************************/

int gpio_fd_close(int fd)

{

return close(fd);

}

#define xfm20512_ADDR          (0x47)

#define I2C_BUF_LEN            (256)

#define DELAY_MS(ms)            usleep((ms) * 1000)

/*****************************************************************************

函 数 名  : i2c_write_proc

功能描述  : i2c_write_proc

输入参数  : int fd

unsigned char addr

unsigned char reg

unsigned char *val

unsigned char len

输出参数  : 无

返 回 值  : static

调用函数  :

被调函数  :

修改历史      :

1.日    期  : 2016年6月12日

作    者  : zlg

修改内容  : 新生成函数

*****************************************************************************/

static int i2c_write_proc

(

int fd,

unsigned char addr,

unsigned char reg,

unsigned char *val,

unsigned char len

)

{

unsigned char buf[I2C_BUF_LEN];

struct i2c_rdwr_ioctl_data cmd;

struct i2c_msg msg[1];

memset(buf, 0, sizeof(buf));    // zero memset buffer

buf[0] = reg;                  // The first byte indicates which register we'll write

memcpy(&buf[1], val, len);      // The last bytes indicates the values to write

/* Construct the i2c_msg struct */

msg[0].addr  = addr;                // Device address

msg[0].flags = 0;              // Write option

msg[0].len  = len + 1;        // Include register byte

msg[0].buf  = buf;            // Data buffer

/* Construct the i2c_rdwr_ioctl_data struct */

cmd.msgs  = msg;                // Message

cmd.nmsgs = sizeof(msg) / sizeof(struct i2c_msg);

/* Transfer the i2c command packet to the kernel and verify it worked */

if (ioctl(fd, I2C_RDWR, &cmd) < 0)

{

printf("Unable to send data!\n");

return 1;

}

return 0;

}

/*****************************************************************************

函 数 名  : i2c_read_proc

功能描述  : i2c_read_proc

输入参数  : int fd

unsigned char addr

unsigned char reg

unsigned char *val

unsigned char len

输出参数  : 无

返 回 值  : static

调用函数  :

被调函数  :

修改历史      :

1.日    期  : 2016年6月12日

作    者  : zlg

修改内容  : 新生成函数

*****************************************************************************/

static int i2c_read_proc

(

int fd,

unsigned char addr,

unsigned char reg,

unsigned char *val,

unsigned char len

)

{

struct i2c_rdwr_ioctl_data cmd;

struct i2c_msg msg[2];

msg[0].addr  = addr;

msg[0].flags = 0;

msg[0].len  = 1;

msg[0].buf  = ®

msg[1].addr  = addr;

msg[1].flags = I2C_M_RD /* | I2C_M_NOSTART*/;

msg[1].len  = len;

msg[1].buf  = val;

/* Construct the i2c_rdwr_ioctl_data struct */

cmd.msgs  = msg;

cmd.nmsgs = sizeof(msg) / sizeof(struct i2c_msg);

/* Send the request to the kernel and get the result back */

if (ioctl(fd, I2C_RDWR, &cmd) < 0)

{

printf("Unable to send data!\n");

return -1;

}

return 0;

}

/*****************************************************************************

函 数 名  : xfm20512_get_version

功能描述  : xfm20512_get_version

输入参数  : int fd

unsigned int *version

输出参数  : 无

返 回 值  :

调用函数  :

被调函数  :

修改历史      :

1.日    期  : 2016年6月12日

作    者  : zlg

修改内容  : 新生成函数

*****************************************************************************/

int xfm20512_get_version(int fd, unsigned int *version)

{

unsigned int data = 0x00000F00;

/* 1. Send version query command */

if (i2c_write_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, 4))

return -1;

DELAY_MS(1);    // Delay 1ms at least

/* 2. Query command status */

do {

if (i2c_read_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, 4))

return -1;

DELAY_MS(1);    // Delay 1ms at least

} while (0x00020001 != data);

if (i2c_read_proc(fd, xfm20512_ADDR, 0x01, (unsigned char *)version, 8))

return -1;

return 0;

}

/*****************************************************************************

函 数 名  : xfm20512_get_degree

功能描述  : xfm20512_get_degree

输入参数  : int fd

unsigned int *degree

输出参数  : 无

返 回 值  :

调用函数  :

被调函数  :

修改历史      :

1.日    期  : 2016年6月12日

作    者  : zlg

修改内容  : 新生成函数

*****************************************************************************/

int xfm20512_get_degree(int fd, unsigned int *degree)

{

unsigned int data = 0x00001000;

/* 1. Send degree query command */

if (i2c_write_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, 4))

return -1;

DELAY_MS(1);    // Delay 1ms at least

/* 2. Query command status */

do {

if (i2c_read_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, 4))

return -1;

DELAY_MS(1);    // Delay 1ms at least

} while (0x00010001 != data);

/* 3. Read wakeup degree */

if (i2c_read_proc(fd, xfm20512_ADDR, 0x01, (unsigned char *)degree, 4))

return -1;

return 0;

}

int xfm20512_enter_wakeup(int fd)

{

unsigned int data = 0x00001100;

/* 1. Send enter wakeup command */

if (i2c_write_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, sizeof(unsigned int)))

return -1;

DELAY_MS(1);    // Delay 1ms at least

/* 2. Query command status */

do {

if (i2c_read_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, sizeof(unsigned int)))

return -1;

DELAY_MS(1);    // Delay 1ms at least

} while (0x00000001 != data);

return 0;

}

int xfm20512_exit_wakeup(int fd, unsigned int beam)

{

unsigned int data = 0x00001200 | ((beam & 0x3) << 16);

/* 1. Send exit wakeup command */

if (i2c_write_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, sizeof(unsigned int)))

return -1;

DELAY_MS(1);    // Delay 1ms at least

/* 2. Query command status */

do {

if (i2c_read_proc(fd, xfm20512_ADDR, 0x00, (unsigned char *)&data, sizeof(unsigned int)))

return -1;

DELAY_MS(1);    // Delay 1ms at least

} while (0x00030001 != data);

return 0;

}

/****************************************************************

* i2c_init

****************************************************************/

int i2c_init()

{

int fd = -1;

int ret;

int write_count = 0;

/* 打开master侧的I2C-0接口 */

i2c_fileId = open("/dev/i2c-0", O_RDWR);

if (i2c_fileId < 0)

{

printf("Open i2c-0 Port Error!\n");

return I2C_FILE_OPEN_ERR;

}

#if 0

/* 注册从机 */

if (ioctl(i2c_fileId, I2C_SLAVE, xfm20512_ADDR) < 0)

{

printf("ioctl error\n");

return REGISTER_SLAVE_ERR;

}

#endif

/* 查询模块版本信息 */

unsigned int version;

if (!xfm20512_get_version(i2c_fileId, &version))

{

printf("version = %d\n", version);

}

return WAKEUP_SUCCESS;

}

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_open

(JNIEnv *env, jobject obj, jstring path, jint oflag)

{

int gpio;

int ret;

/* i2c init */

ret = i2c_init();

if (ret < 0)

{

return ret;

}

system("setenforce 0");

system("chmod 777 /sys/class/gpio");

system("chmod 777 /sys/class/gpio/export");

system("echo 68 > /sys/class/gpio/export");

system("chmod 777 /sys/class/gpio/gpio68");

system("chmod 777 /sys/class/gpio/gpio68/direction");

system("chmod 777 /sys/class/gpio/gpio68/edge");

system("chmod 777 /sys/class/gpio/gpio68/value");

gpio = GPIO_C4;

gpio_export(gpio);

gpio_set_dir(gpio, 0);

gpio_set_edge(gpio, (char*)"rising");

gpio_fileId = gpio_fd_open(gpio);

if (gpio_fileId < 0)

{

return WAKEUP_FAIL;

}

return gpio_fileId;

}

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_getWakeUpState

(JNIEnv *env, jobject obj, jint wakeup_fd)

{

struct pollfd fdset[2];

int nfds = 2;

int timeout, rc;

char buf[MAX_BUF];

int len;

#if 0

while (1) {

memset((void*)fdset, 0, sizeof(fdset));

fdset[0].fd = STDIN_FILENO;

fdset[0].events = POLLIN;

fdset[1].fd = wakeup_fd;

fdset[1].events = POLLPRI;

rc = poll(fdset, nfds, 1000);

if (rc < 0) {

//printf("\npoll() failed!\n");

return -1;

}

if (rc == 0) {

printf(".");

return 0;

}

if (fdset[1].revents & POLLPRI) {

len = read(fdset[1].fd, buf, 1);

xfm20512_get_degree(i2c_fileId, &wakeup_degree);

return SOUND_LOCALIZATION_OCCUR;

}

if (fdset[0].revents & POLLIN) {

(void)read(fdset[0].fd, buf, 1);

}

fflush(stdout);

}

#endif

memset(buf, '\0', sizeof(buf));

#if 0

len = read(wakeup_fd, buf, 16);

if (strlen(buf) != 0)

{

xfm20512_get_degree(i2c_fileId, &wakeup_degree);

return 1;

}

#else

unsigned int value = 0;

(void)gpio_get_value(68, &value);

if (value != 0)

{

xfm20512_get_degree(i2c_fileId, &wakeup_degree);

while (value != 0)

{

(void)gpio_get_value(68, &value);

}

return 1;

}

#endif

return WAKEUP_SUCCESS;

}

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_getWakeUpDegree

(JNIEnv *env, jobject obj)

{

return wakeup_degree;

}

JNIEXPORT jint JNICALL Java_com_robot_et_core_hardware_wakeup_WakeUp_close

(JNIEnv *env, jobject obj, jint gpio_fileId)

{

close(gpio_fileId);

return WAKEUP_SUCCESS;

}

#ifdef __cplusplus

}

#endif

4.头文件i2c-dev.h(直接从Linux驱动中直接复制过来的)

/*    i2c-dev.h - i2c-bus driver, char device interface    Copyright (C) 1995-97 Simon G. Vogl    Copyright (C) 1998-99 Frodo LooijaardThis program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,    MA 02110-1301 USA.*/

#ifndef _LINUX_I2C_DEV_H

#define _LINUX_I2C_DEV_H

#include <linux/types.h>

#include <linux/compiler.h>

/* /dev/i2c-X ioctl commands.  The ioctl's parameter is always an

* unsigned long, except for:

* - I2C_FUNCS, takes pointer to an unsigned long

* - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data

* - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data

*/

#define I2C_RETRIES 0x0701   /* number of times a device address should be polled when not acknowledging */

#define I2C_TIMEOUT 0x0702  /* set timeout in units of 10 ms */

/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses

* are NOT supported! (due to code brokenness)

*/

#define I2C_SLAVE 0x0703 /* Use this slave address */

#define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if it is already in use by a driver! */

#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */

#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */

#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */

#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */

#define I2C_SMBUS 0x0720 /* SMBus transfer */

/* This is the structure as used in the I2C_SMBUS ioctl call */

struct i2c_smbus_ioctl_data {

__u8 read_write;

__u8 command;

__u32 size;

union i2c_smbus_data __user *data;

};

/* This is the structure as used in the I2C_RDWR ioctl call */

struct i2c_rdwr_ioctl_data {

struct i2c_msg __user *msgs; /* pointers to i2c_msgs */

__u32 nmsgs; /* number of i2c_msgs */

};

#define  I2C_RDRW_IOCTL_MAX_MSGS 42

#ifdef __KERNEL__

#define I2C_MAJOR 89 /* Device major number */

#endif

#endif /* _LINUX_I2C_DEV_H */

5.Android.mk文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := WakeUp

LOCAL_SRC_FILES := wakeup.cpp

LOCAL_LDLIBS += -L$(SYSTEM)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

6.生成.so文件(注意包名一直,否则不能够成功生成 libWakeUp.so文件)

7.直接调用

简单代码:

private int fd;

private int wakeUpState;

private int degree;

fd = WakeUp.open("", 0);

wakeUpState = WakeUp.getWakeUpState(fd);

degree = WakeUp.getWakeUpDegree();


提示还有一个命令问题:

chmod 777 /dev/i2c-0

chmod 777 /sys/class/gpio

chmod 777 /sys/class/gpio/export

echo 68 > /sys/class/gpio/export

chmod 777 /sys/class/gpio/gpio68

chmod 777 /sys/class/gpio/gpio68/direction

chmod 777 /sys/class/gpio/gpio68/edge

chmod 777 /sys/class/gpio/gpio68/value

setenforce 0

更新:上面的问题如何解决?参考链接:http://www.jianshu.com/p/5bda839f31ae

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容