td1030 i2c driver of openwrt

Taidou 1030, gps sensor, default uart interface, support i2c interface to read and write.
However, td1030 does not want the usual i2c devices, such as eeprom 24c02, temperature and humidity sensor sth12, etc., which have the concept of registers.
td1030 only needs to read and write.

  • openwrt enables i2c simulation 1 , i2c simulation needs to modify the device tree definition port and other parameters 2
 .config - OpenWrt Configuration
 > Kernel modules > I2C support ────────────────────────────────────────────────────────────────────────
  ┌────────────────────────────────────────── I2C support ───────────────────────────────────────────┐
  │  Arrow keys navigate the menu.  <Enter> selects submenus ---> (or empty submenus ----).          │  
  │  Highlighted letters are hotkeys.  Pressing <Y> includes, <N> excludes, <M> modularizes          │  
  │  features.  Press <Esc><Esc> to exit, <?> for Help, </> for Search.  Legend: [*] built-in  [ ]
  │  excluded  <M> module  < > module capable                                                        │  
  │ ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ │  
  │ │            <*> kmod-i2c-core................................................ I2C support     │ │  
  │ │            -*-   kmod-i2c-algo-bit............................. I2C bit-banging interfaces   │ │  
  │ │            < >   kmod-i2c-algo-pca................................ I2C PCA 9564 interfaces   │ │  
  │ │            < >   kmod-i2c-algo-pcf................................ I2C PCF 8584 interfaces   │ │  
  │ │            <*>   kmod-i2c-gpio.................................. GPIO-based bitbanging I2C   │ │  
  │ │            < > kmod-i2c-gpio-custom........................ Custom GPIO-based I2C device     │ │  
  │ │            < > kmod-i2c-mt7628................................. MT7628/88 I2C Controller     │ │  
  │ │            < > kmod-i2c-mux................................ I2C bus multiplexing support     │ │  
  │ │            < > kmod-i2c-smbus........................... SMBus-specific protocols helper     │ │  
  │ │            < > kmod-i2c-tiny-usb................................... I2C Tiny USB adaptor     │ │  
  │ │                                                                                              │ │  
  │ │                                                                                              │ │  
  │ │                                                                                              │ │  
  │ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │  
  ├──────────────────────────────────────────────────────────────────────────────────────────────────┤  <Select>    < Exit >    < Help >    < Save >    < Load >
  └──────────────────────────────────────────────────────────────────────────────────────────────────┘  
  • There are two ways to read:
    1. Write driver, read and write after loading
    2. No driver is needed, operate i2c bus read and write directly at the application layer

  • Driver writing
    td1030.c

/*
 * gps 
 *
 * Written by: eric
 *
 *
 */


// #define DEBUG 
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/hwmon-sysfs.h>
#include <linux/uaccess.h>

#include <linux/delay.h>


// -------------------------------|-同步头--|--识别码--|---长度---|GGA|端口-|频率|保存|--校验---|
static const uint8_t gga_config[]={
    
    0x23,0x3e,0x03,0x51,0x04,0x00,0x04,0x04,0x01,0x01,0x62,0x86};
static const uint8_t gsv_config[]={
    
    0x23,0x3e,0x03,0x51,0x04,0x00,0x06,0x04,0x01,0x01,0x64,0x8e};
static const uint8_t rmc_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x03, 0x04, 0x01, 0x01, 0x61, 0x82};
static const uint8_t gsa_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x05, 0x04, 0x01, 0x01, 0x63, 0x8A};
static const uint8_t txt_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x0D, 0x04, 0x01, 0x01, 0x6B, 0xAA};

// ACK:233E0101 
// NACK:233E0102
static const uint8_t td1030_ack[]={
    
    0x23,0x3E,0x01,0x01};
static const uint8_t td1030_nack[]={
    
    0x23,0x3E,0x01,0x02};

/* Device registers */
#define TD1030_REG_CONFIG	0x00
#define TD1030_READ_LEN		(256)

/**
 * @brief  hex数据转字符串
 * @note   
 * @param  data[]: hex数据
 * @param  data_len: hex数据长度
 * @param  *string: 输出字符串
 * @param  string_len: 输出字符串缓冲区长度
 * @retval 
 */
