设备树里有关节点各种操作,结构体

结构体
struct device_node 保存设备节点的信息
struct of_device_id 结构体
struct property 节点属性的结构体
struct resource 硬件资源结构体 节点里reg属性获取后得到 resource结构体

根据上面的指引到这里找到详细描述

找到设备节点

struct device_node 保存设备节点的信息
struct device_node {const char *name;.//节点中属性为name的值const char *type;//节点中属性为device_type的值phandle phandle;const char *full_name;//节点的名字,在device_node结构体后面放一个字符串,full_name指向它struct fwnode_handle fwnode;struct  property *properties;//链表,连接该节点的所有属性struct  property *deadprops;    /* removed properties */struct  device_node *parent;//指向父节点struct  device_node *child;// 指向子节点struct  device_node *sibling;// 指向兄弟节点
#if defined(CONFIG_OF_KOBJ)struct  kobject kobj;
#endifunsigned long _flags;void    *data;
#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;
#endif
};
of_device_id结构体

暂时见到过,用这个结构体装着要寻找的设备节点的信息,然后去寻找想要的设备节点
of_find_matching_node_and_match这个函数里面用到过这个结构体

struct of_device_id {char    name[32];//节点中属性为name的值char    type[32];//节点中属性为device_type的值char    compatible[128];// 节点的名字,在device_node结构体后面放一个字符串,full_name指向它const void *data;//链表,连接该节点的所有属性
};

设备树获取节点函数

根据节点路径寻找节点函数

of_find_node_by_path函数 (内核源码/include/linux/of.h)

struct device_node *of_find_node_by_path(const char *path)@description : 寻找节点
@param - const char *path : 指定节点在设备树中的路径。
@return : struct *device_node  结构体指针
@return : NULL失败
根据节点名字寻找节点函数

of_find_node_by_name函数 (内核源码/include/linux/of.h)

struct device_node *of_find_node_by_name(struct device_node *from,const char *name);@description : 名字寻找节点
@param : struct device_node *from : 从指定节点开始查找,NULL从根节点开始查找
@param : const char *name : 要找的节点名字
@return : struct *device_node 结构体指针
@return : NULL 查找失败
根据节点类型寻找节点函数

of_find_node_by_type函数 (内核源码/include/linux/of.h)

struct device_node *of_find_node_by_type(struct device_node *from,const char *type)@param : struct device_node *from : 从指定节点后面开始查找
@param : const char *type : 节点类型,也就是deivce_node->type
@return : struct *device_node
@return : NULL没找到
.根据节点类型和compatible属性寻找节点函数

of_find_compatible_node函数 (内核源码/include/linux/of.h)

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)@param - struct device_node *from : 从某节点开始查找
@param - const char *type : 查找的类型,也是device_node->type
@param - const char *compatible : compatabile属性,device_node->compatible
根据匹配表寻找节点函数

of_find_matching_node_and_match函数 (内核源码/include/linux/of.h)

static inline struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)@description : 结构体包含了更多的匹配参数,也就是说相比前三个寻找节点函数,这个函数匹配的参数更多,对节点的筛选更细。
@param - struct device_node *from : 从哪个节点开始
@param - const struct of_device_id *matches : 和匹配表匹配的节点
@param - const struct of_device_id **match : 一个用来匹配设备节点专用的结构体
@return : struct *device_node : 匹配到的device_node节点
寻找父节点函数

of_get_parent函数 (内核源码/include/linux/of.h)

struct device_node *of_get_parent(const struct device_node *node)@param - const struct device_node *node : 指定谁(节点)要找的父节点
@return : struct * device_noed :device_node节点的指针
@return : NULL没找到
寻找子节点函数

of_get_next_child函数 (内核源码/include/linux/of.h)

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)@param - const struct device_node *node : 指定谁(节点)要找的子节点
@param : struct device_node *prev :前一个子节点。寻找的是prev节点之后的节点。这是一个迭代寻找过程,例如寻找第二个子节点,这里就要填第一个子节点。参数为NULL 表示寻找第一个子节点
@return : struct * device_noed :device_node节点的指针
@return : NULL没找到

从节点里找到想要的属性

刚刚我们从设备树里找到想要的节点,现在就可以从里面拿出想要属性

直接获得属性和值

节点属性的结构体 struct property
struct property {char    *name; //属性名int     length;//属性长度void    *value;//属性值struct property *next; //下一个属性
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)struct bin_attribute attr;
#endif
};
节点里reg属性获取后得到 resource结构体
struct resource {resource_size_t start;//起始地址resource_size_t end;//结束地址const char *name;//属性名字unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;
};

