沙洋網(wǎng)頁(yè)定制惠州抖音seo策劃
一、input子系統(tǒng)基本框架
Linux內(nèi)核為了兩個(gè)目的:
- 簡(jiǎn)化純輸入類外設(shè)(如:鍵盤、鼠標(biāo)、游戲桿、軌跡球、觸摸屏。。。等等)的驅(qū)動(dòng)開(kāi)發(fā)
- 統(tǒng)一輸入類外設(shè)產(chǎn)生的數(shù)據(jù)格式(struct input_event),更加方便應(yīng)用層編程
設(shè)計(jì)了輸入子系統(tǒng)
事件處理層:接收來(lái)自核心層上報(bào)的事件,并選擇對(duì)應(yīng)的handler(事件處理器 struct input_handler)去處理。內(nèi)核維護(hù)著多個(gè)事件處理器對(duì)象,每個(gè)input_handler對(duì)象專門處理一類事件,所有產(chǎn)生同類事件的設(shè)備驅(qū)動(dòng)共用同一個(gè)handler。
設(shè)備驅(qū)動(dòng)層:主要實(shí)現(xiàn)獲取硬件設(shè)備的數(shù)據(jù)信息(包括觸摸屏被按下、按下位置、鼠標(biāo)移動(dòng)、鍵盤按下等等),并轉(zhuǎn)換為核心層定義的規(guī)范事件后提交給核心層,該層每個(gè)設(shè)備對(duì)應(yīng)一個(gè)struct input_dev對(duì)象,
核心層:負(fù)責(zé)連接設(shè)備驅(qū)動(dòng)層和事件處理層,為設(shè)備驅(qū)動(dòng)層提供輸入設(shè)備驅(qū)動(dòng)的接口(struct input_dev)以及輸入設(shè)備驅(qū)動(dòng)的注冊(cè)函數(shù)(input_register_device),為事件處理層提供輸入事件驅(qū)動(dòng)的接口;通知事件處理層對(duì)事件進(jìn)行處理。
二、驅(qū)動(dòng)開(kāi)發(fā)步驟
/*init或probe函數(shù)中:
1. 創(chuàng)建struct input_dev對(duì)象input_allocate_device
2. 設(shè)置事件類型以及相關(guān)參數(shù)set_bit
3. 注冊(cè)struct input_dev對(duì)象input_register_device
*//*exit或remove函數(shù)中:
1. 注銷struct input_dev對(duì)象input_unregister_device
2. 銷毀struct input_dev對(duì)象input_free_device
*//*上報(bào)事件兩種事件上報(bào)方式:1. 對(duì)有中斷支持的輸入設(shè)備:在其中斷處理函數(shù)(上半部或下半部)中上報(bào)事件2. 對(duì)無(wú)中斷支持的輸入設(shè)備:使用workqueue循環(huán)定時(shí)上報(bào)(struct delayed_work)主要函數(shù):input_eventinput_report_absinput_sync
*/
相關(guān)接口:
/*_init*/
struct input_dev *input_allocate_device(void):/*創(chuàng)建一個(gè)輸入設(shè)備對(duì)象。返回一個(gè)指向 input_dev 結(jié)構(gòu)的指針,該結(jié)構(gòu)用于表示輸入設(shè)備。*/void set_bit(struct input_dev *dev, unsigned long whichbits):/*設(shè)置輸入設(shè)備的事件類型。dev 是輸入設(shè)備對(duì)象的指針,whichbits 是一個(gè)位掩碼,用于指定事件類型。例如,使用 set_bit(dev, EV_KEY) 可以設(shè)置輸入設(shè)備支持按鍵事件。whichbits:EV_KEY:按鍵事件。這個(gè)事件類型用于處理鍵盤、鼠標(biāo)等輸入設(shè)備的按鍵事件EV_ABS:絕對(duì)坐標(biāo)事件。這個(gè)事件類型用于處理絕對(duì)坐標(biāo)的事件,例如觸摸屏的觸摸位置。*/void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)/*配置輸入設(shè)備的絕對(duì)坐標(biāo)參數(shù)。dev 是輸入設(shè)備對(duì)象的指針,axis 是要配置的坐標(biāo)軸(如ABS_X、ABS_Y等),min 和 max 分別是坐標(biāo)軸的最小值和最大值,fuzz 和 flat 是用于指定坐標(biāo)軸的模糊度和平坦度參數(shù)。*/int input_register_device(struct input_dev *dev)/*注冊(cè)輸入設(shè)備到內(nèi)核。將輸入設(shè)備對(duì)象注冊(cè)到內(nèi)核,以便它可以開(kāi)始上報(bào)事件。*//*_exit*/
void input_unregister_device(struct input_dev *dev)//注銷輸入設(shè)備。從內(nèi)核中注銷輸入設(shè)備,停止事件的上報(bào)。
void input_free_device(struct input_dev *dev)//釋放輸入設(shè)備。釋放輸入設(shè)備對(duì)象的內(nèi)存。/*上報(bào)事件*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)/*上報(bào)一個(gè)通用輸入事件。通常,這個(gè)函數(shù)用于上報(bào)不是絕對(duì)坐標(biāo)或按鍵事件的事件。dev 是輸入設(shè)備對(duì)象的指針,type 表示事件類型,code 表示事件代碼(例如,按鍵代碼),value 表示事件的值。*/void input_report_key(struct input_dev *dev, unsigned int code, int value)/*上報(bào)按鍵事件。dev 是輸入設(shè)備對(duì)象的指針,code 表示按鍵事件的代碼,value 表示按鍵事件的值(0表示松開(kāi),1表示按下)。*/void input_report_abs(struct input_dev *dev, unsigned int code, int value)/*上報(bào)絕對(duì)坐標(biāo)事件。dev 是輸入設(shè)備對(duì)象的指針,code 表示絕對(duì)坐標(biāo)事件的代碼,value 表示絕對(duì)坐標(biāo)的值。*/void input_sync(struct input_dev *dev)//上報(bào)完成后需要調(diào)用這些函數(shù)來(lái)通知系統(tǒng)處理完整事件, 這個(gè)函數(shù)告訴內(nèi)核事件已經(jīng)完整,可以處理了。/*應(yīng)用層數(shù)據(jù)類型*/
struct input_event { //這是一個(gè)用于表示輸入事件的結(jié)構(gòu)體。它包含以下字段:struct timeval time; //事件的時(shí)間戳。__u16 type; //事件類型,如 EV_KEY(按鍵事件)、EV_ABS(絕對(duì)坐標(biāo)事件)等。__u16 code; //事件代碼,具體表示事件的含義,例如按下哪個(gè)鍵或是哪個(gè)絕對(duì)坐標(biāo)軸。__s32 value; //事件的值,通常表示按鍵的狀態(tài)(按下或松開(kāi))或絕對(duì)坐標(biāo)的值。
}
三、key2-input版代碼解析
key2.c
#include <linux/module.h> // Linux內(nèi)核模塊頭文件
#include <linux/kernel.h> // 內(nèi)核相關(guān)功能的頭文件
#include <linux/fs.h> // 文件系統(tǒng)相關(guān)功能的頭文件
#include <linux/gpio.h> // GPIO庫(kù)的頭文件
#include <linux/interrupt.h> // 中斷處理相關(guān)功能的頭文件
#include <linux/of_gpio.h> // Open Firmware GPIO相關(guān)功能的頭文件
#include <linux/of_irq.h> // Open Firmware中斷相關(guān)功能的頭文件
#include <linux/cdev.h> // 字符設(shè)備相關(guān)功能的頭文件
#include <linux/wait.h> // 等待隊(duì)列相關(guān)功能的頭文件
#include <linux/sched.h> // 調(diào)度相關(guān)功能的頭文件
#include <linux/poll.h> // poll相關(guān)功能的頭文件
#include <linux/mm.h> // 內(nèi)存管理相關(guān)功能的頭文件
#include <linux/input.h> // 輸入子系統(tǒng)相關(guān)功能的頭文件
#include <linux/delay.h> // 延時(shí)相關(guān)功能的頭文件
#include <linux/slab.h> // 內(nèi)存分配相關(guān)功能的頭文件
#include <asm/uaccess.h> // 用戶態(tài)內(nèi)核態(tài)數(shù)據(jù)傳輸相關(guān)功能的頭文件struct fs4412key2_dev
{struct input_dev *pdev; // 輸入設(shè)備結(jié)構(gòu)體指針,用于注冊(cè)輸入事件int gpio; // GPIO引腳的編號(hào)int irqno; // 中斷編號(hào)
};struct fs4412key2_dev *pgmydev = NULL; // 指向驅(qū)動(dòng)程序數(shù)據(jù)結(jié)構(gòu)的指針// 中斷處理函數(shù),處理按鍵中斷
irqreturn_t key2_irq_handle(int no, void *arg)
{struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;int status1 = 0;int status2 = 0;// 讀取GPIO引腳狀態(tài)兩次,用于防抖status1 = gpio_get_value(pmydev->gpio);mdelay(1);status2 = gpio_get_value(pmydev->gpio);// 如果兩次狀態(tài)不一致,認(rèn)為是抖動(dòng),不處理if (status1 != status2){return IRQ_NONE;}// 根據(jù)按鍵狀態(tài)生成輸入事件if (status1){input_event(pmydev->pdev, EV_KEY, KEY_2, 0); // 按鍵釋放事件input_sync(pmydev->pdev); // 同步輸入事件}else{input_event(pmydev->pdev, EV_KEY, KEY_2, 1); // 按鍵按下事件input_sync(pmydev->pdev); // 同步輸入事件}return IRQ_HANDLED;
}// 模塊初始化函數(shù)
int __init fs4412key2_init(void)
{int ret = 0;struct device_node *pnode = NULL;// 查找設(shè)備樹(shù)節(jié)點(diǎn)pnode = of_find_node_by_path("/mykey2_node");if (NULL == pnode){printk("find node failed\n");return -1;}// 分配驅(qū)動(dòng)程序數(shù)據(jù)結(jié)構(gòu)內(nèi)存pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev), GFP_KERNEL);if (NULL == pgmydev){printk("kmalloc for struct fs4412key2_dev failed\n");return -1;}// 從設(shè)備樹(shù)中獲取GPIO引腳編號(hào)pgmydev->gpio = of_get_named_gpio(pnode, "key2-gpio", 0);// 從設(shè)備樹(shù)中獲取中斷編號(hào)pgmydev->irqno = irq_of_parse_and_map(pnode, 0);// 分配并注冊(cè)輸入設(shè)備pgmydev->pdev = input_allocate_device();set_bit(EV_KEY, pgmydev->pdev->evbit);set_bit(KEY_2, pgmydev->pdev->keybit);ret = input_register_device(pgmydev->pdev);// 請(qǐng)求中斷處理函數(shù)ret = request_irq(pgmydev->irqno, key2_irq_handle, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "fs4412key2", pgmydev);if (ret){printk("request_irq failed\n");input_unregister_device(pgmydev->pdev);input_free_device(pgmydev->pdev);kfree(pgmydev);pgmydev = NULL;return -1;}return 0;
}// 模塊卸載函數(shù)
void __exit fs4412key2_exit(void)
{// 釋放中斷free_irq(pgmydev->irqno, pgmydev);// 注銷輸入設(shè)備input_unregister_device(pgmydev->pdev);input_free_device(pgmydev->pdev);// 釋放驅(qū)動(dòng)程序數(shù)據(jù)結(jié)構(gòu)內(nèi)存kfree(pgmydev);pgmydev = NULL;
}MODULE_LICENSE("GPL"); // 指定模塊許可證
module_init(fs4412key2_init); // 指定模塊初始化函數(shù)
module_exit(fs4412key2_exit); // 指定模塊卸載函數(shù)
testkey2.c
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>#include <stdio.h>int main(int argc,char *argv[])
{int fd = -1;struct input_event evt;if(argc < 2){printf("Argument is too few\n");return 1;}/*open*/fd = open(argv[1],O_RDONLY);if(fd < 0){printf("open %s failed\n",argv[1]);return 2;}/*init mpu6050*/while(1){read(fd,&evt,sizeof(evt));if(evt.type == EV_KEY && evt.code == KEY_2){if(evt.value){printf("KEY2 DOWN\n");}else{printf("KEY2 UP\n");}}}/*close*/close(fd);fd = -1;return 0;
}
四、mpu6050-input版代碼解析
mpu6050drv.c
#include <linux/module.h> // Linux內(nèi)核模塊頭文件
#include <linux/kernel.h> // 內(nèi)核相關(guān)功能的頭文件
#include <linux/fs.h> // 文件系統(tǒng)相關(guān)功能的頭文件
#include <linux/i2c.h> // I2C總線相關(guān)功能的頭文件
#include <linux/cdev.h> // 字符設(shè)備相關(guān)功能的頭文件
#include <linux/wait.h> // 等待隊(duì)列相關(guān)功能的頭文件
#include <linux/sched.h> // 調(diào)度相關(guān)功能的頭文件
#include <linux/poll.h> // poll相關(guān)功能的頭文件
#include <linux/slab.h> // 內(nèi)存分配相關(guān)功能的頭文件
#include <linux/mm.h> // 內(nèi)存管理相關(guān)功能的頭文件
#include <linux/input.h> // 輸入子系統(tǒng)相關(guān)功能的頭文件
#include <linux/io.h> // I/O內(nèi)存操作相關(guān)功能的頭文件
#include <asm/uaccess.h> // 用戶態(tài)內(nèi)核態(tài)數(shù)據(jù)傳輸相關(guān)功能的頭文件
#include <asm/atomic.h> // 原子操作相關(guān)功能的頭文件/****************MPU6050內(nèi)部寄存器地址****************/#define SMPLRT_DIV 0x19 //陀螺儀采樣率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通濾波頻率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺儀自檢及測(cè)量范圍,典型值:0x18(不自檢,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速計(jì)自檢、測(cè)量范圍及高通濾波頻率,典型值:0x18(不自檢,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //電源管理,典型值:0x00(正常啟用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默認(rèn)數(shù)值0x68,只讀)
#define SlaveAddress 0x68 //MPU6050-I2C地址// 定義MPU6050設(shè)備結(jié)構(gòu)體
struct mpu6050_dev
{struct input_dev * pinput; // 輸入設(shè)備結(jié)構(gòu)體指針struct i2c_client *pclient; // I2C客戶端結(jié)構(gòu)體指針struct delayed_work work; // 延遲工作結(jié)構(gòu)體
};struct mpu6050_dev *pgmydev = NULL; // 指向MPU6050設(shè)備數(shù)據(jù)結(jié)構(gòu)的指針// 讀取MPU6050寄存器的函數(shù)
int mpu6050_read_byte(struct i2c_client *pclt, unsigned char reg)
{int ret = 0;char txbuf[1] = {reg};char rxbuf[1] = {0};struct i2c_msg msg[2] = {{pclt->addr, 0, 1, txbuf},{pclt->addr, I2C_M_RD, 1, rxbuf}};ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));if (ret < 0){printk("ret = %d, in mpu6050_read_byte\n", ret);return ret;}return rxbuf[0];
}// 寫(xiě)入MPU6050寄存器的函數(shù)
int mpu6050_write_byte(struct i2c_client *pclt, unsigned char reg, unsigned char val)
{int ret = 0;char txbuf[2] = {reg, val};struct i2c_msg msg[1] = {{pclt->addr, 0, 2, txbuf},};ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));if (ret < 0){printk("ret = %d, in mpu6050_write_byte\n", ret);return ret;}return 0;
}// 延遲工作函數(shù),用于讀取MPU6050傳感器數(shù)據(jù)
void mpu6050_work_func(struct work_struct *pwk)
{struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk, struct mpu6050_dev, work);unsigned short ax = 0;unsigned short ay = 0;unsigned short az = 0;unsigned short gx = 0;unsigned short gy = 0;unsigned short gz = 0;unsigned short temp = 0;// 讀取加速度和陀螺儀數(shù)據(jù)ax = mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_L);ax |= (mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_X, ax);ay = mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_L);ay |= (mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_Y, ay);az = mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_L);az |= (mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_Z, az);gx = mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_L);gx |= (mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_RX, gx);gy = mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_L);gy |= (mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_RY, gy);gz = mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_L);gz |= (mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_H) << 8);input_report_abs(pmydev->pinput, ABS_RZ, gz);temp = mpu6050_read_byte(pmydev->pclient, TEMP_OUT_L);temp |= (mpu6050_read_byte(pmydev->pclient, TEMP_OUT_H) << 8);input_report_abs(pmydev->pinput, ABS_MISC, temp);input_sync(pmydev->pinput);schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 延遲1秒后再次讀取數(shù)據(jù)
}// 初始化MPU6050傳感器
void init_mpu6050(struct i2c_client *pclt)
{mpu6050_write_byte(pclt, PWR_MGMT_1, 0x00);mpu6050_write_byte(pclt, SMPLRT_DIV, 0x07);mpu6050_write_byte(pclt, CONFIG, 0x06);mpu6050_write_byte(pclt, GYRO_CONFIG, 0xF8);mpu6050_write_byte(pclt, ACCEL_CONFIG, 0x19);
}// I2C設(shè)備驅(qū)動(dòng)的探測(cè)函數(shù)
static int mpu6050_probe(struct i2c_client *pclt, const struct i2c_device_id *pid)
{int ret = 0;pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);if (NULL == pgmydev){printk("kmalloc failed\n");return -1;}memset(pgmydev, 0, sizeof(struct mpu6050_dev));pgmydev->pclient = pclt;init_mpu6050(pgmydev->pclient);pgmydev->pinput = input_allocate_device();set_bit(EV_ABS, pgmydev->pinput->evbit);input_set_abs_params(pgmydev->pinput, ABS_X, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_Y, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_Z, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_RX, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_RY, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_RZ, -32768, 32767, 0, 0);input_set_abs_params(pgmydev->pinput, ABS_MISC, -32768, 32767, 0, 0);ret = input_register_device(pgmydev->pinput);if (ret){printk("input_register_device failed\n");input_free_device(pgmydev->pinput);pgmydev->pinput = NULL;kfree(pgmydev);pgmydev = NULL;return -1;}INIT_DELAYED_WORK(&pgmydev->work, mpu6050_work_func);schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 初始化后立即開(kāi)始讀取數(shù)據(jù)return 0;
}// I2C設(shè)備驅(qū)動(dòng)的卸載函數(shù)
static int mpu6050_remove(struct i2c_client *pclt)
{cancel_delayed_work(&pgmydev->work);input_unregister_device(pgmydev->pinput);input_free_device(pgmydev->pinput);pgmydev->pinput = NULL;kfree(pgmydev);pgmydev = NULL;return 0;
}// 匹配設(shè)備樹(shù)中的MPU6050節(jié)點(diǎn)
struct of_device_id mpu6050_dt[] =
{{.compatible = "invensense,mpu6050"},{}
};// 定義MPU6050設(shè)備驅(qū)動(dòng)的ID
struct i2c_device_id mpu6050_ids[] =
{{"mpu6050", 0},{}
};// 定義MPU6050設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體
struct i2c_driver mpu6050_driver =
{.driver = {.name = "mpu6050",.owner = THIS_MODULE,.of_match_table = mpu6050_dt,},.probe = mpu6050_probe,.remove = mpu6050_remove,.id_table = mpu6050_ids,
};// 注冊(cè)MPU6050設(shè)備驅(qū)動(dòng)
module_i2c_driver(mpu6050_driver);MODULE_LICENSE("GPL"); // 指定模塊許可證