在学习和分析Linux驱动的时候,感受最大的问题是不理解内核提供的很多接口的功能和说明。在本篇中整理总结在开发中遇到的接口,对于具体功能依靠Linux/Documentation目录下说明、源码注释、以及自己开发中的理解总结。对这些接口的整理,也是梳理重新认识Linux框架和代码的过程,个人受益匪浅。另外本篇中肯定因为个人认知的局限,有纰漏的地方,因此会长期进行更新。
本项目的所有驱动相关的接口来自于Linux版本: Linux 6.1.5, 其它版本对应的接口可能不一致,如果有改动,不在特殊说明,可自行根据实际接口判断, 关于驱动接口函数的模板见后续说明。
注意: 一部分接口有devm_和普通版本,其中devm表示释放由内核管理,不需要手动释放,这部分接口都只在文件驱动资源管理接口中说明,不在单独列出。
//函数原型
void __iomem *ioremap(unsigned long phys_addr, unsigned long size);
//参数
phys_addr: 硬件访问的实际物理地址
size: 物理地址的长度
返回: 转换后的虚拟内存地址指针
//例程
usr_led {
compatible = "rmk,usr-led";
reg = <0x020c406c 0x04>,
<0x020e0068 0x04>,
<0x020e02f4 0x04>,
<0x0209c000 0x04>,
<0x0209c004 0x04>;
status = "okay";
};
int ret;
u32 cfg0;
size_t *virual_cfg0;
struct device_node *led_node;
led_node = of_find_node_by_path("/usr_led");
ret = of_property_read_u32(led_node, "reg", &cfg0);
virual_cfg0 = ioremap(cfg0, 4); //将cfg0对应的物理地址转换为虚拟内存地址,内核可以访问
//函数原型
void iounmap(const volatile void __iomem *addr);
//参数
addr: 已经映射的虚拟地址
//例程
iounmap(virual_cfg0);
//函数原型
void * kmalloc(size_t size, gfp_t flags)
//参数
size: 申请数据的长度
gfp: 申请数据的类型
GFP_KERNEL: 进程上下文申请的内存,GFP_KERNEL的标记可以引发直接的内存回收,从而导致进程阻塞睡眠,这在原子上下文显然是不允许的
GFP_ATOMIC: 在中断、软中断、spinlock等原子上下文里面,申请内存,应该使用GFP_ATOMIC标记,申请尽可能不触发内存回收,不会休眠
返回: 成功返回一个指向分配的内存,失败则返回NULL
//例程
pm_info = kmalloc(sizeof(*pm_info), GFP_KERNEL);
//函数原型
static inline void *kzalloc(size_t size, gfp_t gfp)
//参数
size: 申请数据的长度
gfp: 申请数据的类型
GFP_KERNEL: 进程上下文申请的内存,GFP_KERNEL的标记可以引发直接的内存回收,从而导致进程阻塞睡眠,这在原子上下文显然是不允许的
GFP_ATOMIC: 在中断、软中断、spinlock等原子上下文里面,申请内存,应该使用GFP_ATOMIC标记,申请尽可能不触发内存回收,不会休眠
返回: 成功返回一个指向分配的内存,失败则返回NULL
//例程
struct spi_transfer *t;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 */
if(!t) {
return -ENOMEM;
}
//函数原型
void kfree(void *p);
//参数
p:申请的指针
//例程
kfree(t)
//函数原型
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
//参数
ptr:判断是否出错的对象指针
返回:0表示正常,其它表示返回错误
//例程
imx->clk_32k = devm_clk_get_optional(&pdev->dev, "32k");
if (IS_ERR(imx->clk_32k)) {
//do somethings
}
//函数原型
#define MAJOR(dev) ((dev)>>8)
#define MINOR(dev) ((dev) & 0xff)
#define MKDEV(ma, mi) ((ma)<<8 | (mi))
//函数原型
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
//参数
ptr: 指向结构体中变量的指针
type:结构体数据类型
member: 变量在结构体中对应的对象
返回: 变量所在结构体的起始指针
//例程说明
struct ap3216_data
{
struct cdev cdev;
//...
};
struct ap3216_data *chip;
chip = container_of(inode->i_cdev, struct ap3216_data, cdev);
filp->private_data = chip;
//函数原型
static inline void writel(u32 value, volatile void __iomem *addr)
//参数
value: 向虚拟内存地址写入的值
addr: 指向虚拟内存memory地址的指针
//例程
info->regs = devm_platform_ioremap_resource(pdev, 0);
writel(0x01, info->regs);
//函数原型
static inline u32 readl(const volatile void __iomem *addr)
//参数
addr: 指向虚拟内存memory地址的指针
返回: 从虚拟内存地址中读取的值
//例程
info->regs = devm_platform_ioremap_resource(pdev, 0);
u32 regval = readl(info->regs);
// 函数原型
static inline void kref_init(struct kref *kref)
// 参数
kref: 是指向要初始化的kref结构体的指针
// 例程说明
struct kref kref_val;
kref_init(&kref_val);
// 函数原型
static inline void kref_get(struct kref *kref)
// 参数
kref: 是指向要增加的的kref结构体的指针
// 例程说明
kref_get(&kref_val);
// 函数原型
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
// 参数
kref: 是指向要释放的的kref结构体的指针
release: 调用释放对象的回调函数
返回: 1表示对象已经被释放,其它则返回0
// 例程说明
kref_put(&mapping->kref, release_iommu_mapping);
// 函数原型
static inline unsigned int kref_read(const struct kref *kref)
// 参数
kref: 是指向要获取的的kref结构体的指针
返回: 对象的引用计数的值
// 例程说明
int ref = kref_read(&kref_val);
// 函数原型
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv, struct module *owner);
// 参数
drv: 是指向要注册的platform_driver结构体的指针
owner:驱动模块的拥有者,一般为THIS_MODULE(驱动在编译时会生成)
返回: 0表示执行成功,其它值表示执行出错
// 例程说明
/*
用于匹配platform总线下的节点,可通过ls /sys/bus/platform/devices/查看支持的接口
匹配属性为compatible,如usr_beep的属性查看
cat /sys/bus/platform/devices/usr_beep/of_node/compatible
*/
static const struct of_device_id of_match_beep[] = {
{ .compatible = "rmk,usr-beep"},
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match_beep);
static struct platform_driver platform_driver = {
.driver = {
.name = "usr_beep",
.of_match_table = of_match_beep,
},
.probe = beep_probe,
.remove = beep_remove,
};
platform_driver_register(&platform_driver);
/*
注意:
有设备树情况下,在总线加载时会解析设备树并创建指定总线下的设备(如/sys/bus/i2c/devices/下)
无设备树情况,需要自己手动创建设备, 此时使用id_table匹配。
*/
//函数原型
void platform_driver_unregister(struct platform_driver *drv)
//参数
drv: 指向要注销的platform_driver结构体的指针
//例程
platform_driver_unregister(&platform_driver);
//函数原型
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, \
platform_driver_unregister)
//参数
__platform_driver: 管理platform_driver结构体的对象
//例程
static struct platform_driver platform_driver = {
.driver = {
.name = "usr_beep",
.of_match_table = of_match_beep,
},
.probe = beep_probe,
.remove = beep_remove,
};
module_platform_driver(platform_driver);
//函数原型
#define module_platform_driver_probe(__platform_driver, __platform_probe) \
static int __init __platform_driver##_init(void) \
{ \
return platform_driver_probe(&(__platform_driver), \
__platform_probe); \
} \
module_init(__platform_driver##_init); \
static void __exit __platform_driver##_exit(void) \
{ \
platform_driver_unregister(&(__platform_driver)); \
} \
module_exit(__platform_driver##_exit);
//参数
__platform_driver: 管理platform_driver结构体的对象
__platform_probe:probe加载的函数
//例程
module_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe);
//函数原型
int platform_device_register(struct platform_device *pdev)
//参数
pdev: 指向要注册的platform_device结构体的指针
返回: 0表示执行成功,其它值表示执行出错
//例程
static struct w1_gpio_platform_data foo_w1_gpio_pdata = {
.ext_pullup_enable_pin = -EINVAL,
};
static struct platform_device foo_w1_device = {
.name = "w1-gpio", //设备和驱动匹配时,对应的驱动名称
.id = -1,
.dev.platform_data = &foo_w1_gpio_pdata,
};
platform_device_register(&foo_w1_device);
/*
注意:
有设备树时,内核启动时解析设备树自动创建
不使用设备树时需要自己手动创建总线,驱动使用id_table匹配name选项。
目前主流是设备树方案,此接口很少使用。
*/
//函数原型
void platform_device_unregister(struct platform_device *)
//参数
drv:指向需要移除的platform_device结构体的指针
//例程
platform_device_unregister(&foo_w1_device);
//函数原型
int platform_add_devices(struct platform_device **devs, int num);
//参数
devs: 指向要注册的platform_device结构体的指针数组
nmu:注册的设备个数
返回: 0表示注册成功,其它值表示注册失败(任意一个失败都会直接返回)
//例程
static struct platform_device *devices[] __initdata = {
&kb3886bl_device,
};
platform_add_devices(devices, ARRAY_SIZE(devices));
/*
注意:由platform_add_devices注册的设备,仍然用platform_device_unregister进行移除
*/
//函数原型+
int platform_get_irq(struct platform_device *dev, unsigned int num)
//参数
dev: 指向一个platform_device结构体的指针,该结构体代表了一个平台设备
num:用于指定要获取的中断的索引,支持多个interrupts时可以通过此区分
返回: 非负值表示返回的中断号,负值表示失败
//例程
//中断线号带两个参数类型,表示中断控制器为gpio1,对应gpio1的9号中断
interrput-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
//中断线号带的参数类型由所属控制器的#interrupt-cells决定
//gpio1的#interrupt-cells = <2>,中断类型省略
//interrupts = <中断类型,中断号偏移值,中断触发类型>
//中断类型
//GIC_IPI: inter processer interrupt, 中断号0~15
//GIC_PPI: peripheral processer interrupt, 中断号16~31
//GIC_SPI:shared processer interrupt, 中断号32~32+224
//GPI_SGI: software generated interruot
//中断触发类型
//IRQ_TYPE_NONE 0
//IRQ_TYPE_EDGE_RISING 1
//IRQ_TYPE_EDGE_FALLING 2
//IRQ_TYPE_EDGE_BOTH 3
//IRQ_TYPE_LEVEL_HIGH 4
//IRQ_TYPE_LEVEL_LOW 5
interrupts = <GIC_SPI 23 1> //对应中断线号32+23=55, 上升沿触发
int irq = platform_get_irq(pdev, 0); //取interrupts位置0对应的中断编号
//函数原型
int platform_irq_count(struct platform_device *dev);
//参数
dev: 指向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
返回: 非负值表示查询到的interrupts属性中的中断个数,负值表示获取失败
//例程
int count = platform_irq_count(pdev);
//函数原型
int platform_get_irq_byname(struct platform_device *dev, const char *name)
//参数
dev: 指向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
name: 设备结构中的属性
返回: 成功返回对应名称的irq值,负值表示失败
//例程
//interrupt-name指定中断的name,通过platform_get_irq_byname访问
//返回<0 9 IRQ_TYPE_LEVEL_HIGH>;,值为9
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
<0 9 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "edma-tx", "edma-err";
fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err")
//函数原型
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
//参数
dev:: 指向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
type: 获取资源的类型,主要如下,
IORESOURCE_IO gpio类型资源
IORESOURCE_MEM reg寄存器类型资源
IORESOURCE_IRQ interrupt中断类型资源
IORESOURCE_DMA DMA类型的资源
index: 获取资源的索引值, 如果对于一个资源包含多个,按照索引值读取
返回: NULL表示获取失败,其它返回resoure对应的指针地址
//例程
//在设备树加载时,会将设备树内的节点中属性转换成对应的资源,后续可以通过此方法读取
{
gpio = <&gpio5 2 GPIO_ACTIVE_LOW>; //IORESOURCE_IO
reg = <0x01c00000 4>, <0x01c00004 4>; //IORESOURCE_MEM
interrupts = <18 IRQ_TYPE_EDGE_FALLING>; //IORESOURCE_IRQ
dmas = <&edma0 3 1
&edma0 2 1> //IORESOURCE_DMA
}
//获取i/o属性
struct resource *io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
devm_request_region(dev, io_rc->start, resource_size(io_rc), KBUILD_MODNAME)
led_data->io_base = io_rc->start;
//获取reg属性(IORESOURCE_MEM)
volatile ssize_t* regs[2];
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(!res)
regs[0] = ioremap(res->start, (res->end-res->start)+1);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if(!res)
regs[1] = ioremap(res->start, (res->end-res->start)+1);
//获取irq属性(IORESOURCE_IRQ)
int irq;
struct resource *res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(!res)
irq = res->start;
//函数原型
struct resource *platform_get_resource_byname(struct platform_device *dev, unsigned int type, const char *name)
//参数
dev: 向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
type: 资源的类型, IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_REG, IORESOURCE_IRQ, IORESOURCE_DMA, IORESOURCE_BUS
name: 资源的名称
返回: NULL表示获取失败,其它返回resoure对应的指针地址
//例程
//interrupt-name指定中断的name,通过platform_get_resource_byname访问
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
<0 9 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "edma-tx", "edma-err";
fsl_edma->errirq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "edma-err")
//函数原型
static inline void platform_set_drvdata(struct platform_device *pdev, void *data)
//参数
dev: 指向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
data: 需要保存的私有数据信息
//例程
struct led_data *chip;
chip = devm_kzalloc(&pdev->dev, sizeof(struct led_data), GFP_KERNEL);
if(!chip){
dev_err(&pdev->dev, "malloc error\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, chip);
//函数原型
static inline void *platform_get_drvdata(const struct platform_device *pdev)
//参数
dev: 指向一个platform_device结构体的指针,它包含了设备的各种信息,比如设备名称、资源(如内存区域、中断等)等
返回: 获取保存的私有数据信息
//例程
struct led_data *chip;
chip = platform_get_drvdata(pdev);
申请gpio资源,功能等同于devm_gpio_request, 见章节ch03-x3.
//函数原型
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
//参数
gpio: 需要配置的引脚线号
返回:0表示设置成功,负值表示设置失败
//例程
gpio_request(gpio_det, "MMC detect");
if(ret)
return ret;
gpio_direction_input(gpio_det);
//函数原型
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
//参数
gpio: 需要配置的引脚线号
返回:0表示设置成功,负值表示设置失败
//例程
ret = devm_gpio_request(&pdev->dev, chip->gpio, "led");
if (ret < 0){
dev_err(&pdev->dev, "request gpio failed!\n");
return -EINVAL;
}
gpio_direction_output(chip->gpio, 1);
//函数原型
static inline void gpio_free(unsigned gpio)
//参数
gpio: 已经申请的gpio线号
//例程
gpio_free(chip->gpio);
//函数原型
#define gpio_set_value __gpio_set_value
static inline void __gpio_set_value(unsigned gpio, int value)
{
return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}
//参数
gpio: 需要配置的引脚线号
value: 设置引脚的电平,写入原始值,不考虑
//例程
gpio_set_value(chip->gpio, 1);
//函数原型
#define gpio_set_value __gpio_get_value
static inline int __gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
//参数
gpio: 需要配置的引脚线号
返回: 读取引脚的电平值
//例程
value = gpio_get_value(chip->gpio);
//函数原型
static inline bool gpio_is_valid(int number)
{
/* only non-negative numbers are valid */
return number >= 0;
}
//参数
gpio: 需要配置的引脚线号
返回: 引脚编号是否有效
//例程
if (!gpio_is_valid(template->gpio))
return ERR_PTR(-ENOENT);
//函数原型
struct gpio_desc *gpio_to_desc(unsigned gpio)
//参数:
gpio: gpio的值
返回: 获取I/O的资源
//例程
struct gpio_desc *desc;
desc = gpio_to_desc(chip->gpio);
//函数原型
int desc_to_gpio(const struct gpio_desc *desc)
//参数:
desc: gpio资源描述符
返回: 获取gpio线号
//例程
chip->gpio = desc_to_gpio(desc);
//函数原型
static inline int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
//参数
gpio: 请求的给gpio线号
name: 定义gpio的名称
gpio: gpio的线号
//例程
gpio_export_link(&pdev->dev, "gpiod", chip->key_gpio);
//函数原型
static inline int gpio_export(unsigned gpio, bool direction_may_change)
//参数
gpio: 请求的给gpio线号
direction_may_change: 定义是否允许方向改变
//例程
gpio_export(chip->key_gpio, 0);
//函数原型
static inline void gpio_unexport(unsigned gpio)
//参数
gpio: 已经导出的gpio线号
//例程
gpio_unexport(chip->key_gpio);
//函数原型
int gpiod_direction_input(struct gpio_desc *desc);
//参数
desc: 获取的gpio资源
返回: 0表示设置成功,其它则失败
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "key", GPIOD_IN);
gpiod_direction_input(desc);
//函数原型
int gpiod_direction_output(struct gpio_desc *desc, int value)
//参数
desc: 获取的gpio资源结构
value: gpio的引脚状态
返回: 0表示成功,其它则是错误码
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
gpiod_direction_output(desc);
//函数原型
void gpiod_set_value(struct gpio_desc *desc, int value)
//参数
desc: 获取的gpio资源
value: 设置引脚的状态
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
gpiod_set_value(desc, 1);
//函数原型
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
//参数
desc: 获取的gpio资源
value: 设置引脚的状态
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
gpiod_set_raw_value(desc, 1);
//函数原型
int gpiod_to_irq(const struct gpio_desc *desc);
//参数
desc: 获取的gpio资源
返回: 转换后的irq中断线号
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
int irq = gpiod_to_irq(desc);
//函数原型
int gpiod_get_value(const struct gpio_desc *desc)
//参数
desc: 获取的gpio资源
返回: 获取的I/O状态值
//例程
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
int value = gpiod_get_value(desc);
//函数原型
void gpiod_toggle_active_low(struct gpio_desc *desc)
//参数:
desc: gpio的资源描述符
//例程
struct gpio_desc *desc;
desc = gpio_to_desc(chip->gpio);
gpiod_toggle_active_low(desc);
//函数原型
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
//参数
owner:驱动模块的拥有者,固定为THIS_MODULE
driver: 驱动加载时,总线需要访问的驱动结构体
返回: 0表示执行成功,其它值表示执行出错
//例程
//设备树
ap3216@1e {
compatible = "rmk,ap3216";
reg = <0x1e>;
};
/*
用于匹配i2c总线下的节点,可通过ls /sys/bus/i2c/devices/查看支持的接口
总线匹配成功设备树后创建资源,这里每个设备节点对应i2c_adpater接口点。
匹配属性为compatible,另外支持设备的名称为1-0038的格式,其中1为i2c编号,38为i2c设备地址
*/
static const struct of_device_id ap3216_of_match[] = {
{ .compatible = "rmk,ap3216" }, //匹配的为i2c总线下设备节点的compatible属性
{ /* Sentinel */ }
};
static struct i2c_driver ap3216_driver = {
.probe = ap3216_probe,
.remove = ap3216_remove,
.driver = {
.owner = THIS_MODULE,
.name = "ap3216",
.of_match_table = ap3216_of_match,
},
};
int ret = i2c_add_driver(&ap3216_driver);
//函数原型
void i2c_del_driver(struct i2c_driver *driver);
//参数
driver: 驱动移除时,总线需要访问的驱动结构体
返回: 0表示执行成功,其它值表示执行出错
//例程
int ret = i2c_del_driver(&ap3216_driver);
//函数原型
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
//参数
adap: i2c适配器,i2c作为总线可以挂载多个i2c设备,这里每个设备创建一个适配器结构并指定编号,当指定adap时就可以获取对应的外部器件资源。
msgs: i2c传输的数据指针数组
num: 传递消息的个数
返回: 成功返回传递消息的个数,其它则为失败
//例程
struct i2c_msg msgs[2];
__be16 wbuf = cpu_to_be16(reg);
int ret;
//发送地址数据
msgs[0].flags = 0;
msgs[0].addr = client->addr; //i2c地址
msgs[0].len = 2;
msgs[0].buf = (u8 *)&wbuf;
//读取数据信息
msgs[1].flags = I2C_M_RD;
msgs[1].addr = client->addr;
msgs[1].len = len;
msgs[1].buf = buf;
ret = i2c_transfer(client->adapter, msgs, 2);
struct i2c_adapter {
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
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 irq_domain *host_notify_domain;
struct regulator *bus_regulator;
};
struct i2c_client {
unsigned short flags; /* div., see below */
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
unsigned short addr; /* chip address - NOTE: 7/10bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
void *devres_group_id; /* ID of probe devres group */
};
不使用设备树情况,需要自己在i2c总线下添加设备节点时涉及得接口,当使用设备树,这部分由内核加载设备树时解析自动生成,用户不需要自己开发。考虑到文档复杂度问题,这里只列出功能,不详细说明。
//在i2c总线下添加设备节点
int i2c_add_adapter(struct i2c_adapter *adap);
//在i2c总线下添加内核管理的设备节点
int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter);
//删除i2c总线中的设备节点
void i2c_del_adapter(struct i2c_adapter *adap)
//根据编号获得i2c适配器资源
struct i2c_adapter *i2c_get_adapter(int nr);
//释放获得得i2c适配器资源
void i2c_put_adapter(struct i2c_adapter *adap)
//函数原型
void i2c_set_clientdata(struct i2c_client *client, void *data);
//参数
client: i2c设备管理结构
data:设置的私有数据
//例程
i2c_set_clientdata(client, chip);
//函数原型
void *i2c_get_clientdata(const struct i2c_client *client);
//参数
client: i2c设备管理结构
返回: 驱动保存的私有数据
//例程
chip = i2c_get_clientdata(client);
//函数原型
int spi_setup(struct spi_device *spi)
//参数
spi: spi设备管理结构
返回: spi是否配置成功
//例程
struct spi_device *spi;
spi->mode = SPI_MODE_0;
spi_setup(spi);
//函数原型
#define spi_register_driver(driver) \
__spi_register_driver(THIS_MODULE, driver)
int __spi_register_driver(struct module *owner, struct spi_driver *sdrv);
//参数
owner:驱动模块的拥有者,固定为THIS_MODULE
sdrv: 驱动加载时,总线需要访问的驱动结构体
返回: 0表示执行成功,其它值表示执行出错
//例程
//设备树
spidev0:icm20608@0 {
compatible = "rmk,icm20608";
spi-max-frequency = <8000000>;
reg = <0>;
};
/*
用于匹配spi总线下的节点,可通过ls /sys/bus/spi/devices/查看支持的接口
总线匹配成功设备树后创建资源,这里每个设备节点对应spi_device。
匹配属性为compatible
*/
static const struct of_device_id icm20608_of_match[] = {
{ .compatible = "rmk,icm20608" },
{ /* Sentinel */ }
};
static struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.owner = THIS_MODULE,
.name = "icm20608",
.of_match_table = icm20608_of_match,
},
};
int ret = spi_register_driver(&icm20608_driver);
//函数原型
void spi_unregister_driver(struct spi_driver *driver);
//参数
driver: 驱动移除时,总线需要访问的驱动结构体
//例程
int ret = spi_register_driver(&icm20608_driver);
//函数原型
static inline void spi_message_init(struct spi_message *m)
//参数
m: 定义初始化的message消息
//例程
struct spi_message m;
spi_message_init(&m);
//函数原型
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
//参数
t: 增加的spi数据实体
m: 已经初始化的message消息
//例程
struct spi_message m;
struct spi_transfer t;
unsigned char txdata[32];
unsigned char rxdata[32];
txdata[0] = reg | 0x80;
t->tx_buf = txdata;
t->rx_buf = rxdata;
t->len = len+1;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
//函数原型
int spi_sync(struct spi_device *spi, struct spi_message *message)
//参数
spi: 选择要进行spi通讯的设备结构
message: 要发送的消息
返回: 0表示通讯成功, 其它表示通讯失败
//例程
struct spi_message m;
struct spi_transfer t;
struct spi_device *spi; //probe时提供的消息
int ret;
"...."
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);
if(!ret)
{
memcpy(rx_buf, &t->rx_buf[1], t->len-1);
}
/*
spi在发送的同时会进行接收,所以接收的实际长度为rx_req+tx_len
例如发送寄存器地址长度1,实际发送和接收长度都需要为rx_req+1,
*/
//函数原型
int spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers, unsigned int num_xfers)
//参数
spi: 选择要进行spi通讯的设备结构
xfers: 要发送的数据数组
num_xfer: 要发送的数据数组数目
返回: 0表示通讯成功, 其它表示通讯失败
//例程
struct spi_transfer t[2];
struct spi_device *spi; //probe时提供的消息
t[0]->tx_buf = txdata;
t[0]->rx_buf = rxdata;
t[0]->len = len+1;
t[1]->tx_buf = txdata;
t[1]->rx_buf = rxdata;
t[1]->len = len+1;
spi_sync_transfer(spi, t, ARRAY_SIZE(t));
//函数原型
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx)
//参数
spi: 选择要进行spi通讯的设备结构
txbuf: 发送数据的首地址
n_tx: 发送数据的长度
rx_buf:接收数据的首地址
n_rx:接收数据的长度
返回: 0表示通讯成功, 其它表示通讯失败
//举例
u8 txbuf[1];
u8 rxbuf[6];
struct spi_device *spi; //probe时提供的消息
int ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 6);
//函数原型
void spi_set_drvdata(struct spi_device *spi, void *data);
//参数
spi: spi设备结构
data:设备需要保存的私有数据
//例程
spi_set_drvdata(spi, chip);
//函数原型
void *spi_get_drvdata(struct spi_device *spi);
//参数
spi: spi设备结构
返回: 设备保存的私有数据
//例程
chip = spi_get_drvdata(spi);
//函数原型
#define regmap_init_spi(dev, config) \
__regmap_lockdep_wrapper(__regmap_init_spi, #config, \
dev, config)
struct regmap *__regmap_init_spi(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
//参数
dev: spi总线设备结构
config: spi管理器件的配置信息
返回: 管理spi总线的regmap资源
//例程
const struct regmap_config icm20608_regmap_config = {
.reg_bits = 8, /* 寄存器bit数 */
.val_bits = 8, /* 读取单个字节的bit数 */
.read_flag_mask = 0x80, /* 读标志掩码,会与寄存器地址或 */
.max_register = 255, /* 允许输入的最大寄存器地址 */
};
struct spi_device *spi;
chip->map = devm_regmap_init_spi(spi, &icm20608_regmap_config);
//函数原型
#define regmap_init_i2c(i2c, config) \
__regmap_lockdep_wrapper(__regmap_init_i2c, #config, \
i2c, config)
struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
//参数
dev: i2c总线设备结构
config: i2c管理器件的配置信息
返回: 管理i2c总线的regmap资源
//例程
const struct regmap_config ap3216_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 255,
};
map = regmap_init_i2c(client, &ap3216_regmap_config);
//函数原型
#define regmap_init_mmio(dev, regs, config) \
regmap_init_mmio_clk(dev, NULL, regs, config)
#define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \
dev, clk_id, regs, config)
struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
//参数
dev: 匹配的设备节点信息
regs: 寄存器地址信息
config:寄存器的配置定义
返回: 管理内存映射I/O(mmio)的regmap资源实例
//例程
struct regmap_config icst_regmap_conf = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
struct regmap *map;
map = regmap_init_mmio(dev, base, &icst_regmap_conf);
//函数原型
#define regmap_init_mmio_clk(dev, clk_id, regs, config) \
__regmap_lockdep_wrapper(__regmap_init_mmio_clk, #config, \
dev, clk_id, regs, config)
//参数
dev: 驱动匹配设备节点的struct device结构体
clk_id: 寄存器使能需要启动的时钟名称,会调用相应的clk_perpare接口进行使能
regs: 寄存器地址信息
config:寄存器的配置定义
返回: 管理内存映射I/O(mmio)的regmap资源实例
//例程
struct device *dev = &pdev->dev;
wdev->regmap = devm_regmap_init_mmio_clk(dev, NULL, base,
&imx2_wdt_regmap_config);
//函数原型
void regmap_exit(struct regmap *map)
//参数
map: 已经申请的regmap资源
//例程
regmap_exit(map);
//函数原型
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
//参数
map: 已经申请的regmap资源
reg: 访问的器件寄存器地址
val: 读取的地址内数据
返回: 0表示写入成功,其它则写入失败
//例程
int ret,val;
ret = regmap_read(chip->map, AP3216C_SYSTEMCONG, &val);
//函数原型
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
//参数
map: 已经申请的regmap资源
reg: 访问的器件寄存器地址
val: 写入的数据
返回: 0表示写入成功,其它则写入失败
//例程
int ret;
ret = regmap_write(chip->map, AP3216C_SYSTEMCONG, 0x04);
//函数原型
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count)
//参数
map: 已经申请的regmap资源
reg: 访问的器件寄存器起始地址
val: 存放读取数据的首地址
val_count: 读取数据的长度
返回: 0表示读取成功,其它则读取失败
//例程
int ret;
ret = regmap_bulk_read(chip->map, ICM20_ACCEL_XOUT_H, readbuf, 14);
//函数原型
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_count)
//参数
map: 已经申请的regmap资源
reg: 访问的器件寄存器地址
val: 写入的数据的首地址
val_count: 写入数据的长度
返回: 0表示写入成功,其它则写入失败
//例程
int ret;
char data;
data = 0x80;
ret = regmap_bulk_write(chip->map, ICM20_PWR_MGMT_1, &data, 1);
//函数原型
static inline int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
return regmap_update_bits_base(map, reg, mask, val, NULL, false, false);
}
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
//参数
map: 已经申请的regmap资源
reg: 访问的器件寄存器地址
mask: 要更改的位掩码
val: 要更改的值
返回: 0表示写入成功,其它表示更改失败
//例程
struct regmap *gpr;
/* set FEC clock from internal PLL clock source */
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sl-iomuxc-gpr");
if (!IS_ERR(gpr)) {
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6SL_GPR1_FEC_CLOCK_MUX2_SEL_MASK, 0);
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6SL_GPR1_FEC_CLOCK_MUX1_SEL_MASK, 0);
} else {
pr_err("failed to find fsl,imx6sl-iomux-gpr regmap\n");
}
//函数原型
static inline int regmap_write_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
return regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
}
//参数
map: 指向regmap对象的指针,该对象表示要访问的寄存器空间
reg: 要访问的寄存器的地址偏移量
mask: 一个位掩码,指定了要更新的位字段。只有与mask相对应的位才会被改变,其他位保持不变
val:要写入到指定位字段的值。这个值应该与mask相对应,即只有mask中设置为1的位才在val中有意义
返回: 操作成功返回0,其它则返回错误码
//例程
regmap_write_bits(wdev->regmap, IMX2_WDT_WICR, IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);
//函数原型
struct regmap *syscon_regmap_lookup_by_phandle(
struct device_node *np,
const char *property);
//参数
np: 指向设备的device_node节点的指针
property: 节点关键字属性
返回: 获取的regmap资源
//例程
priv->syscon = syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
//函数原型
struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
//参数
s: 设备节点的compatible说明
返回: 获取的regmap资源
//例程
//dts
gpr: iomuxc-gpr@20e4000 {
compatible = "fsl,imx6ul-iomuxc-gpr",
"fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e4000 0x4000>;
};
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
//函数原型
int misc_register(struct miscdevice *misc);
//参数
misc:需要注册的杂项设备结构体
返回: 0表示注册成功,失败返回负的错误码
//例程说明
static const struct file_operations lsi_fops = {
.open = mraid_mm_open,
.unlocked_ioctl = mraid_mm_unlocked_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
static struct miscdevice megaraid_mm_dev = {
.minor = MISC_DYNAMIC_MINOR, //指定的子设备号,不能够是已存在的子设备
.name = "megadev0",
.fops = &lsi_fops,
};
int err;
err = misc_register(&megaraid_mm_dev);
//注册时会将megaraid_mm_dev作为地址加入到drvdata中
//filep->private_data为dev数据
//函数原型
void misc_deregister(struct miscdevice *misc);
//参数
misc:需要移除的杂项设备结构体
//例程说明
misc_deregister(&megaraid_mm_dev);
//函数原型
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
//参数
major:申请的字符型设备主设备号(非0值申请对应设备号,0则动态申请),设备号已存在会导致申请失败
name:设备或者驱动的名称
fops:字符型设备的操作接口
返回: 返回申请到的主设备号,负值表示失败
//例程说明
int major;
static const struct file_operations dsp56k_fops = {
.owner = THIS_MODULE,
.read = dsp56k_read,
.write = dsp56k_write,
.unlocked_ioctl = dsp56k_ioctl,
.open = dsp56k_open,
.release = dsp56k_release,
.llseek = noop_llseek,
};
major = register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)
//函数原型
void unregister_chrdev(unsigned int major, const char *name)
//参数
major:已注册的字符型设备号
name:设备或者驱动的名称
返回: 0表示申请成功,负的错误值表示申请失败
//例程说明
unregister_chrdev(DSP56K_MAJOR, "dsp56k");
//函数原型
int register_chrdev_region(dev_t from, unsigned count, const char *name);
//参数
from:申请的设备号起始值(主设备号+子设备号)
count:申请的设备号数目
name:设备或者驱动的名称
返回: 0表示申请成功,负的错误值表示申请失败
//例程说明
dev_t devid = MKDEV(65, 0);
int ret;
ret = register_chrdev_region(devid, 1, "led");
//函数原型
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
//参数
dev: 需要申请的设备号指针地址
baseminor: 允许申请的设备号最小值
count: 申请设备号的数量
name: 设备的名称
返回: 0表示申请成功,负的错误值表示申请失败
//例程说明
dev_t devid;
int ret;
ret = alloc_chrdev_region(&devid, 0,1, "led");
//函数原型
void unregister_chrdev_region(dev_t from, unsigned count)
//参数
from: 已申请的设备号起始值(主设备号)
count: 已申请的设备号数目
//例程说明
unregister_chrdev_region(devid, 1);
//函数原型
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
//参数
cdev: 待初始化的字符设备结构体
fops: 设备支持的访问接口结构体
//例程说明
struct cdev cdev;
static struct file_operations beep_fops = {
.owner = THIS_MODULE,
.open = beep_open,
.read = beep_read,
.write = beep_write,
.unlocked_ioctl = beep_ioctl,
.release = beep_release,
};
cdev_init(&cdev, &beep_fops);
cdev.owner = THIS_MODULE;
//函数原型
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
//参数
p: 需要添加到内核的字符设备结构
dev: 关联设备号
count: 添加内核的设备数目
返回:0表示绑定成功,其它值则为失败
//例程说明
int ret;
ret = cdev_add(&cdev, devid, 1);
//函数原型
void cdev_del(struct cdev *p)
//参数
p: 需要删除的字符设备
//例程说明
cdev_del(&cdev);
//函数原型
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
struct class * __must_check __class_create(struct module *owner, const char *name, struct lock_class_key *key)
//参数
owner: 驱动模块的拥有者
name: 驱动模块名称
返回: 类指针,IS_ERR(class)为0则正常,其它异常可通过PTR_ERR(class)读取异常值
//例程说明
static struct class *aoe_class;
int result;
aoe_class = class_create(THIS_MODULE, "aoe");
if (IS_ERR(aoe_class)){
result = PTR_ERR(aoe_class);
}
//函数原型
void class_destroy(struct class *cls)
//参数
cls: 已经创建成功的设备类
//例程说明
class_destroy(aoe_class);
//函数原型
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
//参数
cls: 已经创建成功的设备类结构
parent: 指向父设备的指针,不存在为NULL
devt: 之前创建设备对应的设备号
drvdata: 添加到设备中以回调的数据,没有则为NULL, 通过 dev_get_drvdata()获取
fmt:设备名称
成功: 返回设备指针,IS_ERR(device)为0则正常,其它异常可通过PTR_ERR(device)读取异常值
//例程说明
ur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, cur, "%s", cur->bsr_name);
if (IS_ERR(cur->bsr_device)) {
printk(KERN_ERR "device_create failed for %s\n", cur->bsr_name);
}
//函数原型
void device_destroy(struct class *class, dev_t devt)
//参数
cls: 已经创建成功的设备类结构
devt: 之前创建设备对应的设备号
//例程说明
device_destroy(bsr_class, cur->bsr_dev);
//函数原型
struct attribute {
char *name; //文件名称
struct module *owner; //文件拥有者,一般是THIS_MODULE
umode_t mode; //文件权限,0666
};
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr, //对应应用文件的读取接口
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr, //对应应用文件的写入接口
const char *buf, size_t count);
};
int device_create_file(struct device *dev, const struct device_attribute *attr)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
attr: 设备文件的配置属性描述符
返回: 0表示创建成功,其它表示创建失败
//例程
//在/sys/devices/platform/led/下创建led文件
chip->led_attr.attr.name = DEVICE_NAME;
chip->led_attr.attr.mode = 0666;
chip->led_attr.show = led_show;
chip->led_attr.store = led_store;
ret = device_create_file(&pdev->dev, &chip->led_attr);
if(ret != 0)
{
dev_info(&pdev->dev, "device create file failed!\n");
goto exit_device_create;
}
//函数原型
void device_remove_file(struct device *dev, const struct device_attribute *attr)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
attr: 设备文件的配置属性描述符
//例程
device_remove_file(&pdev->dev, &chip->led_attr);
//函数原型
static inline void *dev_get_platdata(const struct device *dev)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
返回: 指向设备私有数据的指针
//例程
struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev)
//函数原型
static inline void input_set_drvdata(struct input_dev *dev, void *data)
//参数
dev: input设备结构
data:设备需要保存的私有数据
//例程
input_set_drvdata(input, chip);
//函数原型
static inline void *input_get_drvdata(struct input_dev *dev)
//参数
dev: input设备结构
返回: 设备需要保存的私有数据
//例程
chip = input_get_drvdata(input);
//函数原型
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
//参数
dev: input设备结构。
type: 支持的设备类型。
#define EV_SYN 0x00 //用于事件间的分割标志。事件可能按时间或空间进行分割,就像在多点触摸协议中的例子。
#define EV_KEY 0x01 //用来描述键盘,按键或者类似键盘设备的状态变化。
#define EV_REL 0x02 //用来描述相对坐标轴上数值的变化,例如:鼠标向左方移动了5个单位。
#define EV_ABS 0x03 //用来描述相对坐标轴上数值的变化,例如:描述触摸屏上坐标的值。
#define EV_MSC 0x04 //当不能匹配现有的类型时,使用该类型进行描述。
#define EV_SW 0x05 //用来描述具备两种状态的输入开关。
#define EV_LED 0x11 //用于控制设备上的 LED 灯的开和关。
#define EV_SND 0x12 //用来给设备输出提示声音。
#define EV_REP 0x14 //用于可以自动重复的设备(autorepeating)。
#define EV_FF 0x15 //用来给输入设备发送强制回馈命令
#define EV_PWR 0x16 //特别用于电源开关的输入
#define EV_FF_STATUS 0x17 //用于接收设备的强制反馈状态。
code: 支持的设备Code值(根据Event,会在input子系统进一步说明)。
//例程
input_set_capability(chip->input_dev, EV_KEY, KEY_0);
//函数原型
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
int min, int max, int fuzz, int flat)
//参数
dev: input设备结构。
axis: 指定轴的方向
min:指定轴的最小值
max: 指定轴的最大值
fuzz: 用于过滤噪声的fuzz值
flat: 在此值内的值将被事件流丢弃, 然后当作0报告
//例程
input_set_abs_params(joy->input, axes[i].code,
axes[i].range[0], axes[i].range[1],
axes[i].fuzz, axes[i].flat);
//函数原型
int input_register_device(struct input_dev *dev)
//参数
dev: 待注册的input设备结构
返回: 0表示成功,其它则表示注册失败
//例程
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
error = input_register_device(button_dev);
if (error) {
printk(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;
}
//函数原型
void input_unregister_device(struct input_dev *dev)
//参数
dev: 待移除的input设备
//例程
input_unregister_device(button_dev)
//函数原型
int input_setup_polling(struct input_dev *dev,
void (*poll_fn)(struct input_dev *dev))
//参数
dev: 注册的input设备
poll_fn: 周期性执行的回调函数
//例程
static void adc_joystick_poll(struct input_dev *input) {
}
input_setup_polling(input, adc_joystick_poll);
//函数原型
void input_set_poll_interval(struct input_dev *dev, unsigned int interval)
//参数
dev: 注册的input设备
interval: 周期性执行回调函数间隔
//例程
input_set_poll_interval(input, poll_interval);
input_report相关接口都是对于input_evnet的封装,代码如下。
//向应用层提交按键事件
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
//向应用层提交相对地址
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}
//向应用层提交绝对地址
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}
//向应用层提交开关状态
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}
//同步命令,提交所有数据
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
//函数原型
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
//参数
dev: 提交数据的input设备
type: 事件类型,参考input_set_capability
code: 事件的值,对应input_set_capability的code
value: 事件附带数据,可以额外告知上层应用
//例程
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
input_report_key(input, KEY_0, 1);
//函数原型
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
//参数
size: 申请的数据长度
dev: fb指向管理设备的指针
返回: 申请的管理fb结构的数据指针
//例程
myfb = framebuffer_alloc(sizeof(struct fb_info), &spi->dev);
//函数原型
void framebuffer_release(struct fb_info *info)
//参数
info: 已经申请的fb设备管理结构
//例程
framebuffer_release(myfb)
//函数原型
int
register_framebuffer(struct fb_info *fb_info)
//参数
fb_info: 已经申请的fb设备管理结构
返回: 0表示注册成功,其它则表示失败
//例程
myfb->fobs = &myfb_obs;
gmem_size = 240*240*2; //设置显存大小
gmem_addr = kmalloc(gmem_size, GFP_KERNEL);
if (!gmem_addr) {
return -1;
}
myfb->pseudo_palette = pseudo_palette; //调色板缓冲区
myfb->var = myfb_var; //设置分辨率参数
//...
ret = register_framebuffer(myfb);
//函数原型
void
unregister_framebuffer(struct fb_info *fb_info)
//参数
fb_info: 已经注册fb设备管理结构
//例程
unregister_framebuffer(myfb);
//函数原型
int fb_deferred_io_init(struct fb_info *info)
//参数
fb_info: 已申请的fb设备管理结构
返回: 0表示初始化成功,其它则表示失败
//例程
fb_deferred_io_init(myfb);
//函数原型
void fb_deferred_io_cleanup(struct fb_info *info)
//参数
fb_info: 已经开启刷新机制的fb管理结构
//例程
fb_deferred_io_cleanup(myfb);
//函数原型
void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
//参数
fb_info: 已经注册的fb管理结构
rect: 用于填充的矩形参数
struct fb_fillrect {
__u32 dx; /* screen-relative */
__u32 dy;
__u32 width;
__u32 height;
__u32 color;
__u32 rop;
};
//例程
static struct fb_ops myfb_ops = {
.fb_fillrect = sys_fillrect,
};
//函数原型
void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
//参数
fb_info: 已经注册的fb管理结构
area: 用于复制的区域信息
struct fb_copyarea {
__u32 dx;
__u32 dy;
__u32 width;
__u32 height;
__u32 sx;
__u32 sy;
};
//例程
static struct fb_ops myfb_ops = {
.fb_copyarea = sys_copyarea,
};
//函数原型
void sys_imageblit(struct fb_info *p, const struct fb_image *image)
//参数
fb_info: 已经注册的fb管理结构
image: 图片的具体信息
struct fb_image {
__u32 dx; /* Where to place image */
__u32 dy;
__u32 width; /* Size of image */
__u32 height;
__u32 fg_color; /* Only used when a mono bitmap */
__u32 bg_color;
__u8 depth; /* Depth of the image */
const char *data; /* Pointer to image data */
struct fb_cmap cmap; /* color map info */
};
//例程
static struct fb_ops myfb_ops = {
.fb_imageblit = sys_imageblit,
};
//函数原型
int device_property_read_u8_array(struct device *dev, const char *propname, u8 *val, size_t nval);
int device_property_read_u16_array(struct device *dev, const char *propname, u16 *val, size_t nval);
int device_property_read_u32_array(struct device *dev, const char *propname, u32 *val, size_t nval);
int device_property_read_u64_array(struct device *dev, const char *propname, u64 *val, size_t nval);
int device_property_read_string_array(struct device *dev, const char *propname, const char **val, size_t nval);
int device_property_read_string(struct device *dev, const char *propname, const char **val);
//参数
dev: 匹配的设备
propname: 属性的名称
val: 存储获取数据的数组
nval: 数组的长度
返回:如果val为NULL,成功返回数据的数目,不为NULL,0表示成功,其它表示失败。
//例程
adc1: adc@2198000 {
//...
fsl,adck-max-frequency = <30000000>, <40000000>,
<20000000>;
};
device_property_read_u32_array(dev, "fsl,adck-max-frequency", info->max_adck_rate, 3);
//函数原型
static inline int device_property_read_u8(struct device *dev,
const char *propname, u8 *val)
{
return device_property_read_u8_array(dev, propname, val, 1);
}
int device_property_read_u16(struct device *dev, const char *propname, u16 *val);
int device_property_read_u32(struct device *dev, const char *propname, u32 *val);
int device_property_read_u64(struct device *dev, const char *propname, u64 *val);
//参数
dev: 匹配的设备
propname: 属性的名称
val: 存储获取数据的地址
返回:如果val为NULL,成功返回数据的数目,不为NULL,0表示成功,其它表示失败。
//例程
adc1: adc@2198000 {
//...
fsl,adck-max-frequency = <30000000>, <40000000>,
<20000000>;
};
device_property_read_u32(dev, "fsl,adck-max-frequency", &info->adck_rate);
//函数原型
static inline int device_property_count_u8(struct device *dev, const char *propname)
{
return device_property_read_u8_array(dev, propname, NULL, 0);
}
static inline int device_property_count_u16(struct device *dev, const char *propname)
static inline int device_property_count_u32(struct device *dev, const char *propname)
static inline int device_property_count_u64(struct device *dev, const char *propname)
//参数
dev: 匹配的设备
propname: 属性的名称
返回:正数表示数目,负数表示不存在
//例程
adc1: adc@2198000 {
//...
fsl,adck-max-frequency = <30000000>, <40000000>,
<20000000>;
};
int count = device_property_count_u32(dev, "fsl,adck-max-frequency");
//函数原型
int copy_from_user(void *to, const void __user volatile *from, unsigned long n)
//参数
to:目的地址,内核空间
from: 源地址,用户空间
n:数据长度
返回: 0表示成功,否则返回未拷贝成功的字节数
//例程说明
copy_from_user(&zrange, argp, sizeof(struct blk_zone_range))
//函数原型
int copy_to_user(void __user volatile *to, const void *from, unsigned long n)
//参数
to:目的地址,用户空间
from: 源地址,内核空间
n:数据长度
返回: 0表示成功,否则返回未拷贝成功的字节数
//例程说明
copy_to_user(cp, &c, 1)
//函数原型
#define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
//参数
fmt: 打印的字符串
//例程说明
printk(KERN_EMERG"logger infomation!");
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
//函数原型
#define dev_dbg(dev, fmt, ...) \
dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
//参数
dev:当前所属的身边
fmt: 打印的字符串
//例程说明
dev_dbg(&pdev->dev, "device create success!");
//函数原型
#define dev_info(dev, fmt, ...) \
dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)
//参数
dev:当前所属的身边
fmt: 打印的字符串
//例程说明
dev_info(&pdev->dev, "device create success!");
//函数原型
#define dev_warn(dev, fmt, ...) \
dev_printk_index_wrap(_dev_warn, KERN_WARNING, dev, dev_fmt(fmt), ##__VA_ARGS__)
//参数
dev:当前所属的身边
fmt: 打印的字符串
//例程说明
dev_warn(&pdev->dev, "device create success!");
//函数原型
#define dev_err(dev, fmt, ...) \
dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
//参数
dev:当前所属的身边
fmt: 打印的字符串
//例程说明
dev_err(&pdev->dev, "device create failed!");
直接开始下一节说明: 内核设备树接口