从device_node节点里获取属性的函数

提取节点属性函数

of_find_property函数 (内核源码/include/linux/of.h)

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)@description : 从节点里面提取想要的属性
@param - const struct device_node *np : 指定的设备树节点
@param - const char *name : 属性名的大小
@param - int *lenp : 属性值的大小,作为输出参数
@return :struct *property : 获取到想要的属性值
@return :NULL
读取整型属性函数

相对于上面,数值直接出来,不用再从property结构体里取属性,读取一组
of_property_read_uX_array函数组 (内核源码/include/linux/of.h)
//8位整数读取函数
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)
//16位整数读取函数
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)
//32位整数读取函数
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)
//64位整数读取函数
int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

-------------------------->简化,直接把长度设置为1
//8位整数读取函数
int of_property_read_u8 (const struct device_node *np, const char *propname,u8 *out_values)
//16位整数读取函数
int of_property_read_u16 (const struct device_node *np, const char *propname,u16 *out_values)
//32位整数读取函数
int of_property_read_u32 (const struct device_node *np, const char *propname,u32 *out_values)
//64位整数读取函数
int of_property_read_u64 (const struct device_node *np, const char *propname,u64 *out_values)

int of_property_read_u16 (const struct device_node \*np, const char \*propname,u16 \*out_values)@param - const struct device_node *np : 指定的节点
@param - char *propname :要获取的属性值
@param - u32 *out_values :输出参数,保存获得的返回值
@param - size_t sz 设置想读取的参数长度
@return : 0 : 成功读取
@return : -EINVAL : 属性不存在
@return : -ENODATA :没有要读取的数据
@return : -EOVERFLOW :属性列表太小
读取字符串属性函数

设备节点中存在很多字符串属性,例如compatible、status、type等等,这些属性可以使用查找节点属性函数of_find_property来获取,但是这样比较繁琐。那就用下面这个吧
of_property_read_string函数 (内核源码/include/linux/of.h)

int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string)@param - const struct device_node :设备节点
@param - char *propname : 属性名
@param - char **out_string : 输出函数,获得字符串的指针
@return - 0 :sucess
@return - -EINVAL : 属性不存在
@return - -ENODATA :没有要读取的数据
@return - -EOVERFLOW :属性列表太小

获得属性property后,把地址映射

这时候要用到内存映射相关of函数—>解决之前得到物理地址还要ioremap的转换

内存映射相关of函数

of_iomap函数 (内核源码/drivers/of/address.c)

void __iomem *of_iomap(struct device_node *np, int index)@param - struct device_node *np :要获取的设备节点里的属性的结构体
@param - int index : 通常index有多段,index指定要映射的那一段,标号从0开始
@return : 成功返回转换得到的虚拟地址
@return : 失败返回NULL
常规获取地址of函数

那就是设备树里面设置的实际的物理地址(没有经过映射)
of_address_to_resource函数 (内核源码/drivers/of/address.c)

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)@param - struct device_node *dev : 要获取的设备节点里的属性的结构体
@param - int index : 通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
@param - struct resource *r : 输出函数-->resource结构体,返回地址得到的信息
@return - 0 :sucess
@return - -EINVAL
@return - -ENODATA 

//21年/1月30日更

通过pinctl子系统里GPIO子系统控制引脚

除了上面直接获取属性,通过映射进行操作,还可以通过pinctl子系统里已经有的gpio子系统控制gpio

获取GPIO编号函数of_get_named_gpio

of_get_named_gpio函数(内核源码include/linux/of_gpio.h)

static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)@param - struct device_node *np : 指定设备节点
@param - const char *propname : GPIO属性名,在设备树和对应属性名对应
@param - int index :  引脚索引值,在设备树中一条引脚属性可以包含多个引脚,该参数用于指定获取那个引脚。
@return - GPIO编号 : sucess
@return - 负数 : failed
GPIO申请函数gpio_request

gpio_request函数(内核源码drivers/gpio/gpiolib-legacy.c)

static inline int gpio_request(unsigned gpio, const char *label);@param - unsigned gpio : 要申请的GPIO编号,上一个函数of_get_named_gpio返回的值
@param - const char *label : 引脚的名字,相当于为申请得到的引脚取了个别名。
@return - 0 : sucess
@return - 负数 : failed
GPIO释放函数

gpio_free函数(内核源码drivers/gpio/gpiolib-legacy.c)

static inline void gpio_free(unsigned gpio);@param - unsigned gpio : 要释放的gpio
@return - NULL
GPIO输出设置函数gpio_direction_output

