/**************************************************************************                 I.MX6 bq27441 driver hacking* 声明:*     本文主要是记录对电池计量芯片bq27441芯片驱动注册过程进行代码跟踪。**                                      2016-2-19 深圳 南山平山村 曾剑锋 ************************************************************************/static int __init bq27x00_battery_init(void)
{int ret;ret = bq27x00_battery_i2c_init();     -----------------------+if (ret)                                                     |return ret;                                              ||ret = bq27x00_battery_platform_init();                       |if (ret)                                                     |bq27x00_battery_i2c_exit();       -----------------------*-----+|     |return ret;                                                  |     |
}                                                                |     |
module_init(bq27x00_battery_init);                               |     ||     |
static void __exit bq27x00_battery_exit(void)                    |     |
{                                                                |     |bq27x00_battery_platform_exit();                             |     |bq27x00_battery_i2c_exit();                                  |     |
}                                                                |     |
module_exit(bq27x00_battery_exit);                               |     ||     |
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");           |     |
MODULE_DESCRIPTION("BQ27x00 battery monitor driver");            |     |
MODULE_LICENSE("GPL");                                           |     ||     ||     |
static inline int __init bq27x00_battery_i2c_init(void)  <-------+     |
{                                                                      |int ret = i2c_add_driver(&bq27x00_battery_driver);   -----------+  |if (ret)                                                        |  |printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); |  ||  |return ret;                                                     |  |
}                                                                   |  ||  |
static inline void __exit bq27x00_battery_i2c_exit(void)    <-------*--+
{                                                                   |i2c_del_driver(&bq27x00_battery_driver);                        |
}                                                                   |||
static const struct i2c_device_id bq27x00_id[] = {    <------+      |{ "bq27200", BQ27200 },                                  |      |{ "bq27500", BQ27500 },                                  |      |{ "bq27520", BQ27520 },                                  |      |{ "bq274xx", BQ274XX },                                  |      |{ "bq276xx", BQ276XX },                                  |      |{ "bq2753x", BQ2753X },                                  |      |{},                                                      |      |
};                                                           |      |
MODULE_DEVICE_TABLE(i2c, bq27x00_id);                        |      ||      |
static struct i2c_driver bq27x00_battery_driver = {      <---|------+.driver = {                                              |.name = "bq27x00-battery",                           |},                                                       |.probe = bq27x00_battery_probe,                   -------*-------+.remove = bq27x00_battery_remove,                        |       |.id_table = bq27x00_id,                          --------+       |
};                                                                   ||
static int __init bq27x00_battery_probe(struct i2c_client *client, <-+const struct i2c_device_id *id)
{char *name;struct bq27x00_device_info *di;int num;int retval = 0;u8 *regs;/* Get new ID for the new battery device */retval = idr_pre_get(&battery_id, GFP_KERNEL);if (retval == 0)return -ENOMEM;mutex_lock(&battery_mutex);retval = idr_get_new(&battery_id, client, &num);mutex_unlock(&battery_mutex);if (retval < 0)return retval;name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);if (!name) {dev_err(&client->dev, "failed to allocate device name\n");retval = -ENOMEM;goto batt_failed_1;}di = kzalloc(sizeof(*di), GFP_KERNEL);if (!di) {dev_err(&client->dev, "failed to allocate device info data\n");retval = -ENOMEM;goto batt_failed_2;}di->id = num;di->dev = &client->dev;di->chip = id->driver_data;di->bat.name = name;di->bus.read = &bq27xxx_read_i2c;                -------------+di->bus.write = &bq27xxx_write_i2c;              -------------*-+di->bus.blk_read = bq27xxx_read_i2c_blk;         -------------*-*-+di->bus.blk_write = bq27xxx_write_i2c_blk;       -------------*-*-*-+di->dm_regs = NULL;                                           | | | |di->dm_regs_count = 0;                                        | | | || | | |if (di->chip == BQ27200)                                      | | | |regs = bq27200_regs;                                      | | | |else if (di->chip == BQ27500)                                 | | | |regs = bq27500_regs;                                      | | | |else if (di->chip == BQ27520)                                 | | | |regs = bq27520_regs;                                      | | | |else if (di->chip == BQ2753X)                                 | | | |regs = bq2753x_regs;                                      | | | |else if (di->chip == BQ274XX) {                               | | | |regs = bq274xx_regs;                                      | | | |di->dm_regs = bq274xx_dm_regs;               -------------*-*-*-*-+di->dm_regs_count = ARRAY_SIZE(bq274xx_dm_regs);          | | | | |} else if (di->chip == BQ276XX) {                             | | | | |/* commands are same as bq274xx, only DM is different */  | | | | |regs = bq276xx_regs;                                      | | | | |di->dm_regs = bq276xx_dm_regs;                            | | | | |di->dm_regs_count = ARRAY_SIZE(bq276xx_dm_regs);          | | | | |} else {                                                      | | | | |dev_err(&client->dev,                                     | | | | |"Unexpected gas gague: %d\n", di->chip);              | | | | |regs = bq27520_regs;                                      | | | | |}                                                             | | | | || | | | |memcpy(di->regs, regs, NUM_REGS);                             | | | | || | | | |di->fw_ver = bq27x00_battery_read_fw_version(di);             | | | | |dev_info(&client->dev, "Gas Guage fw version is 0x%04x\n",    | | | | |di->fw_ver);                                          | | | | || | | | |retval = bq27x00_powersupply_init(di);                 -------*-*-*-*-*-+if (retval)                                                   | | | | | |goto batt_failed_3;                                       | | | | | || | | | | |/* Schedule a polling after about 1 min */                    | | | | | |schedule_delayed_work(&di->work, 60 * HZ);                    | | | | | || | | | | |i2c_set_clientdata(client, di);                               | | | | | |retval = sysfs_create_group(&client->dev.kobj,                | | | | | |&bq27x00_attr_group);                                 | | | | | |if (retval)                                                   | | | | | |dev_err(&client->dev, "could not create sysfs files\n");  | | | | | || | | | | |return 0;                                                     | | | | | || | | | | |
batt_failed_3:                                                    | | | | | |kfree(di);                                                    | | | | | |
batt_failed_2:                                                    | | | | | |kfree(name);                                                  | | | | | |
batt_failed_1:                                                    | | | | | |mutex_lock(&battery_mutex);                                   | | | | | |idr_remove(&battery_id, num);                                 | | | | | |mutex_unlock(&battery_mutex);                                 | | | | | || | | | | |return retval;                                                | | | | | |
}                                                                 | | | | | || | | | | |
static int bq27xxx_read_i2c(struct bq27x00_device_info *di, <-----+ | | | | |u8 reg, bool single)                                        | | | | |
{                                                                   | | | | |struct i2c_client *client = to_i2c_client(di->dev);             | | | | |struct i2c_msg msg[2];                                          | | | | |unsigned char data[2];                                          | | | | |int ret;                                                        | | | | || | | | |if (!client->adapter)                                           | | | | |return -ENODEV;                                             | | | | || | | | |msg[0].addr = client->addr;                                     | | | | |msg[0].flags = 0;                                               | | | | |msg[0].buf = &reg;                                              | | | | |msg[0].len = sizeof(reg);                                       | | | | |msg[1].addr = client->addr;                                     | | | | |msg[1].flags = I2C_M_RD;                                        | | | | |msg[1].buf = data;                                              | | | | |if (single)                                                     | | | | |msg[1].len = 1;                                             | | | | |else                                                            | | | | |msg[1].len = 2;                                             | | | | || | | | |ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));      | | | | |if (ret < 0)                                                    | | | | |return ret;                                                 | | | | || | | | |if (!single)                                                    | | | | |ret = get_unaligned_le16(data);                             | | | | |else                                                            | | | | |ret = data[0];                                              | | | | || | | | |return ret;                                                     | | | | |
}                                                                   | | | | || | | | |
static int bq27xxx_write_i2c(struct bq27x00_device_info *di,   <----+ | | | |u8 reg, int value, bool single)                               | | | |
{                                                                     | | | |struct i2c_client *client = to_i2c_client(di->dev);               | | | |struct i2c_msg msg;                                               | | | |unsigned char data[4];                                            | | | |int ret;                                                          | | | || | | |if (!client->adapter)                                             | | | |return -ENODEV;                                               | | | || | | |data[0] = reg;                                                    | | | |if (single) {                                                     | | | |data[1] = (unsigned char)value;                               | | | |msg.len = 2;                                                  | | | |} else {                                                          | | | |put_unaligned_le16(value, &data[1]);                          | | | |msg.len = 3;                                                  | | | |}                                                                 | | | || | | |msg.buf = data;                                                   | | | |msg.addr = client->addr;                                          | | | |msg.flags = 0;                                                    | | | || | | |ret = i2c_transfer(client->adapter, &msg, 1);                     | | | |if (ret < 0)                                                      | | | |return ret;                                                   | | | || | | |return 0;                                                         | | | |
}                                                                     | | | || | | |
static int bq27xxx_read_i2c_blk(struct bq27x00_device_info *di, <-----+ | | |u8 reg, u8 *data, u8 len)                                           | | |
{                                                                       | | |struct i2c_client *client = to_i2c_client(di->dev);                 | | |struct i2c_msg msg[2];                                              | | |int ret;                                                            | | || | |if (!client->adapter)                                               | | |return -ENODEV;                                                 | | || | |msg[0].addr = client->addr;                                         | | |msg[0].flags = 0;                                                   | | |msg[0].buf = &reg;                                                  | | |msg[0].len = 1;                                                     | | || | |msg[1].addr = client->addr;                                         | | |msg[1].flags = I2C_M_RD;                                            | | |msg[1].buf = data;                                                  | | |msg[1].len = len;                                                   | | || | |ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));          | | |if (ret < 0)                                                        | | |return ret;                                                     | | || | |return ret;                                                         | | |
}                                                                       | | || | |
static int bq27xxx_write_i2c_blk(struct bq27x00_device_info *di, <------+ | |u8 reg, u8 *data, u8 sz)                                          | |
{                                                                         | |struct i2c_client *client = to_i2c_client(di->dev);                   | |struct i2c_msg msg;                                                   | |int ret;                                                              | |u8 buf[33];                                                           | || |if (!client->adapter)                                                 | |return -ENODEV;                                                   | || |buf[0] = reg;                                                         | |memcpy(&buf[1], data, sz);                                            | || |msg.buf = buf;                                                        | |msg.addr = client->addr;                                              | |msg.flags = 0;                                                        | |msg.len = sz + 1;                                                     | || |ret = i2c_transfer(client->adapter, &msg, 1);                         | |if (ret < 0)                                                          | |return ret;                                                       | || |return 0;                                                             | |
}                                                                         | || |
static struct dm_reg bq274xx_dm_regs[] = {              <-----------------+ |{82, 0, 2, 1000},    /* Qmax */                                         |{82, 5, 1, 0x81},    /* Load Select */                                  |{82, 10, 2, 1340},    /* Design Capacity */                             |{82, 12, 2, 3700},    /* Design Energy */                               |{82, 16, 2, 3250},    /* Terminate Voltage */                           |{82, 27, 2, 110},    /* Taper rate */                                   |
};                                                                          ||
static int __init bq27x00_powersupply_init(             <-------------------+struct bq27x00_device_info *di)
{int ret;di->bat.type = POWER_SUPPLY_TYPE_BATTERY;if (di->chip == BQ274XX) {set_properties_array(di, bq274xx_battery_props,ARRAY_SIZE(bq274xx_battery_props));} else if (di->chip == BQ276XX) {set_properties_array(di, bq276xx_battery_props,ARRAY_SIZE(bq276xx_battery_props));} else if (di->chip == BQ27520) {set_properties_array(di, bq27520_battery_props,ARRAY_SIZE(bq27520_battery_props));} else if (di->chip == BQ2753X) {set_properties_array(di, bq2753x_battery_props,ARRAY_SIZE(bq2753x_battery_props));} else {set_properties_array(di, bq27x00_battery_props,ARRAY_SIZE(bq27x00_battery_props));}di->bat.get_property = bq27x00_battery_get_property;di->bat.external_power_changed = bq27x00_external_power_changed;INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll);    -------------+mutex_init(&di->lock);                                              ||ret = power_supply_register(di->dev, &di->bat);                     |if (ret) {                                                          |dev_err(di->dev, "failed to register battery: %d\n", ret);      |return ret;                                                     |}                                                                   ||dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);     ||bq27x00_update(di);                                                 ||return 0;                                                           |
}                                                                       ||
static void bq27x00_battery_poll(struct work_struct *work) <------------+
{struct bq27x00_device_info *di =container_of(work, struct bq27x00_device_info, work.work);if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) &&!rom_mode_gauge_dm_initialized(di)) {rom_mode_gauge_dm_init(di);                        -------------+}                                                                   ||bq27x00_update(di);                                    -------------*---+|   |if (poll_interval > 0) {                                            |   |/* The timer does not have to be accurate. */                   |   |set_timer_slack(&di->work.timer, poll_interval * HZ / 4);       |   |schedule_delayed_work(&di->work, poll_interval * HZ);           |   |}                                                                   |   |
}                                                                       |   ||   |
#define INITCOMP_TIMEOUT_MS        10000                                |   |
static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di)  <---+   |
{                                                                           |int i;                                                                  |int timeout = INITCOMP_TIMEOUT_MS;                                      |u8 subclass, offset;                                                    |u32 blk_number;                                                         |u32 blk_number_prev = 0;                                                |u8 buf[32];                                                             |bool buf_valid = false;                                                 |struct dm_reg *dm_reg;                                                  ||dev_dbg(di->dev, "%s:\n", __func__);                                    ||while (!rom_mode_gauge_init_completed(di) && timeout > 0) {             |msleep(100);                                                        |timeout -= 100;                                                     |}                                                                       ||if (timeout <= 0) {                                                     |dev_err(di->dev, "%s: INITCOMP not set after %d seconds\n",         |__func__, INITCOMP_TIMEOUT_MS/100);                             |return;                                                             |}                                                                       ||if (!di->dm_regs || !di->dm_regs_count) {                               |dev_err(di->dev, "%s: Data not available for DM initialization\n",  |__func__);                                                      |return;                                                             |}                                                                       ||enter_cfg_update_mode(di);                            ------------+     |for (i = 0; i < di->dm_regs_count; i++) {                         |     |dm_reg = &di->dm_regs[i];                                     |     |subclass = dm_reg->subclass;                                  |     |offset = dm_reg->offset;                                      |     ||     |/*                                                            |     |* Create a composite block number to see if the subsequent   |     |* register also belongs to the same 32 btye block in the DM  |     |*/                                                           |     |blk_number = subclass << 8;                                   |     |blk_number |= offset >> 5;                                    |     ||     |if (blk_number == blk_number_prev) {                          |     |copy_to_dm_buf_big_endian(di, buf, offset,                |     |dm_reg->len, dm_reg->data);                           |     |} else {                                                      |     ||     |if (buf_valid)                                            |     |update_dm_block(di, blk_number_prev >> 8,             |     |(blk_number_prev << 5) & 0xFF , buf);             |     |else                                                      |     |buf_valid = true;                                     |     ||     |read_dm_block(di, dm_reg->subclass, dm_reg->offset,       |     |buf);                                                 |     |copy_to_dm_buf_big_endian(di, buf, offset,                |     |dm_reg->len, dm_reg->data);                           |     |}                                                             |     |blk_number_prev = blk_number;                                 |     |}                                                                 |     ||     |/* Last buffer to be written */                                   |     |if (buf_valid)                                                    |     |update_dm_block(di, subclass, offset, buf); ------------------*-+   || |   |exit_cfg_update_mode(di);                           --------------*-*-+ |
}                                                                     | | | || | | |
#define CFG_UPDATE_POLLING_RETRY_LIMIT 50                             | | | |
static int enter_cfg_update_mode(struct bq27x00_device_info *di) <----+ | | |
{                                                                       | | |int i = 0;                                                          | | |u16 flags;                                                          | | || | |dev_dbg(di->dev, "%s:\n", __func__);                                | | || | |if (!unseal(di, BQ274XX_UNSEAL_KEY))                                | | |return 0;                                                       | | || | |control_cmd_wr(di, SET_CFGUPDATE_SUBCMD);                           | | |msleep(5);                                                          | | || | |while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {                        | | |i++;                                                            | | |flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);             | | |if (flags & (1 << 4))                                           | | |break;                                                      | | |msleep(100);                                                    | | |}                                                                   | | || | |if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {                          | | |dev_err(di->dev, "%s: failed %04x\n", __func__, flags);         | | |return 0;                                                       | | |}                                                                   | | |+------------------------------------------------+ | |return 1;          |                                                  | |
}                      |                                                  | ||                                                  | |V                                                  | |
static int update_dm_block(struct bq27x00_device_info *di, u8 subclass,   | |u8 offset, u8 *data)                                                  | |
{                                                                         | |u8 buf[32];                                                           | |u8 cksum;                                                             | |u8 blk_offset = offset >> 5;                                          | || |dev_dbg(di->dev, "%s: subclass %d offset %d\n",                       | |__func__, subclass, offset);                                      | || |di->bus.write(di, BLOCK_DATA_CONTROL, 0, true);                       | |msleep(5);                                                            | || |di->bus.write(di, BLOCK_DATA_CLASS, subclass, true);                  | |msleep(5);                                                            | || |di->bus.write(di, DATA_BLOCK, blk_offset, true);                      | |msleep(5);                                                            | || |di->bus.blk_write(di, BLOCK_DATA, data, 32);                          | |msleep(5);                                                            | |print_buf(__func__, data);                                            | || |cksum = checksum(data);                                               | |di->bus.write(di, BLOCK_DATA_CHECKSUM, cksum, true);                  | |msleep(5);                                                            | || |/* Read back and compare to make sure write is successful */          | |di->bus.write(di, DATA_BLOCK, blk_offset, true);                      | |msleep(5);                                                            | |di->bus.blk_read(di, BLOCK_DATA, buf, 32);                            | |if (memcmp(data, buf, 32)) {                                          | |dev_err(di->dev, "%s: error updating subclass %d offset %d\n",    | |__func__, subclass, offset);                                  | |return 0;                                                         | |} else {                                                              | |return 1;                                                         | |}                                                                     | |
}                                                                         | || |
static int exit_cfg_update_mode(struct bq27x00_device_info *di)   <-------+ |
{                                                                           |int i = 0;                                                              |u16 flags;                                                              ||dev_dbg(di->dev, "%s:\n", __func__);                                    ||control_cmd_wr(di, BQ274XX_SOFT_RESET);                                 ||while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {                            |i++;                                                                |flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);                 |if (!(flags & (1 << 4)))                                            |break;                                                          |msleep(100);                                                        |}                                                                       ||if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {                              |dev_err(di->dev, "%s: failed %04x\n", __func__, flags);             |return 0;                                                           |}                                                                       ||if (seal(di))                                                           |return 1;                                                           |else                                                                    |return 0;                                                           |
}                                                                           |||
static void bq27x00_update(struct bq27x00_device_info *di)     <------------+
{struct bq27x00_reg_cache cache = {0, };bool is_bq27200 = (di->chip == BQ27200);bool is_bq27500 = (di->chip == BQ27500);bool is_bq274xx = (di->chip == BQ274XX);bool is_bq276xx = (di->chip == BQ276XX);cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500);if (cache.flags >= 0) {if (is_bq27200 && (cache.flags & BQ27200_FLAG_CI)) {dev_info(di->dev, "battery is not calibrated!ignoring capacity values\n");cache.capacity = -ENODATA;cache.energy = -ENODATA;cache.time_to_empty = -ENODATA;cache.time_to_empty_avg = -ENODATA;cache.time_to_full = -ENODATA;cache.charge_full = -ENODATA;cache.health = -ENODATA;} else {cache.capacity = bq27x00_battery_read_soc(di);if (!(is_bq274xx || is_bq276xx)) {cache.energy = bq27x00_battery_read_energy(di);cache.time_to_empty =bq27x00_battery_read_time(di,BQ27XXX_REG_TTE);cache.time_to_empty_avg =bq27x00_battery_read_time(di,BQ27XXX_REG_TTECP);cache.time_to_full =bq27x00_battery_read_time(di,BQ27XXX_REG_TTF);}cache.charge_full = bq27x00_battery_read_fcc(di);cache.health = bq27x00_battery_read_health(di);}cache.temperature = bq27x00_battery_read_temperature(di);if (!is_bq274xx)cache.cycle_count = bq27x00_battery_read_cyct(di);cache.power_avg =bq27x00_battery_read_pwr_avg(di, BQ27XXX_POWER_AVG);/* We only have to read charge design full once */if (di->charge_design_full <= 0)di->charge_design_full = bq27x00_battery_read_dcap(di);}if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {di->cache = cache;power_supply_changed(&di->bat);}di->last_update = jiffies;
}