static int8_t hex2str(uint8_t data[], uint32_t data_len, uint8_t *string, uint32_t string_len)
{
    
    
    uint32_t i;
    uint8_t high_8bits_string = 0;
    uint8_t low_8bits_string = 0;

    if ( (2 * data_len) > string_len ) {
    
    
        return -1;
    }

    for (i = 0; i < data_len; i++) {
    
    
        /*parse high 8 bits value*/
        high_8bits_string = data[i] / 16;
        if (high_8bits_string <= 9) {
    
    
            string[2 * i] = high_8bits_string + '0';
        } else if (10 <= high_8bits_string && high_8bits_string <= 15) {
    
    
            string[2 * i] = high_8bits_string - 10 + 'A';
        } else {
    
    
            return -2;
        }

        /*parse low 8 bits value*/
        low_8bits_string = data[i] % 16;
        if (low_8bits_string <= 9) {
    
    
            string[2 * i + 1] = low_8bits_string + '0';
        } else if (10 <= low_8bits_string && low_8bits_string <= 15) {
    
    
            string[2 * i + 1] = low_8bits_string - 10 + 'A';
        } else {
    
    
            return -3;
        }
    }

    return 0;
}
static void str_trim(char *src,char c)  
{
    
      
    int i,j;  
    for (i = 0, j = 0; src[i] != '\0'; i++)  
    {
    
      
        if (src[i] != c)  
        {
    
      
            src[j++] = src[i];  
        }  
    }  
    src[j] = '\0'; 
} 
static void show_hex(struct device *dev,char *val,int len)
{
    
    
	char hex[256]={
    
    0};
	memset(hex,0,256);
	hex2str(val,len,hex,256);
	dev_err(dev, "hex=%s\n",hex);
}


/*
 * Generic counter attributes
 */
static ssize_t td1030_show(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
    
    
	struct i2c_client *client = to_i2c_client(dev);
	int rc=0;
	uint8_t *val=NULL;

	if (!(val = kmalloc(TD1030_READ_LEN, GFP_KERNEL)))
		return -ENOMEM;
	memset(val,0,TD1030_READ_LEN);

	rc = i2c_master_recv(client, val, TD1030_READ_LEN-1);
	if (rc < 0) {
    
    
		dev_warn(dev, "i2c read data cmd failed\n");
		goto _err;
	}

	str_trim(val,0xff);
	// show_hex(dev,val,64);
	// dev_dbg(dev, "read val=%s\n",val);

	// char *p = strstr(val,'$');
	// if(!p)
	// 	goto _err;

	rc = sprintf(buf,"%s",val);

	kfree(val);
	return rc;
	// return copy_to_user(buf,val,strlen(val));
_err:
	kfree(val);
	return rc;
}

static ssize_t td1030_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{
    
    
	struct i2c_client *client = to_i2c_client(dev);
	int rc;
	uint8_t *set_buf=gga_config;
	int set_len=sizeof(gga_config);

	dev_dbg(dev, "td1030_store() called on %s\n", attr->attr.name);
	dev_dbg(dev, "set command:%s.\n",buf);

	
	if (!strcmp(buf,"gga"))	{
    
    
		set_buf=gga_config;
		set_len=sizeof(gga_config);
	}else if (!strcmp(buf,"gsv")){
    
    
		set_buf=gsv_config;
		set_len=sizeof(gsv_config);
	}else if (!strcmp(buf,"rmc")){
    
    
		set_buf=rmc_config;
		set_len=sizeof(rmc_config);
	}else if (!strcmp(buf,"gsa")){
    
    
		set_buf=gsa_config;
		set_len=sizeof(gsa_config);
	}else if (!strcmp(buf,"txt")){
    
    
		set_buf=txt_config;
		set_len=sizeof(txt_config);
	}else{
    
    
		// show_hex(dev,buf,count);
		return -EINVAL;
	}
	
	// 初始化
	rc = i2c_master_send(client, set_buf, set_len);
	if (rc < 0){
    
    
		dev_warn(dev, "command failed.rc=%d\n", rc);
		return -EIO;
	}

	// 申请接收区
	uint8_t *val=NULL;
	if (!(val = kmalloc(16, GFP_KERNEL)))
		return -ENOMEM;
	memset(val,0,16);

	// 读取设置响应
	rc = i2c_master_recv(client, val, 16);
	if (rc < 0) {
    
    
		dev_warn(dev, "i2c read data cmd failed\n");
		goto _err;
	}
	str_trim(val,0xff);

	show_hex(dev,val,16);
	// 判断接收到的数据是否为ACK
	if (memcmp(val,td1030_ack,sizeof(td1030_ack))){
    
    
		dev_err(dev, "read cmd nack!\n");
		goto _err;
	}

	dev_dbg(dev, "td1030 store ok\n");

	kfree(val);
	return count;
_err:
	kfree(val);
	return -1;
}

/*
 * Simple register attributes
 */
static DEVICE_ATTR(get, S_IRUGO, td1030_show, NULL);
static DEVICE_ATTR(set, S_IWUSR, NULL, td1030_store);


static const struct attribute_group td1030_group = {
    
    
	.attrs = (struct attribute *[]) {
    
    
		&dev_attr_get.attr,
		&dev_attr_set.attr,
		NULL,
	},
};



/*
 * Called when a td1030 device is matched with this driver
 */
static int td1030_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
    
    
	int rc;

	if (!i2c_check_functionality(client->adapter,
				     I2C_FUNC_I2C)) {
    
    
		dev_err(&client->dev, "i2c bus does not support the td1030\n");
		rc = -ENODEV;
		goto exit;
	}

	rc = sysfs_create_group(&client->dev.kobj, &td1030_group);
	if (rc)
		goto exit;

	dev_dbg(&client->dev, "td1030 probe ok\n");
	return 0;
	
 exit:
	return rc;
}

static int td1030_remove(struct i2c_client *client)
{
    
    
	sysfs_remove_group(&client->dev.kobj, &td1030_group);
	return 0;
}

static const struct i2c_device_id td1030_id[] = {
    
    
	{
    
     "td1030", 0 },
	{
    
     }
};
MODULE_DEVICE_TABLE(i2c, td1030_id);

static const struct of_device_id td1030_of_match[] = {
    
    
	{
    
     .compatible = "td1030", },
	{
    
    }
};
MODULE_DEVICE_TABLE(of, td1030_of_match);

static struct i2c_driver td1030_driver = {
    
    
	.driver = {
    
    
		.name = "td1030",
		.of_match_table = td1030_of_match,
	},
	.probe = td1030_probe,
	.remove = td1030_remove,
	.id_table = td1030_id,
};

module_i2c_driver(td1030_driver);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("td1030 GPS driver");
MODULE_LICENSE("GPL");

  • Load drive test
root@eric:/# ls
bin        init       overlay    root       td1030.ko  var
dev        lib        proc       sbin       tmp        www
etc        mnt        rom        sys        usr
root@eric:/#

...加载驱动
root@eric:/# insmod td1030.ko
root@eric:/# cd sys/bus/i2c/devices/0-0030/
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# ls
driver     modalias   of_node    subsystem
get        name       set        uevent

...设置gga i2c输出
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# echo -n gga > set
[ 3950.540987] td1030 0-0030: hex=233E0101020002000351586AFFFFFFFF
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030#

...读取定位数据
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# cat get
$GNGGA,095516.00,3107.49910,N,12121.62030,E,1,17,0.66,71.0,M,9.8,M,,*4B
$GNGGA,095517.00,3107.49909,N,12121.62029,E,1,16,0.68,71.0,M,9.8,M,,*45
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030#

  • The application layer directly calls the i2c bus read and write mode (introduced in the next chapter)

  1. Due to the hardware limitation of the mt7688 platform, a maximum of 64 bytes can be read at a time, while the td1030 needs to read at least 256 bytes at a time. If it is read several times, valid data will be lost. ↩︎

  2. Simulate i2c configuration https://blog.csdn.net/pyt1234567890/article/details/112990777 ↩︎

Guess you like

Origin blog.csdn.net/pyt1234567890/article/details/112993422