gpio_direction_output函数(内核源码include/asm-generic/gpio.h)

static inline int gpio_direction_output(unsigned gpio , int value);@param - unsigned gpio : 要设置的GPIO的编号
@param - int value :  输出值,1,表示高电平。0表示低电平(设置的虚拟值与物理值对应,物理值在设备树上)
@return - 0 : suecess
@return - 负数 : failed
GPIO输入设置函数gpio_direction_input

gpio_direction_input函数(内核源码include/asm-generic/gpio.h)

static inline int gpio_direction_input(unsigned gpio)@param - unsigned gpio : 要设置的gpio
@return 0 : sucess
@return 负数 : failed
获取GPIO引脚值函数gpio_get_value

gpio_get_value函数(内核源码include/asm-generic/gpio.h)

static inline int gpio_get_value(unsigned gpio);@param - unsigned gpio : 要获取的gpio编号
@return - gpio状态 : success
@return - 负数 : failed
设置GPIO输出值gpio_set_value

gpio_direction_output函数(内核源码include/asm-generic/gpio.h)
static inline int gpio_direction_output(unsigned gpio, int value);

I2C总线操作


i2c总线包括i2c设备(i2c_client)和i2c驱动(i2c_driver),当我们向linux中注册设备或驱动的时候,按照i2c总线匹配规则进行配对,配对成功,则可以通过i2c_driver中.prob函数创建具体的设备驱动。
设备驱动创建成功,我们还需要实现设备的文件操作接口(file_operations),file_operations中会使用到内核中i2c核心函数(i2c系统已经实现的函数,专门开放给驱动工程师使用)。 使用这些函数会涉及到i2c适配器,也就是i2c控制器。由于ic2控制器有不同的配置,所有linux将每一个i2c控制器抽象成i2c适配器对象。 这个对象中存在一个很重要的成员变量——Algorithm,Algorithm中存在一系列函数指针,这些函数指针指向真正硬件操作代码。
在现代linux中,i2c设备不再需要手动创建,而是使用设备树机制引入,设备树节点是与paltform总线相配合使用的。 所以需先对i2c总线包装一层paltform总线,当设备树节点转换为平台总线设备时,我们在进一步将其转换为i2c设备,注册到i2c总线中。

i2c驱动编写的各种结构体

struct i2c_adapter i2c_适配器

i2c_适配器对应一个i2c控制器,是用于标识物理i2c总线以及访问它所需的访问算法的结构。
sturct i2c_adapter (内核源码/include/linux/i2c.h)

/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/
struct i2c_adapter {struct module *owner;unsigned int class;               /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus */void *algo_data;            //struct i2c_algorithm 结构体,访问总线的算法/* data fields that are valid for all devices   */struct rt_mutex bus_lock;int timeout;                    /* in jiffies */int retries;struct device dev;              // the adapter device, struct device 结构体,控制器,表明这是一个设备。int nr;char name[48];struct completion dev_released;struct mutex userspace_clients_lock;struct list_head userspace_clients;struct i2c_bus_recovery_info *bus_recovery_info;const struct i2c_adapter_quirks *quirks;
};
struct i2c_algorithm i2c算法结构体

直白的说, i2c设备例如mpu6050、i2c接口的oled屏等等就是通过这些函数接口使用i2c总线实现收、发数据的
struct i2c_algorithm (内核源码/include/linux/i2c.h 部分函数)

    /* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error *///master_xfer: 作为主设备时的发送函数,应该返回成功处理的消息数,或者在出错时返回负值。int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);//smbus_xfer: 作为从设备时的发送函数。int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);#if IS_ENABLED(CONFIG_I2C_SLAVE)int (*reg_slave)(struct i2c_client *client);int (*unreg_slave)(struct i2c_client *client);
#endif
};

该结构体两个函数定义
.master_xfer = master_xfer,
static int master_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
编写设备驱动时我们会使用“i2c_transfer”函数执行数据的传输,i2c_transfer函数最终就是调用master_xfer函数实现具体的收发工作。

struct i2c_client i2c从设备结构体