I.MX6 bq27441 driver hacking相关推荐

  1. I.MX6 gpio-keys driver hacking

    /***************************************************************************** I.MX6 gpio-keys drive ...

  2. I.MX6 ar1020 SPI device driver hacking

    /************************************************************************************* I.MX6 ar1020 ...

  3. I.MX6 U-Boot mkconfig hacking

    /***************************************************************************** I.MX6 U-Boot mkconfig ...

  4. I.MX6 mkuserimg.sh hacking

    /************************************************************************ I.MX6 mkuserimg.sh hacking ...

  5. I.MX6 mfgtool2-android-mx6q-sabresd-emmc.vbs hacking

    /********************************************************************* I.MX6 mfgtool2-android-mx6q-s ...

  6. linux read phy reg,PHY

    Linux 下smi/mdio总线通信 韩大卫@吉林师范大学 下面代码描述了在用户层访问smi/mdio总线, 读写phy芯片寄存器的通用代码.Linux内核2.6以上通用. 将下面代码编译后,将可执 ...

  7. I.MX6 Linux Serial Baud Rate hacking

    /********************************************************************************* I.MX6 Linux Seria ...

  8. I.MX6 PHY fixup 调用流程 hacking

    /*********************************************************************************** I.MX6 PHY fixup ...

  9. I.MX6 Goodix GT9xx touchscreen driver porting

    /************************************************************************* I.MX6 Goodix GT9xx touchs ...

最新文章

  1. Python 基础(6)(常用数据结构)
  2. 提高网站访问速度的34条军规(2)
  3. python中文分词jieba总结
  4. JSP、ASP、PHP Web应用程序怎么这么多P!
  5. (转)http接口测试——Jmeter接口测试实例讲解
  6. java集合系列——List集合之ArrayList介绍(二)
  7. UBUNTU16.04下Teamviewer的安装
  8. 【Linux安装】Win10安装Ubuntu双系统(含BIOS操作)
  9. 【快应用】十大手机厂商共推快应用标准
  10. Radix Tree总结
  11. Utf-8编码汉字占多少个字节
  12. EasyExcel实现Mysql数据库Excel数据导出
  13. 小程序报错:Failed to load local image resource /pages/goods/NaN加载资源失败问题解决
  14. Power BI(二十)power pivot之Earlier函数 - DAX进阶的里程碑
  15. 使用fbinst生成u盘启动列表大小不能太大
  16. JSP网页全屏显示、退出全屏、关闭页面
  17. 第一章 函数 极限 连续
  18. MySQL导入导出 —— mysqldump 简介及常见用法
  19. TS-MPEG2视频数字水印演示程序
  20. 局域网控制计算机运行程序,局域网控制远程计算机教程的方法

热门文章

  1. android 各种控件颜色值的设置(使用Drawable,Color)
  2. server-send event object
  3. CCTouchDispatcher sharedDispatcher 方法过期
  4. SQL Server-流程控制 5,Goto 语句
  5. 如何将广告始终定位到网页右下角
  6. cinder存储服务
  7. 面试题-自旋锁,以及jvm对synchronized的优化
  8. Swoft 2 Beta 发布,基于 Swoole 的云原生协程框架
  9. react中ref的使用
  10. 关于java.util.LinkedHashMap cannot be cast to ......的解决办法