【1】复习
platform总线
设备端
设备信息结构体:
struct resource res[] = {
[0] = {
.start = 0xc0053000,
.end = 0xc0053000 + 24 -1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 73,
.end = 73,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device pdev = {
.name = "hello",
.id = -1,
.dev = {
.release = pdev_release,
},
.number_resource = 2,
.resource = res,
};
platform_device_register(&pdev);
platform_device_unregister(&pdev);
驱动端
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "hello",
},
.id_table = pdrv_idtable,
};
platform_driver_register(&pdrv);
platform_driver_unregister(&pdrv);
【2】i2c的驱动框架
user:
open read write close
kernel
设备驱动层:当总线驱动和设备驱动匹配成功之后执行这里的probe函数
1.注册字符设备驱动,给应用层提供访问的接口
2.封装数据,发送数据(i2c_read_reg,i2c_write_reg)
3.完成硬件(mma8451q)的初始化
-------------------------------------------------------------
核心层(i2c-core.c ):它是由linux内核工程师实现的,提供总线驱
动和设备驱动的注册和注销的方式,以及匹配的过程。
-------------------------------------------------------------
总线驱动层(控制器驱动层):在linux内核中厂商已经将控制器驱动
drivers/i2c/busses|编写好了,对控制器的初始化,数据的发送和接收。
i2c-s3c241.c |
------------------|-----------------------------------------------
hardware : i2c控制器 mma8451q
------------ scl ------------
| |-------------------| x |
| | sda | y |
| |-------------------| z |
| | | |
------------ ------------
// s3c2410 s3c6410 s5pv210 exynos4412 s5p6818
【3】i2c设备驱动的实现
1.分配的对象
struct i2c_driver {
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
};
struct i2c_driver mma8451q;
2.初始化对象
mma8451q.probe = mma8451q_probe //匹配成功后执行的函数
mma8451q.remove = mma8451q_remove//分离执行的函数
mma8451q.driver.name = "名字" //这个名字不能用于匹配,但是还必须初始化。
// /sys/bus/i2c/drivers/2-001c
mma8451q.id_table = mma8451q_idtable //用于匹配
3.注册
i2c_add_driver(&mma8451q);
4.注销
i2c_del_driver(&mma8451q);
一键注册、注销
module_i2c_driver(mma8451q);
【4】i2c控制器驱动名字的提交
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //提交的名字
unsigned short addr; //从机地址
};
int i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
功能:向控制器提交名字
参数:
@busnum :总线号(它和控制器的序号一一对应) 2
@info :被提交的信息
@len :提交的i2c_board_info的个数
返回值:成功返回0,失败返回错误码
通用存放设备信息的文件:
arch/arm/plat-s5p6818/fs6818$ vi device.c
716 /*-----------------------------------------
717 * G-Sensor platform device
718 */
719 #include <linux/i2c.h>
720
721 static struct i2c_board_info mma8451q = {
722 .type = "mma8451q",
723 .addr = 0x1c,
724 };
1905 #elif defined(CONFIG_SENSORS_MMA8451) || defined(CONFIG_MXC_MMA8451_MODULE)
1906 printk("plat: add g-sensor mma8451\n");
-1907 // i2c_register_board_info(2, &mma8451_i2c_bdi, 1);
1908 #endif
+1909 i2c_register_board_info(2, &mma8451q, 1);
make uImage 编译内核
cp arch/arm/boot/uImage ~/tftpboot/ 拷贝
重启开发板
【5】i2c数据的封装和发送的过程
//https://www.cnblogs.com/aspirs/p/12371237.html
//7位 10位从机地址的访问问题
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address /
#define I2C_M_RD 0x0001 / read data, from slave to master */
struct i2c_msg {
__u16 addr; //从机地址
__u16 flags; //读写的标志位 0写 I2C_M_RD读
__u16 len; //本次你要写的数据的长度
__u8 *buf; //消息的首地址
};
*/有多少个起始位就有多少个消息,消息的长度是以字节来表示的 /*
写的消息的封装:
char w_buf[] = {reg,data};
struct i2c_msg w_msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = w_buf,
};
读的消息的封装:
char data;
char r_buf []= {reg};
struct i2c_msg r_msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = r_buf,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = &data,
},
};
消息发送:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
功能:消息的发送函数
参数:
@adap :总线驱动的对象 client->adapter
@msgs :消息结构体指针
@num :消息的个数
返回值:成功返回发送了的消息的格式,
否者不是发送的消息的个数。
【6】i2c_client结构体
当设备驱动i2c_driver和总线驱动i2c_adapter匹配成功之后
在内核中就会创建一个i2c_client的结构体,这个结构体就是
用来保存匹配成功后的信息的。
struct i2c_client {
unsigned short addr; //从机地址
char name[I2C_NAME_SIZE];//名字
struct i2c_adapter *adapter;//匹配成功的总线驱动的对象
struct i2c_driver *driver; //匹配成功的驱动端的对象
};
【1】块设备
比如一个移动硬盘:
磁头:有多少个盘面
磁道:一个面内有多少环
扇区:一个环内有多少个扇区,一个扇区512字节
磁盘的数据的读取:
磁盘数据的读取不会按照存储顺序来读取,因为磁头它是机械结构,
通过旋转来访问数据,如果按照数据来访问,需要反复的切换这个
物理结构,比较浪费时间。所以磁盘在访问的时候采用电梯优化的算法
来完成。即一次将一个盘面上的数据全部读取到,然后切换物理结构,
在读取下面的数据。将所有的数据读取完之后,进行数据的排序,排序
之后进行操作。