struct i2c_client (内核源码/include/linux/i2c.h)

  struct i2c_client {//flags: :I2C_CLIENT_TEN表示设备使用10位芯片地址,I2C客户端PEC表示它使用SMBus数据包错误检查unsigned short flags;           /* div., see below              *///addr: addr在连接到父适配器的I2C总线上使用的地址unsigned short addr;            /* chip address - NOTE: 7bit    *///name: 表示设备的类型,通常是芯片名char name[I2C_NAME_SIZE];//adapter: struct i2c_adapter 结构体,管理托管这个I2C设备的总线段。struct i2c_adapter *adapter;    /* the adapter we sit on        *///dev: Driver model设备节点。struct device dev;              /* the device structure         *///init_irq: 作为从设备时的发送函数。int init_irq;                   /* irq set at initialization    *///irq: 表示该设备生成的中断号。int irq;                        /* irq issued by device         *///detected: struct list_head i2c的成员_驱动程序.客户端列表或i2c核心的用户空间设备列表struct list_head detected;#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb;        /* callback for slave mode      */#endif};
struct i2c_msg结构体

描述一个iic消息(内核源码/include/uapi/linux/i2c.h)

struct i2c_msg {__u16 addr;     /* slave address                        */__u16 flags;//消息传输方向和特性。I2C_M_RD:表示读取消息;0:表示发送消息。...__u16 len;//消息数据的长度 /* msg length      */__u8 *buf;//符数组存放消息,作为消息的缓冲区 /* pointer to msg data                  */};
struct i2c_driver i2c设备驱动结构体

struct i2c_driver (内核源码/include/linux/i2c.h)

struct i2c_driver {unsigned int class;int (*probe)(struct i2c_client *, const struct i2c_device_id *);// i2c设备和i2c驱动匹配后,回调该函数指针。int (*remove)(struct i2c_client *);struct device_driver driver;const struct i2c_device_id *id_table;//struct i2c_device_id 要匹配的从设备信息。int (*detect)(struct i2c_client *, struct i2c_board_info *);//设备探测函数const unsigned short *address_list;//设备地址struct list_head clients;//设备链表...};
struct imx_i2c_struct (保存硬件信息)
struct imx_i2c_struct {struct i2c_adapter      adapter;struct clk              *clk;//时钟频率结构体void __iomem            *base;wait_queue_head_t       queue;unsigned long           i2csr;unsigned int            disable_delay;int                     stopped;unsigned int            ifdr; /* IMX_I2C_IFDR */unsigned int            cur_clk;unsigned int            bitrate;//波特率信息结构体const struct imx_i2c_hwdata     *hwdata;struct imx_i2c_dma      *dma;//dam相关结构体
};

I2C设备驱动核心函数

i2c_add_adapter() 向linux系统注册一个i2c适配器

注册一个i2c适配器 (内核源码/drivers/i2c/i2c-core-base.c)

//linux系统自动设置i2c适配器编号(adapter->nr)
int i2c_add_adapter(struct i2c_adapter *adapter)
//手动设置i2c适配器编号(adapter->nr)
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)@param - struct i2c_adapter *adapter : 物理控制器对应的适配器
@return - 0 : sucess
@return - 负数 : failed
i2c_add_driver()宏 注册i2c驱动

注册一个i2c驱动(内核源码/include/linux/i2c.h)

   #define i2c_add_driver(driver)宏函数的本质是调用了i2c_register_driver()函数

如下

i2c_register_driver() 注册I2C驱动函数

注册一个i2c驱动(内核源码/drivers/i2c/i2c-core-base.c)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)@param - struct module *owner :  一般为 THIS_MODULE
@param - i2c_driver *driver : 要注册的 i2c_driver.
@return - 0 : sucess
@return - 负数 :失败
i2c_transfer() 数据传输函数

i2c_transfer()函数最终就是调用我们前面讲到的i2c_imx_xfer()函数来实现数据传输。
收发i2c消息(内核源码/drivers/i2c/i2c-core-base.c)

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)@param - struct i2c_adapter *adap : 收发消息所使用的i2c适配器,i2c_client 会保存其对应的 i2c_adapter
@param - struct i2c_msg *msgs :i2c要发送的一个或多个消息
@param - int num : 消息数量,也就是msgs的数量
@return - msgs的数量 : sucess
@return - 负数 : failed
i2c_master_send()

发送一个i2c消息(内核源码/include/linux/i2c.h)

 static inline int i2c_master_send(const struct i2c_client *client,const char *buf, int count){return i2c_transfer_buffer_flags(client, (char *)buf, count, 0);};
i2c_master_recv()

接收一个i2c消息(内核源码/include/linux/i2c.h)

static inline int i2c_master_recv(const struct i2c_client *client,char *buf, int count){return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);};
i2c_transfer_buffer_flags()

发送一个i2c消息(内核源码/drivers/i2c/i2c-core-base.c)

 int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,int count, u16 flags){int ret;struct i2c_msg msg = {.addr = client->addr,.flags = flags | (client->flags & I2C_M_TEN),.len = count,.buf = buf,};ret = i2c_transfer(client->adapter, &msg, 1);/** If everything went ok (i.e. 1 msg transferred), return #bytes* transferred, else error code.*/return (ret == 1) ? count : ret;}

spi总线操作


左边的总线驱动厂商已经写好了,我们只要写右边的设备驱动

spi_驱动里有关的结构体

struct spi_driver spi设备驱动结构体

struct spi_driver (内核源码/include/linux/spi/spi.h)

struct spi_driver {const struct spi_device_id *id_table;  //用来和spi进行配对int                     (*probe)(struct spi_device *spi); //spi设备和spi驱动匹配成功后,回调该函数指针int                     (*remove)(struct spi_device *spi);void                    (*shutdown)(struct spi_device *spi);struct device_driver    driver;
};
struct spi_device spi设备结构体

struct spi_device (内核源码/include/linux/spi/spi.h)

struct spi_device {struct device     dev; //device类型结构体,spi设备结构体、i2c设备结构体、平台设备结构体都是“继承”自设备结构体struct spi_controller   *controller;//当前spi设备挂载在那个spi控制器struct spi_controller   *master;//在总线驱动中,一个spi_master代表了一个spi总线,用于指定spi设备挂载到那个spi总线上u32           max_speed_hz;//SPI通信的最大频率u8         chip_select;//spi总选用于区分不同SPI设备的一个标号u8           bits_per_word;/SPI通信时一个字节多少位,也就是传输单位u16          mode;//SPI工作模式,工作模式如以上代码中的宏定义
#define SPI_CPHA    0x01            /* clock phase */
#define SPI_CPOL    0x02            /* clock polarity */
#define SPI_MODE_0  (0|0)           /* (original MicroWire) */
#define SPI_MODE_1  (0|SPI_CPHA)
#define SPI_MODE_2  (SPI_CPOL|0)
#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04            /* chipselect active high? */
#define SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */
#define SPI_3WIRE   0x10            /* SI/SO signals shared */
#define SPI_LOOP    0x20            /* loopback mode */
#define SPI_NO_CS   0x40            /* 1 dev/bus, no chipselect */
#define SPI_READY   0x80            /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100           /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200           /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400           /* receive with 2 wires */
#define SPI_RX_QUAD 0x800           /* receive with 4 wires */int           irq;//如果使用了中断,它用于指定中断号void           *controller_state;void          *controller_data;char           modalias[SPI_NAME_SIZE];int         cs_gpio;//片选引脚,驱动和设别树节点匹配成功后自动获取片选引脚,也可以自定义片选引脚struct spi_statistics    statistics;//记录spi名字,用来和spi_driver进行配对};
struct spi_transfer spi传输结构体

在spi设备驱动程序中,spi_transfer结构体用于指定要发送的数据

struct spi_transfer {/* it's ok if tx_buf == rx_buf (right?)* for MicroWire, one buffer must be null* buffers must work with dma_*map_single() calls, unless*   spi_message.is_dma_mapped reports a pre-existing mapping*/const void      *tx_buf;//发送缓冲区void            *rx_buf;//接受缓冲区unsigned        len;//接收或发送的长度,由于spi特性,发送和接收长度相等dma_addr_t      tx_dma;//如果用了DMA,指定tx DMA地址dma_addr_t      rx_dma;//如果用了DMA,指定rx DMA地址struct sg_table tx_sg;//struct sg_table rx_sg;unsigned        cs_change:1;unsigned        tx_nbits:3;unsigned        rx_nbits:3;
#define     SPI_NBITS_SINGLE        0x01 /* 1bit transfer */
#define     SPI_NBITS_DUAL          0x02 /* 2bits transfer */
#define     SPI_NBITS_QUAD          0x04 /* 4bits transfer */u8              bits_per_word;//指定字节多少位,频率,带有默认值u16             delay_usecs;u32             speed_hz;struct list_head transfer_list;
};
struct spi_message spi消息结构体

spi_transfer结构体保存了要发送(或接收)的数据,而在SPI设备驱动中数据是以“消息”的形式发送。 spi_message是消息结构体,我们把它称为消息结构体,发送一个消息分四步, 依次为定义消息结构体、初始化消息结构体、“绑定”要发送的数据(也就是初始化好的spi_transfer结构)、执行发送。

