1. 字符设备注册与注销
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
// 返回值:
// major 值为 0(自动分配) ,正常的话返回申请到的主设备号,失败返回-EBUSY。
// 指定 major 值后,正常返回0,不正常返回-EBUSY。
static inline void unregister_chrdev(unsigned int major, const char *name)
register_chrdev 函数用于注册字符设备,此函数一共有三个参数,这三个参数的含义如下:
-
major:主设备号,Linux 下每个设备都有一个设备号,设备号分为主设备号和次设备号两部分,关于设备号后面会详细讲解。
-
name:设备名字,指向一串字符串。
-
fops:结构体 file_operations 类型指针,指向设备的操作函数集合变量。
unregister_chrdev 函数用户注销字符设备,此函数有两个参数,这两个参数含义如下:
-
major:要注销的设备对应的主设备号。
-
name:要注销的设备对应的设备名。
为了方便管理,Linux 中每个设备都有一个设备号,设备号由主设备号和次设备号两部分组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备( 或这样理解:主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3 )。
device_create()/class_create()
我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
struct class_device *class_device_create(struct class *cls,struct class_device *parent,dev_t devt,struct device *device,const char *fmt, ...)
/* 在probe函数中 */
major = register_chrdev(0, "100ask_led", &led_drv);
/* led_class这个类存放在sysfs下面 */
led_class = class_create(THIS_MODULE,"100ask_led_class");
/* /dev/100ask_led0 */
/* udev根据led_class去sysfs中找到对应的类,并创建设备(major, 0) */
device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0);
of函数-获取设备树中的信息
参考:include/linux/of.h以及of_xxx.h,如of_gpio.h中包含获取设备树中gpio的api的接口,还有of_irq.h等。
extern struct device_node *of_find_node_by_phandle(phandle handle);
extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node);
extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev);
extern struct device_node *of_get_next_available_child(
const struct device_node *node, struct device_node *prev);
extern struct device_node *of_get_child_by_name(const struct device_node *node, const char *name);
extern int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz);
extern int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz);
extern int of_property_read_string(
struct device_node *np,
const char *propname,
const char **out_string);
extern int of_property_match_string(
struct device_node *np,
const char *propname,
const char *string);
extern int of_add_property(struct device_node *np, struct property *prop);
extern int of_remove_property(struct device_node *np, struct property *prop);
extern int of_update_property(struct device_node *np, struct property *newprop);
获取设备树中gpio的个数及编号api
参考:linux/of_gpio.h
/**
* of_gpio_named_count() - Count GPIOs for a device
* @np: device node to count GPIOs for
* @propname: property name containing gpio specifier(s)
*
* The function returns the count of GPIOs specified for a node.
* Note that the empty GPIO specifiers count too. Returns either
* Number of gpios defined in property,
* -EINVAL for an incorrectly formed gpios property, or
* -ENOENT for a missing gpios property
*
* Example:
* gpios = <0
* &gpio1 1 2
* 0
* &gpio2 3 4>;
*
* The above example defines four GPIOs, two of which are not specified.
* This function will return '4'
*/
static inline int of_gpio_named_count(struct device_node *np, const char* propname)
{
return of_count_phandle_with_args(np, propname, "#gpio-cells");
}
/**
* of_gpio_count() - Count GPIOs for a device
* @np: device node to count GPIOs for
*
* Same as of_gpio_named_count, but hard coded to use the 'gpios' property
*/
static inline int of_gpio_count(struct device_node *np)
{
//其实就是调用上面那个函数,只不过name固定为“gpios”
return of_gpio_named_count(np, "gpios");
}
static inline int of_get_gpio_flags(struct device_node *np, int index, enum of_gpio_flags *flags)
{
return of_get_named_gpio_flags(np, "gpios", index, flags);
}
/**
* of_get_named_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
* @propname: Name of property containing gpio specifier(s)
* @index: index of the GPIO
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)
{
return of_get_named_gpio_flags(np, propname, index, NULL);
}
/**
* of_get_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
* @index: index of the GPIO
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
static inline int of_get_gpio(struct device_node *np, int index)
{
return of_get_gpio_flags(np, index, NULL);
}
获取设备树中gpio的描述符
参考:linux/of_gpio.h
下面都是特殊的函数,我们使用时使用对应的函数即可,比如对于__gpiod_get
我们使用gpiod_get
即可,参数和返回值是一样的。
/* Return the number of GPIOs associated with a device / function */
int gpiod_count(struct device *dev, const char *con_id);
/* Acquire and dispose GPIOs */
struct gpio_desc *__must_check __gpiod_get(
struct device *dev,
const char *con_id,
enum gpiod_flags flags);
struct gpio_desc *__must_check __gpiod_get_index(
struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags);
struct gpio_desc *__must_check __gpiod_get_optional(
struct device *dev,
const char *con_id,
enum gpiod_flags flags);
struct gpio_desc *__must_check __gpiod_get_index_optional(
struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags);
struct gpio_descs *__must_check gpiod_get_array(
struct device *dev,
const char *con_id,
enum gpiod_flags flags);
struct gpio_descs *__must_check gpiod_get_array_optional(
struct device *dev,
const char *con_id,
enum gpiod_flags flags);
void gpiod_put(struct gpio_desc *desc);
void gpiod_put_array(struct gpio_descs *descs);
gpio子系统API
基于gpio号的 gpio相关函数参考Linux内核源码:include/linux/gpio.h
基于描述符的gpiod相关函数参考内核源码:include/linux/gpio/consumer.h
相关结构体:
/* 基于gpio编号的 */
/**
* struct gpio - a structure describing a GPIO with configuration
* @gpio: the GPIO number
* @flags: GPIO configuration as specified by GPIOF_*
* @label: a literal description string of this GPIO
*/
struct gpio {
unsigned gpio;
unsigned long flags;
const char *label;
};
/* 基于gpio描述符的 */
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs { /* 注意有个s,代表描述序列 */
unsigned int ndescs;
struct gpio_desc *desc[];
};
基于gpio的API:
bool gpio_is_valid(int number);
int gpio_request(unsigned gpio, const char *label);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free(unsigned gpio);
void gpio_free_array(const struct gpio *array, size_t num);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_set_debounce(unsigned gpio, unsigned debounce);
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
int gpio_cansleep(unsigned gpio);
int gpio_get_value_cansleep(unsigned gpio);
int irq_to_gpio(unsigned irq);
int devm_gpio_request(struct device *dev, unsigned gpio,
const char *label);
......
基于gpiod的API:
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
int gpiod_get_direction(struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array);
int gpiod_get_raw_value(const struct gpio_desc *desc);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
void gpiod_set_raw_array(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array);
/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_raw_array_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array);
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_is_active_low(const struct gpio_desc *desc);
int gpiod_cansleep(const struct gpio_desc *desc);
int gpiod_to_irq(const struct gpio_desc *desc);
/* 老gpio和新gpio之间进行转换的解口 */
struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc);
2. gpiod_get()
使用以下两个函数获取GPIO设备,多个设备时需要附带index参数。函数返回一个GPIO描述符,或一个错误编码,可以使用IS_ERR()进行检查:
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
gpiod_get_index()
gpiod_put()
request_irq
int request_irq(
unsigned int irq,
irq_handler_t handler,
unsigned long irqflags,
const char *devname,
void *dev_id);
err = request_irq(
gpio_keys_100ask[i].irq, //硬中断号,向内核申请
gpio_key_isr, //相应的中断服务程序
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, //属性
"100ask_gpio_key", //中断名称,一般和驱动名称一样
&gpio_keys_100ask[i]); //会传给中断服务程序
-
irq是要申请的硬件中断号。
-
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
-
irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
-
devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
-
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
of_get_next_child
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
函数参数和返回值含义如下:
- node:父节点。
- prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
- **返回值:**找到的下一个子节点。
实例
设备树文件:
/ {
DTS_demo {
compatible = "DTS_demo, BiscuitOS";
status = "okay";
child0 {
compatible = "Child0, BiscuitOS";
};
child1 {
compatbile = "Child1, BiscuitOS";
};
};
};
驱动文件中probe函数:
/* probe platform driver */
static int DTS_demo_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *parent;
struct device_node *nparent;
struct device_node *child;
struct device_node *nchild;
printk("DTS probe entence...\n");
/* Find child */
child = of_get_next_child(np, NULL);
if (child)
printk("\"%s\"'s child: \"%s\"\n", np->full_name, child->full_name);
else
printk("The device node doesn't have child\n");
/* Find next child */
nchild = of_get_next_available_child(np, child);
if (nchild)
printk("\"%s\"'s next child: \"%s\"\n", np->full_name, nchild->full_name);
/* find parent */
parent = of_get_parent(child);
if (parent)
printk("\"%s\"'s parent: \"%s\"\n", child->full_name, parent->full_name);
/* Lterate to a node's parent */
nparent = of_get_next_parent(child);
if (nparent)
printk("\"%s\"'s next parent: \"%s\"\n", child->full_name, parent->full_name);
return 0;
}
结果:
[ 3.581300] DTS probe entence...
[ 3.581316] "/DTS_demo"'s child: "/DTS_demo/child0"
[ 3.581332] "/DTS_demo"'s next child: "/DTS_demo/child1"
[ 3.581347] "/DTS_demo/child0"'s parent: "/DTS_demo"
[ 3.581362] "/DTS_demo/child0"'s next parent: "/DTS_demo"
解释:full-name对于node来说,就是自己的结点名;而对于其子节点来说,full-name则是父节点名字/自己的名字,如上面的结果``/DTS_demo"'s child: "/DTS_demo/child0`。