i2c-ch455.c
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/gpio/consumer.h>
#include <linux/uaccess.h>
#include "i2c-ch455.h"
/* 私有数据结构体 */
struct ch455_data
{
dev_t devnum;
spinlock_t lock; /* 资源锁 */
struct cdev ch455_cdev;
struct gpio_desc *sda_gpio;
struct gpio_desc *scl_gpio;
struct gpio_desc *led1_gpio;
struct gpio_desc *led2_gpio;
struct device *ch455_fops_device; /* fops */
struct platform_device *device_ch455; /* platform */
struct class *ch455_class;
// struct i2c_client *ch455_client;
};
/* 传输函数 */
static void CH455_I2c_Start(struct ch455_data *ch455)
{
gpiod_direction_output(ch455->sda_gpio, 1);
udelay(I2C_DELAY);
gpiod_direction_output(ch455->scl_gpio, 1);
udelay(I2C_DELAY);
gpiod_set_value(ch455->sda_gpio, 0); // ch455->sda_gpio = low;
udelay(I2C_DELAY);
gpiod_set_value(ch455->scl_gpio, 0); // ch455->scl_gpio = low;
udelay(I2C_DELAY);
}
static void CH455_I2c_Stop(struct ch455_data *ch455)
{
gpiod_direction_output(ch455->scl_gpio, 0);
udelay(I2C_DELAY);
gpiod_direction_output(ch455->sda_gpio, 0);
udelay(I2C_DELAY);
gpiod_set_value(ch455->scl_gpio, 1); // ch455->scl_gpio = high;
udelay(I2C_DELAY);
gpiod_set_value(ch455->sda_gpio, 1); // ch455->sda_gpio = high;
udelay(I2C_DELAY);
}
static void CH455_I2c_WrByte(struct ch455_data *ch455, u8 IIC_Byte)
{
u8 i;
/* 先拉低数据为等待写入数据 */
//pr_err("*** func:%s line:%d IIC_Byte:%#x ***\n", __func__, __LINE__, IIC_Byte);
gpiod_direction_output(ch455->sda_gpio, 0);
for (i = 0; i < 8; i++)
{
if ((IIC_Byte & 0x80))
gpiod_set_value(ch455->sda_gpio, 1); // ch455->sda_gpio=high;
else
gpiod_set_value(ch455->sda_gpio, 0); // ch455->sda_gpio=low;
udelay(100);
gpiod_set_value(ch455->scl_gpio, 1); // ch455->scl_gpio=high;
udelay(100);
gpiod_set_value(ch455->scl_gpio, 0); // ch455->scl_gpio=low;
udelay(100);
IIC_Byte <<= 1;
}
}
#if 1
/* 产生一个ACK */
static void CH455_I2c_Send_ACK(struct ch455_data *ch455)
{
gpiod_direction_output(ch455->scl_gpio, 0);
udelay(I2C_DELAY);
gpiod_direction_output(ch455->sda_gpio, 0);
udelay(I2C_DELAY);
gpiod_set_value(ch455->scl_gpio, 1);
udelay(I2C_DELAY);
gpiod_set_value(ch455->scl_gpio, 0);
}
#endif
#if 0
/* 释放sda,等待ack信号 */
static u8 CH455_I2c_Wait_ACK(struct ch455_data *ch455)
{
if (!ch455)
return ERRRES;
gpiod_direction_output(ch455->sda_gpio, 1);
udelay(1);
gpiod_direction_output(ch455->scl_gpio, 1);
udelay(1);
gpiod_direction_input(ch455->sda_gpio);
udelay(300);
/* 如果被拉低代表回应一个ack信号 */
if (gpiod_get_value(ch455->sda_gpio))
{
pr_err("func:%s line:%d sda_stat:%d\n", __func__, __LINE__, gpiod_get_value(ch455->sda_gpio));
CH455_I2c_Stop(ch455);
return ERRRES;
}
gpiod_direction_output(ch455->scl_gpio, 0);
return SUCRES;
}
#endif
static int ch455_write_data(struct ch455_data *ch455, u16 data)
{
int i;
unsigned long flags;
if (!ch455)
return ERRRES;
pr_err("func:%s, line:%d, data:%#x\n", __func__, __LINE__, data);
spin_lock_irqsave(&ch455->lock, flags);
CH455_I2c_Start(ch455);
/* data = cmd1 | cmd2 */
for (i = 1; i >= 0; i--)
{
CH455_I2c_WrByte(ch455, (u8)(data >> (i * 8)));
#if 0
res = CH455_I2c_Wait_ACK(ch455);
if(res < 0) {
pr_err("func:%s: i2c transfer failed\n", __func__);
return ERRRES;
}
#endif
CH455_I2c_Send_ACK(ch455);
}
CH455_I2c_Stop(ch455);
spin_unlock_irqrestore(&ch455->lock, flags);
pr_err("func:%s, line:%d\n", __func__, __LINE__);
return SUCRES;
}
static int show_data(struct ch455_data *ch455, u8 *data)
{
int i;
int j;
int res = LEDNUM;
u8 *buf;
u8 *temp;
u8 DIG[] = {CH455_DIG0, CH455_DIG1, CH455_DIG2, CH455_DIG3};
/* 判断传入的值的数量 */
(res < strlen(data)) ? (res = strlen(data)) : res;
buf = kzalloc(res, GFP_KERNEL);
temp = kzalloc(res, GFP_KERNEL);
memset(buf, 0, res);
memset(temp, 0, res);
memcpy(temp, data, res);
for (i = 0; i < res; i++)
{
if (temp[i] >= '0' && temp[i] <= '9')
buf[i] = real_code[(data[i] - '0')];
else if (temp[i] >= 'a' && temp[i] <= 'f')
buf[i] = real_code[(data[i] - 'a' + 10)];
else if (temp[i] == '.')
{
buf[i - 1] |= 0x80; /* 第八段是seg7控制的,如果上一位有点,数据最高位与上一个1就行 */
temp[i] = 'h'; /* 超过f就行 */
i--;
}
else
continue;
}
j = 0;
for (i = 0; i < res; i++)
{
if (buf[i])
{
if (ch455_write_data(ch455, ((DIG[j++] << 8) | buf[i])) < 0)
goto err_alloc;
}
}
pr_err("*** func:%s line:%d res:%d ***\n", __func__, __LINE__, res);
kfree(buf);
kfree(temp);
return SUCRES;
err_alloc:
kfree(buf);
pr_err("ch455 write data failed\n");
kfree(temp);
return ERRRES;
}
/* 控制led */
static int ctr_led(struct ch455_data *ch455, u8 *data)
{
unsigned long flags;
if (!ch455 || !data || strlen(data) < 6)
{
return ERRRES;
}
spin_lock_irqsave(&ch455->lock, flags);
switch (data[3])
{
case '1':
if ('n' == data[5])
{
gpiod_set_value(ch455->led1_gpio, 1);
}
else
{
gpiod_set_value(ch455->led1_gpio, 0);
}
break;
case '2':
if ('n' == data[5])
{
gpiod_set_value(ch455->led2_gpio, 1);
}
else
{
gpiod_set_value(ch455->led2_gpio, 0);
}
break;
default:
break;
}
spin_unlock_irqrestore(&ch455->lock, flags);
return SUCRES;
}
static int ch455_open(struct inode *node, struct file *filp)
{
struct ch455_data *ch455;
ch455 = container_of(node->i_cdev, struct ch455_data, ch455_cdev);
if (IS_ERR(ch455))
goto err;
filp->private_data = ch455; /* 保存数据到文件结构体file链表中 */
return SUCRES;
err:
pr_err("%s: set ch455 drvdata faile\n", __func__);
return PTR_ERR(ch455);
}
static long ch455_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
/* 用于初始化led,传入打印数值, 控制led灯亮灭 */
int res;
u16 data;
//unsigned long flags;
struct ch455_data *ch455;
ch455 = filp->private_data;
if (!ch455)
goto err;
//spin_lock_irqsave(&ch455->lock, flags);
switch (cmd)
{
case LED1ON:
res = ctr_led(ch455, "led1on");
if (res < 0)
goto err;
break;
case LED1OFF:
res = ctr_led(ch455, "led1off");
if (res < 0)
goto err;
break;
case LED2ON:
res = ctr_led(ch455, "led2on");
if (res < 0)
goto err;
break;
case LED2OFF:
res = ctr_led(ch455, "led2off");
if (res < 0)
goto err;
break;
case NIEXIDATA:
if (copy_from_user(&data, (u16 *)arg, sizeof(u16)))
goto err_copy;
res = ch455_write_data(ch455, data);
if (res < 0)
goto err;
default:
break;
}
//spin_unlock_irqrestore(&ch455->lock, flags);
return SUCRES;
err_copy:
pr_err("copy from user failed!\n");
err:
if (!ch455)
pr_err("%s: get ch455 drvdata faile\n", __func__);
if (res < 0)
pr_err("%s: ch455 write data faile\n", __func__);
//spin_unlock_irqrestore(&ch455->lock, flags);
return ERRRES;
}
static struct file_operations ch455_opr = {
.owner = THIS_MODULE,
.open = ch455_open,
.unlocked_ioctl = ch455_ioctl,
};
/* sys debug */
static ssize_t ch455_dev_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
/* echo值进来直接显示,或者控制led亮灭 */
int res;
char port_buf[5];
char value_buf[4];
struct ch455_data *ch455;
struct platform_device *ch455_client;
pr_err("*** func:%s line:%d buf:%s count:%ld ***\n", __func__, __LINE__,
buf, count);
ch455_client = container_of(dev, struct platform_device, dev);
if (IS_ERR(ch455_client))
goto err;
ch455 = platform_get_drvdata(ch455_client);
if (IS_ERR(ch455))
goto err;
if (count < 9 || buf[3] != ' ')
{
pr_err("<%s> invalid write string: %s\n", __func__, buf);
pr_err("example: echo \"smg\" \"1234\" > xxx/ch455_dev\n");
pr_err("example: echo \"led\" \"led1on\" > xxx/ch455_dev\n");
return ERRRES;
}
sscanf(buf, "%s %s", port_buf, value_buf);
pr_err(" func:%s line:%d buf[0]:%c \n", __func__, __LINE__,
port_buf[0]);
switch (port_buf[0])
{
case 's':
pr_err(" func:%s line:%d \n", __func__, __LINE__);
res = show_data(ch455, value_buf);
break;
case 'l':
pr_err(" func:%s line:%d \n", __func__, __LINE__);
res = ctr_led(ch455, value_buf);
break;
default:
break;
}
pr_err("*** func:%s line:%d ***\n", __func__, __LINE__);
return (res > 0) ? res : ERRRES;
err:
pr_err("cannot get drvdate\n");
if (IS_ERR(ch455_client))
return PTR_ERR(ch455_client);
else
return PTR_ERR(ch455);
}
static DEVICE_ATTR(ch455_dev, S_IWUSR | S_IWGRP, NULL, ch455_dev_store);
static CLASS_ATTR_STRING(version, S_IRUGO, "CH455 1.0.0 20231022");
static int ch455_probe(struct platform_device *ch455_dev)
{
int i;
int res;
int major;
dev_t devnum;
struct device *ch455device;
struct ch455_data *ch455;
pr_err("*** func:%s line:%d ***\n", __func__, __LINE__);
ch455 = devm_kzalloc(&ch455_dev->dev, sizeof(struct ch455_data),
GFP_KERNEL);
if (IS_ERR(ch455))
goto err_kzalloc;
major = MAJOR(devnum);
ch455->device_ch455 = ch455_dev;
ch455device = ch455->ch455_fops_device;
/* 完成字符设备驱动的注册 */
cdev_init(&ch455->ch455_cdev, &ch455_opr);
res = cdev_add(&ch455->ch455_cdev, MKDEV(major, 0), DEVINUM);
if (res)
return ERRRES;
ch455->ch455_class = class_create(THIS_MODULE, "ch455");
if (IS_ERR(ch455->ch455_class))
goto err_class;
res = class_create_file(ch455->ch455_class, &class_attr_version.attr);
if (res)
goto err_create_file;
res = alloc_chrdev_region(&devnum, 0, DEVINUM, "ch455");
if (res)
{
goto err_alloc_cr;
}
for (i = 0; i < DEVINUM; i++)
{
ch455->ch455_fops_device =
device_create(ch455->ch455_class, NULL, MKDEV(major, i),
NULL, "ch455.%d", i);
if (IS_ERR(ch455->ch455_fops_device))
goto err_device_create;
}
res = device_create_file(&ch455_dev->dev, &dev_attr_ch455_dev);
if (res)
goto err_device_create;
spin_lock_init(&ch455->lock);
platform_set_drvdata(ch455_dev, ch455);
/* 获取gpio */
ch455->scl_gpio = devm_gpiod_get_optional(&ch455_dev->dev, "scl", 0);
if (IS_ERR(ch455->scl_gpio))
{
pr_err("devm gpiod get opt failed\n");
goto err_device_create;
}
ch455->sda_gpio = devm_gpiod_get_optional(&ch455_dev->dev, "sda", 0);
if (IS_ERR(ch455->sda_gpio))
{
pr_err("devm gpiod get opt failed\n");
goto err_device_create;
}
ch455->led1_gpio = devm_gpiod_get_optional(&ch455_dev->dev, "led1", 0);
if (IS_ERR(ch455->led1_gpio))
{
pr_err("devm gpiod get opt failed\n");
goto err_device_create;
}
ch455->led2_gpio = devm_gpiod_get_optional(&ch455_dev->dev, "led2", 0);
if (IS_ERR(ch455->led2_gpio))
{
pr_err("devm gpiod get opt failed\n");
goto err_device_create;
}
/* 这里是不是需要加个延时 */
gpiod_direction_output(ch455->led1_gpio, 0);
gpiod_direction_output(ch455->led2_gpio, 0);
/* test */
/* 打开数码管八段显示 */
ch455_write_data(ch455, ((SYSTEM_CONFIG << 8) | 0x01));
//ch455_write_data(ch455, ((CH455_DIG0 << 8) | 0xFF));
//ch455_write_data(ch455, ((CH455_DIG1 << 8) | 0xFF));
//ch455_write_data(ch455, ((CH455_DIG2 << 8) | 0xFF));
//ch455_write_data(ch455, ((CH455_DIG3 << 8) | 0xFF));
pr_err("*** func:%s line:%d ***\n", __func__, __LINE__);
return SUCRES;
err_device_create:
for (--i; i >= 0; i--)
device_destroy(ch455->ch455_class, MKDEV(major, i));
unregister_chrdev_region(devnum, DEVINUM);
err_alloc_cr:
class_remove_file(ch455->ch455_class, &class_attr_version.attr);
err_create_file:
class_destroy(ch455->ch455_class);
err_class:
cdev_del(&ch455->ch455_cdev);
err_kzalloc:
if (IS_ERR(ch455))
{
pr_err("ch455 set pri data failed!\n");
return PTR_ERR(ch455);
}
return ERRRES;
}
static int ch455_remove(struct platform_device *ch455_dev)
{
int i;
int major;
struct ch455_data *ch455;
ch455 = platform_get_drvdata(ch455_dev);
if (IS_ERR(ch455))
goto err;
major = MAJOR(ch455->devnum);
device_remove_file(ch455->ch455_fops_device, &dev_attr_ch455_dev);
for (--i; i >= 0; i--)
device_destroy(ch455->ch455_class, MKDEV(major, i));
cdev_del(&ch455->ch455_cdev);
unregister_chrdev_region(ch455->devnum, DEVINUM);
class_remove_file(ch455->ch455_class, &class_attr_version.attr);
class_destroy(ch455->ch455_class);
return 0;
err:
pr_err("i2c get drvdate failed!\n");
return PTR_ERR(ch455);
}
static struct of_device_id ch455_id[] = {
{
.compatible = "test,ch455",
},
{},
};
static struct platform_driver ch455_dri = {
.driver = {
.owner = THIS_MODULE,
.name = "ch455_driver",
.of_match_table = ch455_id,
},
.probe = ch455_probe,
.remove = ch455_remove,
};
static int __init ch455_init(void)
{
int res;
res = platform_driver_register(&ch455_dri);
if (res)
return ERRRES;
return SUCRES;
}
static void __exit ch455_exit(void)
{
platform_driver_unregister(&ch455_dri);
}
module_init(ch455_init);
module_exit(ch455_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("neardi@wilson.yang");