1、创建字符设备
如果不是I2C的驱动设备,要创建一个字符设备,方法如下:
定义全局变量:
//字符设备
#define SOCTOMCU_DEVICE_NAME “soctomcu”
tatic int soctomcu_major = 0;
static struct class *soctomcu_class;
struct device *soctomcu_device;
#define FRAME_LEN_MAX 132
typedef struct{
unsigned char buf[FRAME_LEN_MAX];
int len;
}soctomcu_frame;
//I2C设备
struct i2c_client *i2c_connect_client;
struct SOCTOR78I2C_data{
struct i2c_client *SOCTOR78I2C_client;
struct delayed_work work;
};
static struct SOCTOR78I2C_data *SOCTOR78I2C_i2c;
定义文件系统接口:
static const struct file_operations soctomcu_fops = {
.owner = THIS_MODULE,
.open = soctomcu_open,
.release = soctomcu_close,
.read = socto_lvdsmcu_read,
.write = socto_lvdsmcu_write,
};
在init初始化函数中:
static int __init soctor78_init(void)
{
/*创建I2C设备定义/
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret=0;
…
//获取主设备号
soctomcu_major = register_chrdev(0, SOCTOMCU_DEVICE_NAME, &soctomcu_fops);
if(soctomcu_major < 0){
unregister_chrdev(soctomcu_major, SOCTOMCU_DEVICE_NAME);
pr_info(“soctomcu register_chrdev fail!\n”);
ret =soctomcu_major;
}
//获取设备类
soctomcu_class = class_create(THIS_MODULE, SOCTOMCU_DEVICE_NAME);
if (IS_ERR(soctomcu_class)) {
class_destroy(soctomcu_class);
printk(KERN_ERR “Error creating class.\n”);
ret = -1;
}
//创建字符设备
soctomcu_device = device_create(soctomcu_class, NULL, MKDEV(soctomcu_major, 0), NULL, SOCTOMCU_DEVICE_NAME);
if (IS_ERR(soctomcu_device)) {
device_destroy(soctomcu_class, MKDEV(soctomcu_major, 0));
printk(KERN_ERR “Error creating class device.\n”);
ret = -1;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = SOCTOR78I2C_ADDR;
strlcpy(info.type, “SocToR78_I2c”, I2C_NAME_SIZE);
adapter = i2c_get_adapter(0); //创建I2C适配器,后面的i2c_transfer()函数需要用到这个适配器
if (!adapter)
{
printk(KERN_ERR "Can't get i2c adapter 0\n");
//kfree(pdata);
ret = -ENODEV;
goto exit;
}
client = i2c_new_device(adapter, &info); //取到client,后面传给probe()函数,在probe()函数中保存给后面的i2C通信函数使用
//printk("i2c_new_device::client=0x%x\n",*client);
i2c_put_adapter(adapter);
if (!client)
{
printk(KERN_ERR "Can't add i2c device at 0x%x\n", (unsigned int)info.addr);
//kfree(pdata);
ret = -ENOMEM;
goto exit;
}
printk(“soctor78 driver installing…\n”);
return i2c_add_driver(&soctor78_driver);
}
static int __devinit soctor78_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret=0;
struct SOCTOR78I2C_data *ts_pdata;
client->addr=SOCTOR78I2C_ADDR;
i2c_connect_client = client;
if(!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE|I2C_FUNC_SMBUS_BYTE_DATA)){
printk("[mcui2c]:i2c_check_functionality failed \n");
return -1;
}
ts_pdata = kzalloc(sizeof(struct SOCTOR78I2C_data), GFP_KERNEL);
if (!ts_pdata)
return -ENOMEM;
ts_pdata->SOCTOR78I2C_client=client;
i2c_set_clientdata(client,ts_pdata);
SOCTOR78I2C_i2c = ts_pdata;
SOCTOR78I2C_i2c->SOCTOR78I2C_client =client;
SOCTOR78I2C_i2c->SOCTOR78I2C_client->adapter=client->adapter;
return 0;
}
后面使用probe()函数中保存的client取到adapter用于I2C接口通信
I2C写:
static int LvdsMcuWrite(int wLen,unsigned char *wData)
{
struct SOCTOR78I2C_data *pata=i2c_get_clientdata(i2c_connect_client);//从保存的client中取到client数据,后面用于I2C数据传输pata->SOCTOR78I2C_client->adapter
struct i2c_msg msgs[1];
unsigned char WriteData[10];
unsigned int msg0_len=0;
unsigned char i=0;
unsigned char j=0;
unsigned int ret=0;
WriteData[i++]=0xFF;
WriteData[i++]=0x55;
for(j=0; j<(wLen);j++){
WriteData[i++]=*(wData+j);
}
WriteData[i++]=SOCWriteIIC_GetSum((wLen),&WriteData[3]);
msg0_len = wLen+3;
for(i=0;i< msg0_len;i++)
printk("WriteData[%d]=0x%x\n",i,WriteData[i]);
msgs[0].flags = !I2C_M_RD;
msgs[0].addr = pata->SOCTOR78I2C_client->addr;
msgs[0].len = msg0_len;
msgs[0].buf = &WriteData[0];
write_read_cmd =WriteData[4];
printk("I2cToRt87::i2c Read write :: write_read_cmd=0x%x\n",write_read_cmd);
ret = i2c_transfer(pata->SOCTOR78I2C_client->adapter, msgs,1);
if(ret < 0){
printk("I2cToRt87::i2c Read write error ret=%d\n",ret);
}
printk("I2cToRt87::i2c Read write OK ret=%d\n",ret);
return ret;
}
I2C读:
static int LvdsMcuRead(int rLen,unsigned char *rData)
{
struct SOCTOR78I2C_data *pata=i2c_get_clientdata(i2c_connect_client);
struct i2c_msg msgs[1];
unsigned char ReadTPAckData[20];
unsigned char i=0;
unsigned int ret=0;
printk("LvdsMcuRead()::rLen=%d,i2c_connect_client->addr=0x%x\n",rLen,i2c_connect_client->addr);
msgs[0].flags = I2C_M_RD;
msgs[0].addr = pata->SOCTOR78I2C_client->addr;
msgs[0].len = rLen;
msgs[0].buf = &ReadTPAckData[0];
ret = i2c_transfer(pata->SOCTOR78I2C_client->adapter, msgs,1);
if(ret < 0){
printk("I2cToRt87::i2c Read write error ret=%d\n",ret);
}
for(i=0;i<rLen;i++){
//printk("I2cToRt87::ReadTPAckData[%d]=0x%x\n",i,ReadTPAckData[i]);
*(rData+i)=ReadTPAckData[i];
}
return ret;
}
例如复位TP的通信指令:
const unsigned char TOUCHPANEL_RESET[]={0x03,0x10,0x13};
int Lvdsmcu_Resettp(void)
{
unsigned char wTempdata[10];
unsigned char ret=0;
wTempdata[0]=TOUCHPANEL_RESET[0];
wTempdata[1]=TOUCHPANEL_RESET[1];
wTempdata[2]=TOUCHPANEL_RESET[2];
ret=LvdsMcuWrite(wTempdata[0], &wTempdata[0]);
if(ret<0){
printk("LvdsMcuWrite()::touchpannel reset error ret=%d\n",ret);
return ret;
}
printk("LvdsMcuWrite()::touchpannel reset ok ret=%d\n",ret);
return ret;
}
最后整个驱动代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/mach-types.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/regulator/consumer.h>
#include <linux/fsl_devices.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
/*Winfred Young add for tftloader*/
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/poll.h>
#include <linux/gpio.h>
/**************soc to mcu communication protocol ****************/
#define SOCTOR78I2C_NAME "SocToR78_I2c"
#define SOCTOR78I2C_NAME_SIZE 20
#define SOCTOR78I2C_ADDR 0x3c //0x78
//add soc to R87 connect test
const unsigned char GET_COMMUNICATIONDET[]={0x03,0x10,0x88};
const unsigned char TOUCHPANEL_RESET[]={0x03,0x10,0x13};
struct i2c_client *i2c_connect_client;
struct SOCTOR78I2C_data{
struct i2c_client *SOCTOR78I2C_client;
struct delayed_work work;
};
static struct SOCTOR78I2C_data *SOCTOR78I2C_i2c;
/********************************************************
*Soc to mcu IO ctrl interface
************************************************************/
#define SOCTOMCU_DEVICE_NAME "soctomcu"
//static DECLARE_WAIT_QUEUE_HEAD(inq);
//int is_read_frame = 0;
static unsigned char write_read_cmd =0x00;
static int soctomcu_major = 0;
static struct class *soctomcu_class;
struct device *soctomcu_device;
static int soctomcuopen=0;
#define FRAME_LEN_MAX 132
typedef struct{
unsigned char buf[FRAME_LEN_MAX];
int len;
}soctomcu_frame;
static spinlock_t mcu_lock;
/**************************************END ***************************/
unsigned char SOCWriteIIC_GetSum(unsigned char cLen,unsigned char *cData)
{
unsigned char i;
unsigned char checkSum=0;
for(i=0; i<(cLen-1); i++){
checkSum += *(cData+i);
}
return(checkSum);
}
/**********************************************************************
wLen: Packet Length:Byte count from CID to CSUM
wData: Byte count from CID to Para-N
1 frame data length=package head + 1byte wlwn+wLen = 2+1+wLen
***********************************************************************/
//static int LvdsMcuWrite(unsigned char wLen,unsigned char *wData)
static int LvdsMcuWrite(int wLen,unsigned char *wData)
{
struct SOCTOR78I2C_data *pata=i2c_get_clientdata(i2c_connect_client);;
struct i2c_msg msgs[1];
unsigned char WriteData[10];
unsigned int msg0_len=0;
unsigned char i=0;
unsigned char j=0;
unsigned int ret=0;
WriteData[i++]=0xFF;
WriteData[i++]=0x55;
for(j=0; j<(wLen);j++){
WriteData[i++]=*(wData+j);
}
WriteData[i++]=SOCWriteIIC_GetSum((wLen),&WriteData[3]);
msg0_len = wLen+3;
for(i=0;i< msg0_len;i++)
printk("WriteData[%d]=0x%x\n",i,WriteData[i]);
msgs[0].flags = !I2C_M_RD;
msgs[0].addr = pata->SOCTOR78I2C_client->addr;
msgs[0].len = msg0_len;
msgs[0].buf = &WriteData[0];
write_read_cmd =WriteData[4];
printk("I2cToRt87::i2c Read write :: write_read_cmd=0x%x\n",write_read_cmd);
ret = i2c_transfer(pata->SOCTOR78I2C_client->adapter, msgs,1);
if(ret < 0){
printk("I2cToRt87::i2c Read write error ret=%d\n",ret);
}
printk("I2cToRt87::i2c Read write OK ret=%d\n",ret);
return ret;
}
/*************************************************************
rLen: 1 frame data length
rData: read 1 frame data length
1 frame data length=package head + 1byte wlwn+wLen = 2+1+wLen
****************************************************************/
//static int LvdsMcuRead(unsigned char rLen,unsigned char *rData)
static int LvdsMcuRead(int rLen,unsigned char *rData)
{
struct SOCTOR78I2C_data *pata=i2c_get_clientdata(i2c_connect_client);
struct i2c_msg msgs[1];
unsigned char ReadTPAckData[20];
unsigned char i=0;
unsigned int ret=0;
printk("LvdsMcuRead()::rLen=%d,i2c_connect_client->addr=0x%x\n",rLen,i2c_connect_client->addr);
msgs[0].flags = I2C_M_RD;
msgs[0].addr = pata->SOCTOR78I2C_client->addr;
msgs[0].len = rLen;
msgs[0].buf = &ReadTPAckData[0];
ret = i2c_transfer(pata->SOCTOR78I2C_client->adapter, msgs,1);
if(ret < 0){
printk("I2cToRt87::i2c Read write error ret=%d\n",ret);
}
for(i=0;i<rLen;i++){
//printk("I2cToRt87::ReadTPAckData[%d]=0x%x\n",i,ReadTPAckData[i]);
*(rData+i)=ReadTPAckData[i];
}
return ret;
}
int Lvdsmcu_Resettp(void)
{
unsigned char wTempdata[10];
unsigned char ret=0;
wTempdata[0]=TOUCHPANEL_RESET[0];
wTempdata[1]=TOUCHPANEL_RESET[1];
wTempdata[2]=TOUCHPANEL_RESET[2];
ret=LvdsMcuWrite(wTempdata[0], &wTempdata[0]);
if(ret<0){
printk("LvdsMcuWrite()::touchpannel reset error ret=%d\n",ret);
return ret;
}
printk("LvdsMcuWrite()::touchpannel reset ok ret=%d\n",ret);
return ret;
}
EXPORT_SYMBOL(Lvdsmcu_Resettp);
int SocConnect_Rt87Mcu(void)
{
unsigned char i=0,ret=0;
unsigned char ReadR17Data[10];
unsigned char WriteData[10];
WriteData[0]=GET_COMMUNICATIONDET[0];
WriteData[1]=GET_COMMUNICATIONDET[1];
WriteData[2]=GET_COMMUNICATIONDET[2];
ret=LvdsMcuWrite(WriteData[0], &WriteData[0]);
if(ret<0){
printk("LvdsMcuWrite()::touchpannel reset error ret=%d\n",ret);
return ret;
}
printk("LvdsMcuWrite()::touchpannel reset ok ret=%d\n",ret);
msleep(30);
ret=LvdsMcuRead(9,&ReadR17Data[0]); //read 9 byte
if(ret<0){
printk("LvdsMcuRead()::touchpannel reset ACK error ret=%d\n",ret);
return ret;
}
for(i=0;i<9;i++)
printk("I2cToRt87::*(rData+%d)=0x%x\n",i,ReadR17Data[i]);
return ret;
}
EXPORT_SYMBOL(SocConnect_Rt87Mcu);
static int __devinit soctor78_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret=0;
struct SOCTOR78I2C_data *ts_pdata;
//struct i2c_adapter *adapter;
//struct i2c_msg msgs[1];
//unsigned char ReadR17Data[10];
//unsigned char WriteData[10];
//unsigned char PwmData[2];
client->addr=SOCTOR78I2C_ADDR;
//printk("soctor78_probe::client=0x%x\n",*client);
i2c_connect_client = client;
if(!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE|I2C_FUNC_SMBUS_BYTE_DATA)){
printk("[mcui2c]:i2c_check_functionality failed \n");
return -1;
}
printk("[soctor78]:%s\n",__FUNCTION__);
ts_pdata = kzalloc(sizeof(struct SOCTOR78I2C_data), GFP_KERNEL);
if (!ts_pdata)
return -ENOMEM;
ts_pdata->SOCTOR78I2C_client=client;
i2c_set_clientdata(client,ts_pdata);
SOCTOR78I2C_i2c = ts_pdata;
SOCTOR78I2C_i2c->SOCTOR78I2C_client =client;
SOCTOR78I2C_i2c->SOCTOR78I2C_client->adapter=client->adapter;
/*********************************************************/
/
//add soc to R87 mcu connect tes dect
/* ret= SocConnect_Rt87Mcu();
if(ret < 0){
printk("SocConnect_Rt87Mcu failed!\n");
return -1;
}
msleep(20);
*/
return 0;
}
static int soctor78_remove(struct i2c_client *client)
{
int ret=0;
struct mcui2c *pdata=i2c_get_clientdata(client);
i2c_set_clientdata(client,NULL);
kfree(pdata);
return ret;
}
//I2C devie id
static const struct i2c_device_id soctor78_id[]={
{SOCTOR78I2C_NAME,0},
{ }
};
MODULE_DEVICE_TABLE(i2c,soctor78_id);
static struct i2c_driver soctor78_driver={
.driver={
.name = SOCTOR78I2C_NAME,
.owner = THIS_MODULE,
},
.probe = soctor78_probe,
.remove = __devexit_p(soctor78_remove),
.id_table = soctor78_id,
};
/****************Analysis of communication protocol************************/
/*************************soc to mcu file system interface *******************/
static void soctomcu_lock_init(void)
{
/* init lock */
spin_lock_init(&mcu_lock);
}
static void soctomcu_lock(void)
{
spin_lock(&mcu_lock);
}
static void soctomcu_unlock(void)
{
spin_unlock(&mcu_lock);
}
static int soctomcu_open(struct inode* inode, struct file* filep)
{
printk("<semisky><soctomcu open>soctomcu_open++\n");
if (soctomcuopen) {
printk(KERN_DEBUG "device_open() - Returning EBUSY. \
Device already open... \n");
return -EBUSY;
}
soctomcu_lock();
soctomcuopen++;
soctomcu_unlock();
try_module_get(THIS_MODULE);
return 0;
}
static int soctomcu_close(struct inode *inode, struct file *filep)
{
printk("<semisky><soctomcu close>soctomcu_close++\n");
soctomcu_lock();
soctomcuopen--;
soctomcu_unlock();
module_put(THIS_MODULE);
return 0;
}
static void printFrame(soctomcu_frame * frame)
{
int index;
return ;
for(index = 0; index < frame->len; index++)
{
pr_info("<semisky> soctomcu_package[%d]=0x%02x\n",index,frame->buf[index]);
}
}
/***************************************************************/
static ssize_t socto_lvdsmcu_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
soctomcu_frame mcu_read_pkg;
int i;
printk("<semisky><mcu read>soctomcu_read++\n");
memset(&mcu_read_pkg, 0, sizeof(soctomcu_frame));
mcu_read_pkg.len=count;
soctomcu_lock();
LvdsMcuRead(mcu_read_pkg.len,&mcu_read_pkg.buf[0]);
soctomcu_unlock();
printFrame(&mcu_read_pkg);
if(copy_to_user((unsigned char *)buf, &mcu_read_pkg.buf[0], count)){
printk("<semisky><mcu_udpate>SOCTOMCU_READ_PACKAGE, copy fail\n");
return -1;
}
printk("<semisky><mcu_udpate>SOCTOMCU_READ_PACKAGE ok\n");
return 0;
}
/***************************************************************/
static ssize_t socto_lvdsmcu_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
soctomcu_frame soctomcu_write_pkg;
unsigned char soctomcu_cmd ;
int i=0;
printk("<semisky><soctomcu write>soctomcu_write++\n");
memset(&soctomcu_write_pkg, 0, sizeof(soctomcu_frame));
if (copy_from_user(&soctomcu_write_pkg.buf[0], (unsigned char*)buf, count)) {
printk("<semisky><SOCTOMCU_WRITE>write farme buff error\n");
}
soctomcu_write_pkg.len=count;
for(i=0;i<soctomcu_write_pkg.len;i++)
printk("<soctomcu write>soctomcu_write_pkg.buf[%d]=0x%x\n",i,soctomcu_write_pkg.buf[i]);
soctomcu_cmd = soctomcu_write_pkg.buf[2];
printk("%s, soctomcu_cmd = 0x%x,soctomcu_write_pkg.len=%d\n",__FUNCTION__,soctomcu_cmd,soctomcu_write_pkg.len);
soctomcu_lock();
LvdsMcuWrite(soctomcu_write_pkg.len, &soctomcu_write_pkg.buf[0]);
soctomcu_unlock();
return 0;
}
/***************************************************************/
static const struct file_operations soctomcu_fops = {
.owner = THIS_MODULE,
.open = soctomcu_open,
.release = soctomcu_close,
.read = socto_lvdsmcu_read,
.write = socto_lvdsmcu_write,
};
/********************************END*************************/
static int __init soctor78_init(void)
{
//struct goodix_ts_platform_data *pdata;
//struct SOCTOR78I2C_data *pdata;
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret=0;
printk("%s\n",__FUNCTION__);
/********************************creat char device node*******************/
/* init access lock */
soctomcu_lock_init();
soctomcuopen = 0;
//printk("<semisky><soctomcu >soctomcu_init++\n");
soctomcu_major = register_chrdev(0, SOCTOMCU_DEVICE_NAME, &soctomcu_fops);
if(soctomcu_major < 0){
unregister_chrdev(soctomcu_major, SOCTOMCU_DEVICE_NAME);
pr_info("<semisky><soctomcu>soctomcu register_chrdev fail!\n");
ret =soctomcu_major;
}
//pr_info("<semisky><soctomcu>register_chrdev success!\n");
soctomcu_class = class_create(THIS_MODULE, SOCTOMCU_DEVICE_NAME);
if (IS_ERR(soctomcu_class)) {
class_destroy(soctomcu_class);
printk(KERN_ERR "<semisky><soctomcu>Error creating class.\n");
ret = -1;
}
soctomcu_device = device_create(soctomcu_class, NULL, MKDEV(soctomcu_major, 0), NULL, SOCTOMCU_DEVICE_NAME);
if (IS_ERR(soctomcu_device)) {
device_destroy(soctomcu_class, MKDEV(soctomcu_major, 0));
printk(KERN_ERR "<semisky><soctomcu>Error creating class device.\n");
ret = -1;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = SOCTOR78I2C_ADDR;
strlcpy(info.type, "SocToR78_I2c", I2C_NAME_SIZE);
adapter = i2c_get_adapter(0);
if (!adapter)
{
printk(KERN_ERR "Can't get i2c adapter 0\n");
//kfree(pdata);
ret = -ENODEV;
goto exit;
}
client = i2c_new_device(adapter, &info);
//printk("i2c_new_device::client=0x%x\n",*client);
i2c_put_adapter(adapter);
if (!client)
{
printk(KERN_ERR "Can't add i2c device at 0x%x\n", (unsigned int)info.addr);
//kfree(pdata);
ret = -ENOMEM;
goto exit;
}
printk("soctor78 driver installing..\n");
return i2c_add_driver(&soctor78_driver);
exit:
return ret;
}
static void __exit soctor78_exit(void)
{
printk("%s\n",__FUNCTION__);
i2c_del_driver(&soctor78_driver);
i2c_unregister_device(i2c_connect_client);
/*************************************************/
/*unregister_chrdev_region(MKDEV(SOCTOMCU_MAJOR,SOCTOMCU_MINOR),10);
cdev_del(soctomcudev);
device_destroy(soctomcu_class, soctomcu_dev);
class_destroy(soctomcu_class);
*/
device_destroy(soctomcu_class, MKDEV(soctomcu_major, 0));
class_destroy(soctomcu_class);
unregister_chrdev(soctomcu_major, SOCTOMCU_DEVICE_NAME);
/**************************************************/
}
module_init(soctor78_init);
module_exit(soctor78_exit);
MODULE_AUTHOR("czwyle");
MODULE_DESCRIPTION("Driver for soctor78 I2C devices");
MODULE_LICENSE("GPL");