    struct list_head        transfers;struct spi_device       *spi;unsigned                is_dma_mapped:1;/* REVISIT:  we might want a flag affecting the behavior of the* last transfer ... allowing things like "read 16 bit length L"* immediately followed by "read L bytes".  Basically imposing* a specific message scheduling algorithm.** Some controller drivers (message-at-a-time queue processing)* could provide that as their default scheduling algorithm.  But* others (with multi-message pipelines) could need a flag to* tell them about such special cases.*//* completion is reported through a callback */void                    (*complete)(void *context);void                    *context;unsigned                frame_length;unsigned                actual_length;int                     status;/* for optional use by whatever driver currently owns the* spi_message ...  between calls to spi_async and then later* complete(), that's the spi_master controller driver.*/struct list_head        queue;void                    *state;
};

spi_message结构体成员我们比较陌生,如果我们不考虑具体的发送细节我们可以不用了解这些成员的含义,因为spi_message的初始化以及“绑定”spi_transfer传输结构体都是由内核函数实现。 唯一要说明的是第二个成员“spi”,它是一个spi_device类型的指针,我们讲解spi_device结构体时说过,一个spi设备对应一个spi_device结构体,这个成员就是用于指定消息来自哪个设备。

各种spi的总线函数(了解,我们自己不怎么用)

spi_init(void) spi总线注册函数

linux系统在开机的时候就会执行,自动进行spi总线注册。
spi总线注册 (内核源码/drivers/spi/spi.c)

static int __init spi_init(void)
{int     status;...status = bus_register(&spi_bus_type);...status = class_register(&spi_master_class);...
}
spi_match_device() spi设备匹配函数

函数提供了四种匹配方式,设备树匹配方式和acpi匹配方式以及id_table匹配方式,如果前面三种都没有匹配成功,则通过设备名进行配对。
spi总线注册 (内核源码/drivers/spi/spi.c)

static int spi_match_device(struct device *dev, struct device_driver *drv)
{const struct spi_device *spi = to_spi_device(dev);const struct spi_driver *sdrv = to_spi_driver(drv);/* Attempt an OF style match */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI */if (acpi_driver_match_device(dev, drv))return 1;if (sdrv->id_table)return !!spi_match_id(sdrv->id_table, spi);return strcmp(spi->modalias, drv->name) == 0;
}
spi_imx_probe() 强大的初数化函数

spi_imx_probe()函数主要有如下功能: 获取设备树节点信息,初始化spi时钟、dma、中断等, 保存spi寄存器起始地址,填充spi控制器回调函数。
spi_imx_probe函数 (内核源码/drivers/spi/spi-imx.c)

static int spi_imx_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;const struct of_device_id *of_id =of_match_device(spi_imx_dt_ids, &pdev->dev);struct spi_imx_master *mxc_platform_info =dev_get_platdata(&pdev->dev);struct spi_master *master;struct spi_imx_data *spi_imx;struct resource *res;const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data :(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;bool slave_mode;...slave_mode = devtype_data->has_slavemode &&of_property_read_bool(np, "spi-slave");if (slave_mode)master = spi_alloc_slave(&pdev->dev,sizeof(struct spi_imx_data));elsemaster = spi_alloc_master(&pdev->dev,sizeof(struct spi_imx_data));if (!master)return -ENOMEM;...ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);if (ret < 0) {if (mxc_platform_info) {num_cs = mxc_platform_info->num_chipselect;master->num_chipselect = num_cs;}} else {master->num_chipselect = num_cs;}spi_imx = spi_master_get_devdata(master);spi_imx->bitbang.master = master;spi_imx->dev = &pdev->dev;spi_imx->slave_mode = slave_mode;spi_imx->devtype_data = devtype_data;master->cs_gpios = devm_kzalloc(&master->dev,sizeof(int) * master->num_chipselect, GFP_KERNEL);spi_imx->bitbang.chipselect = spi_imx_chipselect;spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;spi_imx->bitbang.txrx_bufs = spi_imx_transfer;spi_imx->bitbang.master->setup = spi_imx_setup;spi_imx->bitbang.master->cleanup = spi_imx_cleanup;spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;spi_imx->bitbang.master->slave_abort = spi_imx_slave_abort;spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \...init_completion(&spi_imx->xfer_done);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);spi_imx->base = devm_ioremap_resource(&pdev->dev, res);...ret = spi_bitbang_start(&spi_imx->bitbang);...
}

第2-13行: 声明一些必要的变量,这些结构体的关系如上所示。

第16行: of_property_read_bool()去设备树节点读取指定的值保存在slave_mode中,spi-slave的值指定spi是工作在主模式还是从模式下面。

第17-24行: 根据slave_mode的值为master分配内存。

第26-34行:读取spi片选数量,保存在num_cs中,然后将值赋给master。使用时,就可以通过spi_controller获取片选信号。

