Linux设备驱动入门-I2C设备驱动

Linux设备驱动入门—-I2C设备驱动

/*
* I2C驱动的一些模板:
* (1)、I2C总线驱动的的模块加载和卸载函数模板
* (2)、I2C总线通信方法
* (3)、I2C设备驱动模块的加载和卸载
* (4)、I2C设备驱动的文件操作接口
* (与普通驱动的文件操作一致,只是要使用
* i2c_client,i2c_driver,i2c_adapter,i2c_algorithm
* 结构体和I2C核心,需要协同合作)
*/

/*
* (1) : 初始化I2C适配器所使用的硬件资源,
* 如申请I/O地址,中断号等。通过i2c_add_adapter()
* 添加i2c_adapter的数据结构,当然这个i2c_adapter
* 数据结构的成员已经被XXX设配器的相应函数指针所初始化
* 说明:xxx_adapter_hw_init();xxx_adapter_hw_free();
* 函数的实现都与具体的CPU和I2C设备硬件直接相关。
*/

static int __init i2c_adapter_xxx_init(void)
{
xxx_adapter_hw_init();
i2c_add_adapter(&xxx_adapter);
}

static void __exit i2c_adapter_xxx_exit(void)
{
xxx_adapter_hw_free();
i2c_del_adapter(&xxx_adapter);
}

/*
* (2): 主要实现i2c_algorithm 的master_xfer()
* 函数和 functionality()函数。
* functionality()函数用于返回algorithm所支持的通信协议
* master_xfer()函数在I2C适配器上完成传递给它的
* i2c_msg数组中的每个I2C消息。
*
* 说明:模板中的i2c_adapter_xxx_start(),setaddr(),
* wait_ack(),readbytes(),writebytes(),stop()函数用于
* 适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接
* 相关,需要由工程师根据芯片的数据手册来实现。
*
*/
static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
...
for(i = 0; i < num; i++)
{
i2c_adapter_xxx_start(); /* 产生开始位 */
/* 是读消息 */
if (msgs[i]->flags & I2C_M_RD)
{
/* 发送从设备读地址 */
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);
i2c_adapter_xxx_wait_ack(); /* 获取从设备的ack */

/* 读取msgs[i]->len长的数据到msgs[i]->buf */
i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len);
}
else
{
/* 写消息 */
/* 发送从设备写地址 */
i2c_adapter_xxx_setaddr(msg->addr << 1);
i2c_adapter_xxx_wait_ack; //获取从设备的ack

/* 读取msgs[i]->len长的数据到msgs[i]->buf */
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
}
}
i2c_adapter_xxx_stop(); /* 产生停止位 */
}

/*
* (3): 通过register_chrdev()函数将I2C设备注册为一个字符设备;
* 通过I2C核心的i2c_add_driver()函数添加i2c_driver;
*
*/
static int __init yyy_init(void)
{
int res;
/* 注册字符设备 */
res = register_chrdev(YYY_MAJOR, "YYY", &yyy_fops);//老内核接口
if (res)
goto out;
/* 添加i2c_driver */
res = i2c_add_driver(&yyy_driver);
if(res)
{
goto out_unreg_class;
}
return 0;

out_unreg_class:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);

return res;
}

static void __exit yyy_exit(void)
{
i2c_del_driver(&i2cdev_driver);
unregiister_chrdev(YYY_MAJOR, "YYY");
}

/*
* i2c_add_driver(&yyy_driver)的执行会引发i2c_driver
* 结构体中yyy_attach_adapter()函数的执行,可以在该函
* 数立探测物理设备,只需要调用I2C核心的i2c_probe()
*
*/
static int yyy_attach_adapter(struct i2c_adapter * adapter)
{
/*
* param1:i2c_adapter指针
* param2:要探测的地址数据
* param3:具体的探测函数
*/
return i2c_probe(adapter, &addr_data, yyy_detect);
}
/*
* i2c_probe()函数会引发yyy_detect()函数的调用,
* 可以在yyy_detect()函数中初始化i2c_client
*/
static int yyy_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client * new_client;
struct yyy_data *data;
int err = 0;

if(!i2c_check_functionality(adapter, I2C_FUNC_XXX))
goto exit;

if (!(data = kzalloc(sizeof(struct yyy_data), GFP_KERBEL)))
{
err = - ENOMEM;
goto exit;
}

new_client = &data->client;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &yyy_driver;
new_client->flags = 0;

/* 新的client将依附于adapter */
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;

yyy_init_client(new_client);//此函数是与硬件相关的

return 0;

exit_kfree:
kfree(data);
exit:
return err;
}

/* i2c_detach_client函数 */
static int yyy_detach_client(struct i2c_client *client)
{
int err;
struct yyy_data *data = i2c_get_clientdata(client);

if((err = i2c_detach_client(client)))
return err;

kfree(data);
return 0;
}

/*
* yyy_command()的实现,它实现了针对设备的控制命令;
* 具体的控制命令是设备相关的,对于实时钟而言,命令
* 将是设置时间和获取时间, 而对于视频AD设备而言,
* 命令会是设置采样方式,选择通道等。
*/
static int yyy_command(struct i2c_client *client, unsigned int cmd, void * arg

Linux设备驱动入门-I2C设备驱动

相关文章:

你感兴趣的文章:

标签云: