/* 初始化entity的pads */
int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
struct media_pad *pads);
/* 在两个entity之间创建link */
int media_create_pad_links(const struct media_device *mdev,
const u32 source_function,
struct media_entity *source,
const u16 source_pad,
const u32 sink_function,
struct media_entity *sink,
const u16 sink_pad,
u32 flags,
const bool allow_both_undefined);
/* 开始graph的遍历,从指定的entity开始 */
void media_graph_walk_start(struct media_graph *graph,
struct media_entity *entity);
/* 启动pipeline */
__must_check int media_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe);
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_init 使用 cond_attr 指定的属性初始化条件变量 cond,当 cond_attr 为 NULL 时,使用缺省的属性
LinuxThreads 实现条件变量不支持属性,因此 cond_attr 参数实际被忽略
pthread_cond_t 类型的变量也可以用 PTHREAD_COND_INITIALIZER 常量进行静态初始化
pthread_cond_signal 使在条件变量上等待的线程中的一个线程重新开始。如果没有等待的线程,则什么也不做。如果有多个线程在等待该条件,只有一个能重启动,但不能指定哪一个。
pthread_cond_broadcast 重新启动等待该条件变量的所有线程。如果没有等待的线程,则什么也不做。
pthread_cond_wait 自动解锁互斥量(如同执行了 pthread_unlock_mutex),并等待条件变量触发。这时线程挂起,不占用 CPU 时间,直到条件变量被触发。在调用 pthread_cond_wait 之前,应用程序必须加锁互斥量。pthread_cond_wait 函数返回前,自动重新对互斥量加锁(如同执行了 pthread_lock_mutex)。
pthread_cond_wait 和 pthread_cond_timedwait 是取消点。如果一个线程在这些函数上挂起时被取消,线程立即继续执行,然后再次对 pthread_cond_wait 和 pthread_cond_timedwait 的 mutex 参数加锁,最后执行取消。因此,当调用清除处理程序时,可确保,mutex 是加锁的
pthread_cond_init、pthread_cond_signal、pthread_cond_broadcast 和 pthread_cond_wait 从不返回错误代码。
pthread_cond_timedwait 函数出错时返回下列错误代码:
ETIMEDOUT:abstime 指定的时间超时时,条件变量未触发
EINTR:pthread_cond_timedwait 被触发中断
EBUSY:某些线程正在等待该条件变量
int x,y;
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
等待直到 x > y 的执行流程:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* 对 x、y 进行操作 */
pthread_mutex_unlock(&mut);
对 x 和 y 的修改可能导致 x > y,应当触发条件变量:
pthread_mutex_lock(&mut);
/* 修改 x、y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
struct timeval now;
struct timespec timeout;
int retcode;
pthread_mutex_lock(&mut);
gettimeofday(&now);
timeout.tv_sec = now.tv_sec + 5;
timeout.tv_nsec = now.tv_usec * 1000;
retcode = 0;
while (x <= y && retcode != ETIMEDOUT) {
retcode = pthread_cond_timedwait(&cond, &mut, &timeout);
}
if (retcode == ETIMEDOUT) {
/* 发生超时 */
} else {
/* 操作 x 和 y */
}
pthread_mutex_unlock(&mut);
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
name:要查找的节点名字(不是table和name属性)。
返回值: 找到的节点,如果为 NULL 表示查找失败。
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,即 device_type 属性值。
返回值: 找到的节点,如果为 NULL 表示查找失败。
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,即 device_type 属性值(若为 NULL则表示忽略 device_type 属性)
compatible: 要查找的节点所对应的 compatible 属性列表。
返回值: 找到的节点,如果为 NULL 表示查找失败
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)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
matches: of_device_id 匹配表,也就是在此匹配表里面查找节点。
match: 找到的匹配的 of_device_id。
返回值: 找到的节点,如果为 NULL 表示查找失败
inline struct device_node *of_find_node_by_path(const char *path)
path:带有全路径的节点名,可以使用节点的别名。
返回值: 找到的节点,如果为 NULL 表示查找失败
struct device_node *of_get_parent(const struct device_node *node)
node:要查找的父节点的节点。
返回值: 找到的父节点。
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
node:父节点。
prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
返回值: 找到的下一个子节点。
property *of_find_property(const struct device_node *np, const char *name, int *lenp)
np:设备节点。
name: 属性名字。
lenp:属性值的字节数,一般为NULL
返回值: 找到的属性。
int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
np:设备节点。
proname: 需要统计元素数量的属性名字。
elem_size:每个元素的长度。(如果元素为u32类型则此处填sizeof(u32))
返回值: 得到的属性元素数量。
int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)
np:设备节点。
proname: 要读取的属性名字。
index:要读取的值标号。
out_value:读取到的值
返回值: 0 读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)
int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)
np:设备节点。
proname: 要读取的属性名字。
out_values:读取到的数组值,分别为 u8、 u16、 u32 和 u64。
sz: 要读取的数组元素数量。
返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
int of_property_read_u8(const struct device_node *np,const char *propname, u8 *out_value)
int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value)
int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value)
int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value)
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值。
返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)
np:设备节点。
proname: 要读取的属性名字。
out_string:读取到的字符串值。
返回值: 0,读取成功,负值,读取失败。
int of_n_addr_cells(struct device_node *np)
np:设备节点。
返回值: 获取到的#address-cells 属性值。
int of_n_size_cells(struct device_node *np)
np:设备节点。
返回值: 获取到的#size-cells 属性值。
int of_device_is_compatible(const struct device_node *device, const char *compat)
device:设备节点。
compat:要查看的字符串。
返回值: 0,节点的 compatible 属性中不包含 compat 指定的字符串; 正数,节点的compatible属性中包含 compat 指定的字符串。
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)
dev:设备节点。
index:要读取的地址标号。
size:地址长度。
flags:参数,比如 IORESOURCE_IO、 IORESOURCE_MEM 等
返回值: 读取到的地址数据首地址,为 NULL 的话表示读取失败。
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
dev:设备节点。
in_addr:要转换的地址。
返回值: 得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。
int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
dev:设备节点。
index:地址资源标号。
r:得到的 resource 类型的资源值。
返回值: 0,成功;负值,失败。
void __iomem *of_iomap(struct device_node *np, int index)
np:设备节点。
index: reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为0。(从0开始,一次映射一对,即一个地址一个长度)
返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。
int of_gpio_named_count(struct device_node *np, const char *propname)
np:设备节点。
propname:要统计的 GPIO 属性。
返回值: 正值,统计到的 GPIO 数量;负值,失败。
int of_gpio_count(struct device_node *np)
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO 的编号,如果只有一个 GPIO 信息的话此参数为 0
int gpio_request(unsigned gpio, const char *label)
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数返回值
label:给 gpio 设置个名字。
返回值: 0,申请成功;其他值,申请失败。
void gpio_free(unsigned gpio)
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
返回值: 0,设置成功;负值,设置失败
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
返回值: 非负值,得到的 GPIO 值;负值,获取失败
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
int gpio_to_irq(unsigned int gpio)
gpio: 要获取的 GPIO 编号
返回值: GPIO 对应的中断号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
dev: 设备节点
index:索引号, interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息
返回值:中断号
int gpio_to_irq(unsigned int gpio)
gpio: 要获取的 GPIO 编号,由gpio_request申请而来
返回值: GPIO 对应的中断号
CROSS_ARCH:=ARCH=arm CROSS_COMPILE="$(ARM_EABI_TOOLCHAIN)/arm-eabi-"
KDIR:=$(ANDROID_PRODUCT_OUT)/obj/KERNEL_OBJ/
PWD:=$(shell pwd)
obj-m:= my_module.o
.PHONY: modules package clean
all:package
modules:
@if [ "$(ANDROID_BUILD_TOP)_yes" = "_yes" ]; then echo "You have to run \". build/envsetup.sh\" to init enviroment first. \nAnd then you have to run \"choosecombo\" to setup the project."&&exit 1; fi
@if [ ! -d $(KDIR) ]; then echo "Build kernle first."&&cd $(ANDROID_BUILD_TOP)&&make bootimage&&cd -; fi
$(MAKE) $(CROSS_ARCH) -C $(KDIR) M=$(PWD) modules
package:modules
@mkdir -p ./package
@cp $(obj-m:.o=.ko) ./package
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers package
void platform_device_unregister(struct platform_device *pdev)//卸载platform设备
int platform_device_register(struct platform_device *pdev)//注册platform设备
void platform_driver_unregister(struct platform_driver *drv)//卸载platform驱动
int platform_driver_register (struct platform_driver *driver)//注册platform驱动
int misc_register(struct miscdevice * misc)//注册MISC驱动
int misc_deregister(struct miscdevice *misc)//销毁MISC驱动
struct input_dev *input_allocate_device(void)//申请input_dev
void input_free_device(struct input_dev *dev)//释放input_dev
int input_register_device(struct input_dev *dev)//注册
void input_unregister_device(struct input_dev *dev)//注销
struct input_dev *inputdev;
inputdev = input_allocate_device(); /* 申请 input_dev */
inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */
/*********第一种设置事件和事件值的方法***********/
__set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */
__set_bit(EV_REP, inputdev->evbit); /* 重复事件 */
__set_bit(KEY_0, inputdev->keybit); /*设置产生哪些按键值 */
/************************************************/
/*********第二种设置事件和事件值的方法***********/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
/************************************************/
/*********第三种设置事件和事件值的方法***********/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
/************************************************/
/* 注册 input_dev */
input_register_device(inputdev);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
void input_report_key(struct input_dev *dev, unsigned int code, int value)//上报按键
void input_report_rel(struct input_dev *dev, unsigned int code, int value)
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_mt_sync(struct input_dev *dev)
void input_sync(struct input_dev *dev)
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
typedef long __kernel_long_t;
typedef __kernel_long_t __kernel_time_t;
typedef __kernel_long_t __kernel_suseconds_t;
struct timeval {
__kernel_time_t tv_sec; /* 秒 */
__kernel_suseconds_t tv_usec; /* 微秒 */
};
static struct input_event inputevent;
int err = 0;
err = read(fd, &inputevent, sizeof(inputevent));
if (err > 0) { /* 读取数据成功 */
switch (inputevent.type) {
case EV_KEY:
if (inputevent.code < BTN_MISC) { /* 键盘键值 */
printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
} else {
printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
}
break;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
} else {
printf("读取数据失败\r\n");
}
ABS_MT_POSITION_X x[0]//上报第一个点的x坐标 input_report_abs()
ABS_MT_POSITION_Y y[0]//上报第一个点的y坐标
SYN_MT_REPORT// input_mt_sync()
ABS_MT_POSITION_X x[1]//上报第二个点的x坐标 input_report_abs()
ABS_MT_POSITION_Y y[1]//上报第二个点的y坐标
SYN_MT_REPORT// input_mt_sync()
SYN_REPORT// input_sync() 该轮数据发送完毕
ABS_MT_SLOT 0// input_mt_slot()
ABS_MT_TRACKING_ID 45// input_mt_report_slot_state()
ABS_MT_POSITION_X x[0]//上报第一个点的x坐标 input_report_abs()
ABS_MT_POSITION_Y y[0]//上报第一个点的y坐标
ABS_MT_SLOT 1// input_mt_slot()
ABS_MT_TRACKING_ID 46// input_mt_report_slot_state()
ABS_MT_POSITION_X x[1]//上报第二个点的x坐标 input_report_abs()
ABS_MT_POSITION_Y y[1]//上报第二个点的y坐标
SYN_REPORT// input_sync() 该轮数据发送完毕
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, unsigned int flags)
#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */
#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
#define INPUT_MT_DROP_UNUSED0x0004 /* drop contacts not seen in frame */
#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
#define INPUT_MT_SEMI_MT 0x0010 /* semi-mt device, finger count handled manually */
static inline void input_mt_slot(struct input_dev *dev, int slot)
void input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active)
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
......
/* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
/* 上报每一个触摸点坐标 */
for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
u8 *buf = &rdbuf[i * tplen + offset];
/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
* bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
* bit5:4 保留
* bit3:0 X轴触摸点的11~8位。
*/
type = buf[0] >> 6; /* 获取触摸类型 */
if (type == TOUCH_EVENT_RESERVED)
continue;
/* 我们所使用的触摸屏和FT5X06是反过来的 */
x = ((buf[2] << 8) | buf[3]) & 0x0fff;
y = ((buf[0] << 8) | buf[1]) & 0x0fff;
/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
* bit7:4 Touch ID 触摸ID,表示是哪个触摸点
* bit3:0 Y轴触摸点的11~8位。
*/
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;//是否按下 1:按下 0:松开
input_mt_slot(multidata->input, id);
input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);
if (!down)
continue;
input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
}
input_mt_report_pointer_emulation(multidata->input, true);
input_sync(multidata->input);
......
}
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
int register_framebuffer(struct fb_info *fb_info)
int unregister_framebuffer(struct fb_info *fb_info)
static inline void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t gfp)
static inline void dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_addr)
struct rtc_device *rtc_device_register(const char *name, struct device *dev, const struct rtc_class_ops *ops, struct module *owner)
void rtc_device_unregister(struct rtc_device *rtc)
int i2c_add_adapter(struct i2c_adapter *adapter)/* 使用动态总线号 */
int i2c_add_numbered_adapter(struct i2c_adapter *adap)/* 使用静态总线号 */
void i2c_del_adapter(struct i2c_adapter * adap)
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
void i2c_del_driver(struct i2c_driver *driver)
//S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_msg {
__u16 addr; /* 从机地址 */
__u16 flags; /* 标志 */
#define I2C_M_TEN 0x0010
#define I2C_M_RD 0x0001
#define I2C_M_STOP 0x8000
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
#define I2C_M_RECV_LEN 0x0400
__u16 len; /* 消息(本 msg)长度 */
__u8 *buf; /* 消息数据 */
};
//S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
//S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
i2c_smbus_read_byte()//S Addr Rd [A] [Data] NA P
i2c_smbus_write_byte()//S Addr Wr [A] Data [A] P
i2c_smbus_read_byte_data()//S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P
i2c_smbus_read_word_data()//S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
i2c_smbus_write_byte_data()//S Addr Wr [A] Comm [A] Data [A] P
i2c_smbus_write_word_data()//S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P
i2c_smbus_read_block_data()//S Addr Wr [A] Comm [A] S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P
i2c_smbus_write_block_data()//S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
i2c_smbus_read_i2c_block_data()//S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
i2c_smbus_write_i2c_block_data()//S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args;
args.read_write = read_write;
args.command = command;
args.size = size;
args.data = data;
return ioctl(file,I2C_SMBUS,&args);//本质就是调用ioctl
}
static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
{
return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
}
/* 设备结构体 */
struct xxx_dev {
......
void *private_data; /* 私有数据,一般会设置为 i2c_client */
};
/*
* @description : 读取 I2C 设备多个寄存器数据
* @param - dev : I2C 设备
* @param - reg : 要读取的寄存器首地址
* @param - val : 读取到的数据
* @param - len : 要读取的数据长度
* @return : 操作结果
*/
static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->private_data;
/* msg[0],第一条写消息,发送要读取的寄存器首地址 */
msg[0].addr = client->addr; /* I2C 器件地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg 长度 */
/* msg[1],第二条读消息,读取寄存器数据 */
msg[1].addr = client->addr; /* I2C 器件地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度 */
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
/*
* @description : 向 I2C 设备多个寄存器写入数据
* @param - dev : 要写入的设备结构体
* @param - reg : 要写入的寄存器首地址
* @param - val : 要写入的数据缓冲区
* @param - len : 要写入的数据长度
* @return : 操作结果
*/
static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)
dev->private_data;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */
msg.addr = client->addr; /* I2C 器件地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要发送的数据缓冲区 */
msg.len = len + 1; /* 要发送的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
struct spi_master *spi_alloc_master(struct device *dev,unsigned size)//申请
int spi_register_master(struct spi_master *master)//注册 spi_bitbang_start
void spi_master_put(struct spi_master *master)//释放
void spi_unregister_master(struct spi_master *master)//注销 spi_bitbang_stop
int spi_register_driver(struct spi_driver *sdrv);
void spi_unregister_driver(struct spi_driver *sdrv)
/*-----------------------spi_message-----------------------*/
struct spi_message {
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_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;/* 进行传输的数据长度 */
dma_addr_t tx_dma;
dma_addr_t 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;
};
int spi_setup(struct spi_device *spi)//初始化时钟和SPI模式
void spi_message_init(struct spi_message *m)
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)//将spi_transfer添加到spi_message队列中
int spi_sync(struct spi_device *spi, struct spi_message *message)
int spi_async(struct spi_device *spi, struct spi_message *message)
/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len)
{
int ret;
struct spi_message m;
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
ret = spi_sync(spi, &m); /* 同步传输 */
return ret;
}
/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len)
{
int ret;
struct spi_message m;
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
spi_message_init(&m); /* 初始化 spi_message */
spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
ret = spi_sync(spi, &m); /* 同步传输 */
return ret;
}
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
void init_waitqueue_head(wait_queue_head_t *q)//仅初始化
DECLARE_WAIT_QUEUE_HEAD//宏,定义+初始化
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
DECLARE_WAITQUEUE(name, tsk)//宏,定义并初始化
name:等待队列项的名字
tsk:这个等待队列项属于哪个任务(进程),一般设置为current(全局变量,表示当前进程)
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)//添加
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)//删除
q: 等待队列项要加入的等待队列头
wait:要加入/删除的等待队列项
void wake_up(wait_queue_head_t *q)//唤醒队列中的所有进程(包括TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE状态)
void wake_up_interruptible(wait_queue_head_t *q)//唤醒队列中的所有进程(仅唤醒TASK_INTERRUPTIBLE状态进程)
wait_event(wq, condition)//若condition为真,则唤醒等待队列,否则一直阻塞。(会将进程设置为TASK_UNINTERRUPTIBLE状态)
wait_event_timeout(wq, condition, timeout)//与wait_event类似,timeout为超时时间,单位为jiffies。返回0:超时时间到,且condition为假;返回1:condition为真
wait_event_interruptible(wq, condition)//与wait_event类似,此函数会将进程设置为TASK_INTERRUPTIBLE,即可以被信号打断
wait_event_interruptible_timeout(wq, condition, timeout)//与wait_event_timeout类似,此函数会将进程设置为TASK_INTERRUPTIBLE,即可以被信号打断
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
nfds: 要操作的文件描述符个数
readfds:指向描述符集合,fd_set类型。用于监视指定描述符集的读变化,所指定集合中有一个文件可以读取,则返回大于 0 的值,否则根据 timeout 参数来判断是否超时。设置为 NULL,表示不关心任何文件的读变化
writefds:指向描述符集合,fd_set类型。用于监视指定描述符集的写变化,所指定集合中有一个文件可以写入,则返回大于 0 的值,否则根据 timeout 参数来判断是否超时。设置为 NULL,表示不关心任何文件的写变化
exceptfds:指向描述符集合,fd_set类型。用于监视指定描述符集的异常变化,所指定集合中有一个文件异常,则返回大于 0 的值,否则根据 timeout 参数来判断是否超时。设置为 NULL,表示不关心任何文件的异常变化
void FD_ZERO(fd_set *set)//将 fd_set 变量的所有位都清零
void FD_SET(int fd, fd_set *set)//将 fd_set 变量的某个位置 1(向 fd_set 添加一个文件描述符), fd:要加入的文件描述符
void FD_CLR(int fd, fd_set *set)//将 fd_set 变量的某个位清零(向 fd_set 删除一个文件描述符), fd:要删除的文件描述符
int FD_ISSET(int fd, fd_set *set)//测试 fd_set 的某个位是否置 1(判断某个文件是否可以进行操作), fd:要判断的文件描述符
timeout:超时时间,设为 NULL 表示无限期等待。timeval结构体类型
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微秒 */
};
返回值: 0,超时发生,没有任何文件描述符可以进行操作; -1,发生错误;其他值,可以进行操作的文件描述符个数
void main(void)
{
int ret, fd; /* 要监视的文件描述符 */
fd_set readfds; /* 读操作文件描述符集 */
struct timeval timeout; /* 超时结构体 */
fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
FD_ZERO(&readfds); /* 清除 readfds */
FD_SET(fd, &readfds); /* 将 fd 添加到 readfds 里面 */
/* 构造超时时间 */
timeout.tv_sec = 0;
timeout.tv_usec = 500000; /* 500ms */
ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
switch (ret) {
case 0: /* 超时 */
......
break;
case -1: /* 错误 */
......
break;
default: /* 可以读取数据 */
if(FD_ISSET(fd, &readfds)) { /* 判断是否为 fd 文件描述符 */
/* 使用 read 函数读取数据 */
}
break;
}
}
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 请求的事件 */
short revents; /* 返回的事件 */
};
POLLIN 有数据可以读取
POLLPRI 有紧急的数据需要读取
POLLOUT 可以写数据
POLLERR 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起
POLLNVAL 无效的请求
POLLRDNORM 等同于POLLIN
void main(void)
{
int ret;
int fd; /* 要监视的文件描述符 */
struct pollfd fds;
fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式访问 */
/* 构造结构体 */
fds.fd = fd;
fds.events = POLLIN; /* 监视数据是否可以读取 */
ret = poll(&fds, 1, 500); /* 轮询文件是否可操作,超时 500ms */
if (ret) { /* 数据有效 */
......
/* 读取数据 */
......
} else if (ret == 0) { /* 超时 */
......
} else if (ret < 0) { /* 错误 */
......
}
}
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
epfd:epoll 句柄(即epoll_create的返回值)
EPOLL_CTL_ADD 向epfd添加文件参数fd表示的描述符
EPOLL_CTL_MOD 修改参数fd的event事件
EPOLL_CTL_DEL 从epfd中删除fd描述符
struct epoll_event {
uint32_t events; /* epoll 事件 */
epoll_data_t data; /* 用户数据 */
};
EPOLLIN 有数据可以读取
EPOLLPRI 有紧急的数据需要读取
EPOLLOUT 可以写数据
EPOLLERR 指定的文件描述符发生错误
EPOLLHUP 指定的文件描述符挂起
EPOLLET 设置epoll为边沿触发(默认为水平触发)
EPOLLONESHOT 一次性监视(当监视完成以后需要再次监视某个fd,就需要将fd重新添加到epoll里面)
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
epfd:epoll 句柄
events:指向 epoll_event 结构体的数组,当有事件发生的时候 Linux 内核会填写 events,调用者可以根据 events 判断发生了哪些事件
maxevents:events 数组大小(必须大于 0)
timeout:超时时间,单位为 ms
POLLIN 有数据可以读取
POLLPRI 有紧急的数据需要读取
POLLOUT 可以写数据
POLLERR 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起
POLLNVAL 无效的请求
POLLRDNORM 等同于POLLIN,普通数据可读
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
wait_address:要添加到 poll_table 中的等待队列头
p:poll_table,就是file_operations 中 poll 函数的 wait 参数
struct xxxx_dev {
......
struct wait_queue_head_t r_wait; /* 等待队列头 */
};
unsigned int xxx_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct xxxx_dev *dev = (struct xxxx_dev *)filp->private_data;
poll_wait(filp, &dev->r_wait, wait);
if(......) { /* 相关条件满足 */
mask = POLLIN | POLLRDNORM; /* 返回 PLLIN */
}
return mask;
}
#define SIGHUP 1 /* 终端挂起或控制进程终止 */
#define SIGINT 2 /* 终端中断(Ctrl+C 组合键) */
#define SIGQUIT 3 /* 终端退出(Ctrl+\组合键) */
#define SIGILL 4 /* 非法指令 */
#define SIGTRAP 5 /* debug 使用,有断点指令产生 */
#define SIGABRT 6 /* 由 abort(3)发出的退出指令 */
#define SIGIOT 6 /* IOT 指令 */
#define SIGBUS 7 /* 总线错误 */
#define SIGFPE 8 /* 浮点运算错误 */
#define SIGKILL 9 /* 杀死、终止进程 */
#define SIGUSR1 10 /* 用户自定义信号 1 */
#define SIGSEGV 11 /* 段违例(无效的内存段) */
#define SIGUSR2 12 /* 用户自定义信号 2 */
#define SIGPIPE 13 /* 向非读管道写入数据 */
#define SIGALRM 14 /* 闹钟 */
#define SIGTERM 15 /* 软件终止 */
#define SIGSTKFLT 16 /* 栈异常 */
#define SIGCHLD 17 /* 子进程结束 */
#define SIGCONT 18 /* 进程继续 */
#define SIGSTOP 19 /* 停止进程的执行,只是暂停 */
#define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */
#define SIGTTIN 21 /* 后台进程需要从终端读取数据 */
#define SIGTTOU 22 /* 后台进程需要向终端写数据 */
#define SIGURG 23 /* 有"紧急"数据 */
#define SIGXCPU 24 /* 超过 CPU 资源限制 */
#define SIGXFSZ 25 /* 文件大小超额 */
#define SIGVTALRM 26 /* 虚拟时钟信号 */
#define SIGPROF 27 /* 时钟信号描述 */
#define SIGWINCH 28 /* 窗口大小改变 */
#define SIGIO 29 /* 可以进行输入/输出操作 */
#define SIGPOLL SIGIO
/* #define SIGLOS 29 */
#define SIGPWR 30 /* 断点重启 */
#define SIGSYS 31 /* 非法的系统调用 */
#define SIGUNUSED 31 /* 未使用信号 */
sighandler_t signal(int signum, sighandler_t handler)
typedef void (*sighandler_t)(int)
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
/* 设置信号 SIGIO 的处理函数 */
signal(SIGIO, sigio_signal_func);
fcntl(fd, F_SETOWN, getpid()); /* 将当前进程的进程号告诉给内核 */
flags = fcntl(fd, F_GETFD); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC);/* 设置进程状态为 FASYNC 启用异步通知功能(此时会调用驱动中的fasync函数) */
while(1) {
sleep(2);
}
close(fd);
return 0;
struct fasync_struct {
spinlock_t fa_lock;
int magic;
int fa_fd;
struct fasync_struct *fa_next;
struct file *fa_file;
struct rcu_head fa_rcu;
};
void kill_fasync(struct fasync_struct **fp, int sig, int band)
int (*fasync) (int fd, struct file *filp, int on)
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
struct xxx_dev {
......
struct fasync_struct *async_queue; /* 异步相关结构体 */
};
static int xxx_fasync(int fd, struct file *filp, int on)
{
struct xxx_dev *dev = (xxx_dev)filp->private_data;
if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)//直接调用该函数即可,不用干其他事
return -EIO;
return 0;
}
static struct file_operations xxx_ops = {
......
.fasync = xxx_fasync,
.release = xxx_release,
......
};
static int xxx_release(struct inode *inode, struct file *filp)
{
return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
}
typedef struct {
int counter;
} atomic_t;
ATOMIC_INIT(int i);//定义时初始化
int atomic_read(atomic_t *v);
void atomic_set(atomic_t *v, int i)
void atomic_add(int i, atomic_t *v)
void atomic_sub(int i, atomic_t *v)
void atomic_dec(atomic_t *v)//自减
void atomic_inc(atomic_t *v)//自增
int atomic_dec_return(atomic_t *v)//自减并返回v
int atomic_inc_return(atomic_t *v)//自增并返回v
int atomic_sub_and_test(int i, atomic_t *v)//(v-i)==0?1:0(返回真、假)
int atomic_dec_and_test(atomic_t *v)//(v--)==0?1:0(返回真、假)
int atomic_inc_and_test(atomic_t *v)//(v++)==0?1:0(返回真、假)
int atomic_add_negative(int i, atomic_t *v)//(v+i)<0?1:0(返回真、假)
void set_bit(int nr, void *p)//将p地址的第nr位置 1
void clear_bit(int nr,void *p)
void change_bit(int nr, void *p)//翻转
int test_bit(int nr, void *p)//获取
int test_and_set_bit(int nr, void *p)//将p地址的第nr位置1,并返回nr位原来的值
int test_and_clear_bit(int nr, void *p)//...清零...
int test_and_change_bit(int nr, void *p)//...翻转...
void spin_lock_irq(spinlock_t *lock)//禁止本地中断,并获取自旋锁
void spin_unlock_irq(spinlock_t *lock)//激活本地中断,并释放自旋锁
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)//保存中断状态,禁止本地中断,并获取自旋锁
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)//恢复中断状态,并且激活本地中断,释放自旋锁
void spin_lock_bh(spinlock_t *lock)//关闭下半部,并获取自旋锁
void spin_unlock_bh(spinlock_t *lock)//打开下半部,并释放自旋锁
DEFINE_SEAMPHORE(name)//定义信号量 并设值为1
void sema_init(struct semaphore *sem, int val)//初始化,并设值为val
void down(struct semaphore *sem)//获取信号量(会导致休眠,不能在中断中使用,不能被信号打断)
int down_interruptible(struct semaphore *sem)//获取信号量(会导致休眠,不能在中断中使用,可以被信号打断)
int down_trylock(struct semaphore *sem)//尝试获取信号量(成功返回 0。否则返回非 0)
void up(struct semaphore *sem)//释放信号量
DEFINE_MUTEX(name)//定义并初始化
void mutex_init(mutex *lock)//初始化
void mutex_lock(struct mutex *lock)//上锁,失败则休眠,不可以被信号打断
int mutex_lock_interruptible(struct mutex *lock)//上锁,失败则休眠,可以被信号打断
void mutex_unlock(struct mutex *lock)//解锁
int mutex_trylock(struct mutex *lock)//尝试加锁,成功返回1,失败返回0
int mutex_is_locked(struct mutex *lock)//判断是否加锁,是返回1,否返回0
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author");
//printk
#define KERN_EMERG KERN_SOH "0" /* 紧急事件,一般是内核崩溃 */
#define KERN_ALERT KERN_SOH "1" /* 必须立即采取行动 */
#define KERN_CRIT KERN_SOH "2" /* 临界条件,比如严重的软件或硬件错误*/
#define KERN_ERR KERN_SOH "3" /* 错误状态,一般设备驱动程序中使用KERN_ERR 报告硬件错误 */
#define KERN_WARNING KERN_SOH "4" /* 警告信息,不会对系统造成严重影响 */
#define KERN_NOTICE KERN_SOH "5" /* 有必要进行提示的一些信息 */
#define KERN_INFO KERN_SOH "6" /* 提示性的信息 */
#define KERN_DEBUG KERN_SOH "7" /* 调试信息 */
//注册字符设备(浪费设备号)
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);
//注销字符设备
static inline void unregister_chrdev(unsigned int major, const char *name);
name:设备名字,指向一串字符串
/*注册(方法一,自己确定设备号)*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);//详见设备号API
cdev.owner = THIS_MODULE;
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
/*注册(方法二,系统分配设备号)*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);//详见设备号API
cdev.owner = THIS_MODULE;
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
/*注销*/
void cdev_del(struct cdev *p);
void unregister_chrdev_region(dev_t from, unsigned count);
class_create(owner, name)//创建类 owner一般为THIS_MODULE
void class_destroy(struct class *cls);//卸载类
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);//创建设备 parent=NULL drvdata=NULL
void device_destroy(struct class *class, dev_t devt)//卸载设备
MAJOR(dev)//dev_t -> 主设备号
MINOR(dev)//dev_t -> 次设备号
MKDEV(ma,mi)//主设备号+次设备号 -> dev_t
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
dev:存放起始设备编号的指针,当注册成功,*dev就会等于分配到的起始设备编号,可以通过MAJOR()和MINNOR()宏来提取主次设备号
baseminor:次设备号基地址,也就是起始次设备号
count:要申请的数量,一般都是一个
name:字符设备名称
返回值小于0,表示注册失败
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from:要申请的起始设备号,也就是给定的设备号
count:要申请的数量,一般都是一个
name:设备名字
当返回值小于0,表示注册失败
static inline long copy_to_user(void __user *to, const void *from, unsigned long n)
to:用户空间指针
from:内核空间指针
n:从内核空间向用户空间拷贝数据的字节数
成功返回0,失败返回失败数目
static inline long copy_from_user(void *to, const void __user * from, unsigned long n)
to:内核空间指针
from:用户空间指针
n:从用户空间向内核空间拷贝数据的字节数
成功返回0,失败返回失败数目
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE)
phys_addr:要映射的物理起始地址。
size:要映射的内存空间大小。
void iounmap (volatile void __iomem *addr)
u8 readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)
void writeb(u8 value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)
#查看设备号
cat /proc/devices
#创建设备节点
mknod /dev/xxx c 主设备号 次设备号
#查看设备树节点
ls /proc/device-tree
ls /sys/firmware/devicetree/base
#查看platform相关
ls /sys/bus/platform/devices # 设备
ls /sys/bus/platform/drivers # 驱动
#查看misc相关
ls /sys/class/misc # 驱动
#驱动相关
depmod
modprobe xxx.ko
insmod xxx.ko
lsmod
rmmod xxx.ko
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
time_after(unkown, known)//unkown 时间上超过 known 则返回真
time_before(unkown, known)//unkown 时间上滞后 known 则返回真
time_after_eq(unkown, known)//unkown 时间上超过或等于 known 则返回真
time_before_eq(unkown, known)//unkown 时间上滞后或等于 known 则返回真
int jiffies_to_msecs(const unsigned long j)//jiffies类型j --> ms
int jiffies_to_usecs(const unsigned long j)//jiffies类型j --> us
u64 jiffies_to_nsecs(const unsigned long j)//jiffies类型j --> ns
long msecs_to_jiffies(const unsigned int m)//ms --> jiffies
long usecs_to_jiffies(const unsigned int u)//us --> jiffies
unsigned long nsecs_to_jiffies(u64 n)//ns --> jiffies
void ndelay(unsigned long nsecs)
void udelay(unsigned long usecs)
void mdelay(unsigned long mseces)
struct timer_list {
struct list_head entry;
unsigned long expires; /* 定时器超时时间,单位是节拍数 */
struct tvec_base *base;
void (*function)(unsigned long); /* 定时处理函数 */
unsigned long data; /* 要传递给 function 函数的参数 */
int slack;
};
void init_timer(struct timer_list *timer);//初始化
void add_timer(struct timer_list *timer);//注册并使能
int del_timer(struct timer_list * timer);//删除(需等待 定时处理函数 退出)
int del_timer_sync(struct timer_list *timer);//删除(需等待其他处理器使用完定时器,中断上下文勿用)
int mod_timer(struct timer_list *timer, unsigned long expires);//修改定时值(会使能定时器)
返回值: 0,定时器未被使能; 1,定时器已经使能
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件include/linux/interrupt.h里面查看所有的中断标志。
IRQF_SHARED 多个设备共享一个中断线,共享的所有中断都必须指定此标志。 如果使用共享中断的话,request_irq函数的 dev 参数是唯一区分他们的标志
IRQF_ONESHOT 单次中断,中断执行一次就结束
IRQF_TRIGGER_NONE 无触发
IRQF_TRIGGER_RISING 上升沿触发
IRQF_TRIGGER_FALLING 下降沿触发
IRQF_TRIGGER_HIGH 高电平触发
IRQF_TRIGGER_LOW 低电平触发
name:中断名字,设置以后可以在/proc/interrupts文件中看到对应的中断名字
dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数
返回值: 0 中断申请成功;其他负值:中断申请失败;-EBUSY:中断已经被申请了
int devm_request_threaded_irq(struct device *dev, unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
void free_irq(unsigned int irq, void *dev)
irq: 要释放的中断
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉
irqreturn_t (*irq_handler_t) (int, void *)
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)//需等待 正在执行的中断处理函数执行完
void disable_irq_nosync(unsigned int irq)//无需等待 正在执行的中断处理函数执行完
local_irq_disable()//关闭全局中断
local_irq_enable()//使能全局中断
local_irq_save(flags)//保存中断标志到flags并 关闭全局中断
local_irq_restore(flags)//根据flags设置中断标志并 使能全局中断
struct softirq_action
{
void (*action)(struct softirq_action *);
};
enum
{
HI_SOFTIRQ=0,//高优先级软中断
TIMER_SOFTIRQ,//定时器软中断
NET_TX_SOFTIRQ,//网络数据发送软中断
NET_RX_SOFTIRQ,//网络数据接收软中断
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,//tasklet 软中断
SCHED_SOFTIRQ,//调度软中断
HRTIMER_SOFTIRQ,//高精度定时器软中断
RCU_SOFTIRQ,//RCU 软中断
NR_SOFTIRQS
};
static struct softirq_action softirq_vec[NR_SOFTIRQS];
void open_softirq(int nr, void (*action)(struct softirq_action *))
nr:要开启的软中断,在上述enum中选择一个
action:软中断对应的处理函数
void raise_softirq(unsigned int nr)
struct tasklet_struct
{
struct tasklet_struct *next; /* 下一个 tasklet */
unsigned long state; /* tasklet 状态 */
atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
void (*func)(unsigned long); /* tasklet 执行的函数,用户定义,相当于中断处理函数 */
unsigned long data; /* 函数 func 的参数 */
};
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);//初始化
DECLARE_TASKLET(name, func, data)//宏,定义并初始化
func:tasklet 的处理函数
data:要传递给 func 函数的参数
void tasklet_schedule(struct tasklet_struct *t)
t:要调度的 tasklet,也就是 DECLARE_TASKLET 宏里面的 name
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /* 工作队列处理函数 */
};
struct workqueue_struct {
struct list_head pwqs;
struct list_head list;
struct mutex mutex;
int work_color;
int flush_color;
atomic_t nr_pwqs_to_flush;
struct wq_flusher *first_flusher;
struct list_head flusher_queue;
struct list_head flusher_overflow;
struct list_head maydays;
struct worker *rescuer;
int nr_drainers;
int saved_max_active;
struct workqueue_attrs *unbound_attrs;
struct pool_workqueue *dfl_pwq;
char name[WQ_NAME_LEN];
struct rcu_head rcu;
unsigned int flags ____cacheline_aligned;
struct pool_workqueue __percpu *cpu_pwqs;
struct pool_workqueue __rcu *numa_pwq_tbl[];
};
struct worker {
union {
struct list_head entry;
struct hlist_node hentry;
};
struct work_struct *current_work;
work_func_t current_func;
struct pool_workqueue *current_pwq;
bool desc_valid;
struct list_head scheduled;
struct task_struct *task;
struct worker_pool *pool;
struct list_head node;
unsigned long last_active;
unsigned int flags;
int id;
char desc[WORKER_DESC_LEN];
struct workqueue_struct *rescue_wq;
};
#define INIT_WORK(_work, _func)//初始化,需要自己创建work_struct
#define DECLARE_WORK(n, f)//创建和初始化,无需自己创建work_struct
bool schedule_work(struct work_struct *work)
work: 要调度的工作
返回值: 0 成功,其他值 失败
test: main.c
arm-linux-gnueabihf-gcc -o $@ $^
arm-linux-gnueabihf-objdump -D test > test.dis
clean:
rm test *.dis
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b)
{
return a + b;
}
int main(int argc, char **argv)
{
int a;
int b;
char *endptr;
if (argc != 3)
{
printf("Usage: %s <val1> <val2>\n", argv[0]);
return -1;
}
a = (int)strtol(argv[1], NULL, 0);
b = (int)strtol(argv[2], NULL, 0);
printf("%d + %d = %d\n", a, b, add(a, b));
}
00010404 <add>:
10404: b480 push {r7}
10406: b083 sub sp, #12
10408: af00 add r7, sp, #0
1040a: 6078 str r0, [r7, #4]
1040c: 6039 str r1, [r7, #0]
1040e: 687a ldr r2, [r7, #4]
10410: 683b ldr r3, [r7, #0]
10412: 4413 add r3, r2
10414: 4618 mov r0, r3
10416: 370c adds r7, #12
10418: 46bd mov sp, r7
1041a: f85d 7b04 ldr.w r7, [sp], #4
1041e: 4770 bx lr
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b)
{
int sum;
__asm__ volatile (
"add %0, %1, %2"
:"=r"(sum)
:"r"(a), "r"(b)
:"cc"
);
return sum;
}
int main(int argc, char **argv)
{
int a;
int b;
if (argc != 3)
{
printf("Usage: %s <val1> <val2>\n", argv[0]);
return -1;
}
a = (int)strtol(argv[1], NULL, 0);
b = (int)strtol(argv[2], NULL, 0);
printf("%d + %d = %d\n", a, b, add(a, b));
return 0;
}
asm [Qualifiers] (
``AssemblerTemplate``
: ``OutputOperands``
: ``InputOperands``
: ``Clobbers``
)
asm [Qualifiers] goto (
``AssemblerTemplate``
: /* No outputs. */
: ``InputOperands``
: ``Clobbers``
: ``GotoLabels``
)
#include <stdio.h>
#include <stdlib.h>
int test(int a, int b)
{
__asm__ goto (
"cmp %1, %0\n\t"
/* BLE指令:b比a小则跳转 */
"blt %l2"
: /* No outputs. */
: "r"(a), "r"(b)
: "cc"
: carry);
return 0;
carry:
return 1;
}
int main(int argc, char **argv)
{
int a;
int b;
if (argc != 3)
{
printf("Usage: %s <val1> <val2>\n", argv[0]);
return -1;
}
a = (int)strtol(argv[1], NULL, 0);
b = (int)strtol(argv[2], NULL, 0);
printf("test return is %d\n",test(a, b));
return 0;
}
./test 1 2
test return is 0
./test 3 2
test return is 1
./test 2 2
test return is 0