第37行:将spi_bitbang结构体指向spi控制器,spi_bitbang的作用就是让我们使用普通GPIO来模拟spi时序。由于我们不使用普通GPIO模拟spi,这部分就不深入讲解

第38-41行: 对spi_imx其他成员进行初始化。

第43行: 对master中的cs_gpios动态申请内存。用来存放spi控制器所使用的引脚。

第46-54行: 为spi_bitbang设置了一系列的回调函数,这里重要的有spi_imx_setup、spi_imx_cleanup、spi_imx_prepare_message、spi_imx_unprepare_message,后面会一一介绍。

第58、59行: 获取设备树spi控制器的基地址,并进行虚拟地址映射,存放现在spi_imx结构体变量中的base成员中。

第61行: 接下来调用spi_bitbang_start函数传入bitbang指针

spi设备驱动(在驱动里使用)

spi_register_driver() spi设备注册函数

相同的还有注销函数

int spi_register_driver(struct spi_driver *sdrv)
static inline void spi_unregister_driver(struct spi_driver *sdrv)
@param - struct spi_driver *sdrv : spi类型的结构体
@return - 0 :sucess
@return - 其他值 : failed
spi_setup() spi驱动设置函数

函数设置spi设备的片选信号、传输单位、最大传输速率等,函数中调用spi控制器的成员controller->setup(), 也就是spi_imx->bitbang.master->setup(),在函数spi_imx_probe()中我们将spi_imx_setup赋予该结构体。
spi_setup函数(内核源码/drivers/spi/spi.c)

int spi_setup(struct spi_device *spi)
@param - struct spi_device *spi : spi结构体
@return - 0 :sucess
@return - 其他值 : failed
spi_message_init() 消息队列初始化函数

spi_message_init函数(内核源码/include/linux/spi/spi.h)

static inline void spi_message_init(struct spi_message *m)
{memset(m, 0, sizeof *m);spi_message_init_no_memset(m);
}@param - m :  spi_message 结构体指针,spi_message结构体定义和介绍可在前面关键数据结构中找到。
@return : NULL
spi_message_add_tail() 添加消息队列函数

spi_message_init函数(内核源码/include/linux/spi/spi.h)
这个函数很简单就是将将spi_transfer结构体添加到spi_message队列的末尾。

static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{list_add_tail(&t->transfer_list, &m->transfers);
}

中断系统

request_irq() 中断注册函数
static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler,unsigned long flags, const char *name, void *dev)@param - unsigned int irq : 指定“内核中断号”,这个参数我们会从设备树中获取或转换得到。在内核空间中它代表一个唯一的中断编号
@param - irq_handler_t handler : 用于指定中断处理函数,中断发生后跳转到该函数去执行
@param - unsigned long flags : 中断触发条件,写在下面
@param - const char *name : 中断的名字,中断申请成功后会在“/proc/interrupts”目录下看到对应的文件
@param - void *dev : 如果使用了**IRQF_SHARED** 宏,则开启了共享中断
@return - 0 :sucess
@return - 负数 : failed
#define IRQF_TRIGGER_NONE       0x00000000
#define IRQF_TRIGGER_RISING     0x00000001
#define IRQF_TRIGGER_FALLING        0x00000002
#define IRQF_TRIGGER_HIGH       0x00000004
#define IRQF_TRIGGER_LOW        0x00000008
#define IRQF_TRIGGER_MASK       (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE      0x00000010#define IRQF_SHARED         0x00000080 ---------①
free_irq()中断注销函数
void free_irq(unsigned int irq, void *dev);
@param - unsigned int irq : 从设备树中得到或者转换得到的中断编号
@param - void *dev : 与request_irq函数中dev传入的参数一致
@return - NULL
irqreturn_t() 中断处理函数

在中断申请时需要指定一个中断处理函数

@param - unsigned int irq : 用于指定"内核中断号"
@param - void * dev : 在共享中断中,用来判断中断产生的驱动是哪个
@return - irqreturn_t : enum irqreturn {IRQ_NONE                = (0 << 0),IRQ_HANDLED             = (1 << 0),IRQ_WAKE_THREAD         = (1 << 1),
};typedef enum irqreturn irqreturn_t;

设备树常用奇奇怪怪速查相关推荐

  1. pandas常用函数说明及速查表

    pandas常用函数说明及速查表 如果你用python做开发,那么几乎肯定会使用pandas库. Pandas 是 Python 语言的一个扩展程序库,用于数据分析. Pandas 是一个开放源码.B ...

  2. 《zw版·Halcon-delphi系列原创教程》 zw版-Halcon常用函数Top100中文速查手册

    <zw版·Halcon-delphi系列原创教程> zw版-Halcon常用函数Top100中文速查手册 Halcon函数库非常庞大,v11版有1900多个算子(函数). 这个Top版,对 ...

  3. C语言奇奇怪怪表达式‘abcd‘,及操作符详解

    前言 回顾操作符和一些表达式方面的知识. 表达式及操作符 前言 算术操作符 : + - * / 位操作符 >>.<< >> 算数右移 逻辑右移 << 小 ...

  4. Hadoop常用的操作指令速查和讲解

    一.常用操作指令速查 假设Hadoop的安装目录HADOOP_HOME为/home/admin/hadoop.0.启动与关闭 启动Hadoop 进入HADOOP_HOME目录. 执行sh bin/st ...

  5. 古有陈天华万字血书抗沙俄,今有本剧蒻万字背包虐dp(01,完全,多重,分组,混合等各种背包详解 + 板子题+ 奇奇怪怪滴变式题)

    前言: 本文介绍了01背包.完全背包.多重背包.混合背包.分组背包等背包,并对其进行透彻的剖析,并附上了板子题,供您白嫖,以及一些奇葩变式,颇有意思,供你琢磨玩弄.此外绝大部分题都有二维数组和滚动数组 ...

  6. 【奇奇怪怪小知识】系统盘(C盘)持续发出“救救我”信号(C盘仅剩不足10G,爆红), 操作过后直接腾出近20G

    一.系统磁盘状况 新买的电脑不到两年,平常安装都放去其他磁盘.结果今天一看,我去?C盘变红彤彤的? C盘仅剩不到10G,爆红!c盘似乎在跟我说,救救我!救救我! 无奈之下,我开始与之拉扯,看看都有啥? ...

  7. 奇奇怪怪技术贴之软件账户注销

    窃以为好的软件在注销账户上也要做到完善.近日对几个常用软件的账户注销功能做了测试. 1.百度 登录账号即可注销,5分好评. 流程:登录手机百度App--我的--设置--账号管理--账号急救--账号注销 ...

  8. [算法][算法复杂度]常用算法复杂度速查表

    复杂度通常会使用大 -O记号来表示,比如快速排序的平均时间复杂度是 O(nlog(n)).虽然我们应该做「理解派」,但是即使每个算法/数据结构都理解了,不时仍有可能忘记具体某个算法/数据结构的复杂度( ...

  9. 常用游戏类型简写速查

    ACT......(ACTION GAME )动作游戏 STG......(SHOTING GAME )射击游戏 RPG......(ROLE PLAYING GAME )角色扮演游戏 A.RPG.. ...

最新文章

  1. C/C++中constkeyword
  2. 如何实现java虚拟机的优化_Java虚拟机JVM优化实战的过程全记录
  3. android:persistent (非系统app失效)
  4. linux加密框架 crypto 算法管理 - 动态和静态算法管理
  5. 下半年登场!小米MIX 4概念图曝光:有望首发屏下摄像头
  6. 如何经由PHP获得MySQL procedure结果
  7. mtk android 编译环境,MTK android 快速编译方法.docx
  8. 手把手教你接入支付宝支付
  9. matlab发送mavlink消息
  10. 【k8s】Unable to restart cluster, will reset it: apiserver healthz异常
  11. 小熊派移植华为 LiteOS-M(基于MDK)
  12. Qt中使用QAxObject的dynamicCall和querySubObject函数操作SolidWorks的方法
  13. 计算机语言热门有,百度传课发布2月热门榜单,计算机和语言类大热
  14. 使用深度学习的图像分割(综述)
  15. paddle 学习总结与使用指南 笔记(一)
  16. 利用InstallAnywhere制作JAVA可执行程序安装文件
  17. SendMessage参数大全
  18. VSCode不能跳转到定义的解决方法
  19. JDBC连接ORACLE的三种URL格式
  20. 常见的数据库错误的解决方法!必看!(含安装插件常见错误)

热门文章

  1. matlab url什么意思,URL是什么 url是什么意思?
  2. 猫耳FM音频转换成MP3格式
  3. java 处理大文件
  4. 有关戴尔服务器信息的公众号,戴尔DELL
  5. hexo butterfly主题 添加全局吸底APlayer
  6. 数商云采购管理系统:阳光采购,高效降本
  7. python视频教程唐学韬-计算机基础经典书籍推荐——Python语言
  8. Android 实现京东秒杀功能详解
  9. Win10彻底删除OneDrive的方法
  10. Android display