linux下一个iic驱动(按键+点灯)-互斥

一、前提:

硬件部分:

1. rk3399开发板,其中的某一路iic,这个作为总线的主控制器

2. gd32单片机,其中的某一路iic,从设备。主要是按键上报和灯的亮灭控制。(按键大约30个,灯在键的下面,对应有30个左右。)

3. iic主要的功能是两个部分,是主动上报按键值,和接收点灯命令(收到后需要发送应答)。

4. 该路iic总线上还有一个触摸屏的设备。(不在介绍范围,这个说明一下单片机端不能改为主设备,cpu改为从设备的原因)。

5.cpu与单片机之间有一个中断io引脚,用于有按键(或者应答)数据时,提示cpu(rk3399)的作用,低电平有效。

软件部分:

1.linux4.4.194的内核,arm64平台,自编ic驱动程序。

2.单片机也是自己做的iic程序,使用硬件iic,协议自定义,与驱动一致即可。(本文不介绍单片机的程序)。

3.数据帧的格式(前期已经定好,不再修改):cpu(rk3399,下同)发送指令由5个字节组成,设备(单片机,下同)回复指令由7个字节组成。

cpu主动发送部分:

 设备发送部分:

 

二、iic驱动基本思路和逻辑

1. 发送数据时,首先把数据组合好,调用i2c_master_send,完成发送。(比如点亮熄灭的命令)

i2c_master_send(s_jc_keyboard_info.i2c_client, (char *)&keyboard_send_msg, sizeof(KEYBOARD_I2C_SEND_MSG_S)) != sizeof(KEYBOARD_I2C_SEND_MSG_S)

2.接收数据,单片机发送数据时,是肯定有一个中断触发cpu,所以cpu处理是在中断处理函数中。

   收到中断后,考虑到iic的数据是串行通信,比较费时,所以采用工作队列的方式处理,收到中断,其实就是启动一次工作队列,这时使用了内核自己的队列。

    队列启动后,使用i2c_master_recv接收数据,然后根据协议解析数据,完成相关的操作。

static irqreturn_t s_jc_keyboard_i2c_isr(int irq, void *dev_id)
{if(irq != s_jc_keyboard_info.i2c_client->irq) {return IRQ_NONE;}if(!schedule_work(&jc_keyboard_work)) {pr_err("Error schedule_work!\n");}return IRQ_HANDLED;
}
i2c_master_recv(s_jc_keyboard_info.i2c_client, (char *)&keyboard_recv_msg, sizeof(KEYBOARD_I2C_RECV_MSG_S)) != sizeof(KEYBOARD_I2C_RECV_MSG_S)

三、一个互斥问题。

1. 有闪烁灯的控制,而且是多个灯的闪烁,频率

    这种情况,cpu主动发送数据和设备应答的数据(cpu主动读取数据)比较多,每一次设备应答都会中断,中断比较频繁。

2.同时多次按键,组合键

    这个情况下,中断比较多,cpu主要是读取数据。

3.确实有异常的表现,报出一些数据帧不能解析的错误,偶尔有按键不能正常上报按下和松开的状态。

4.客户在使用的时候,发现多个灯闪的时候,会出现比较大的卡顿现象(后来分析其实是命令丢失,就是第一个点亮或熄灭命令发了很久之后,才开始闪),同时按键也会出现失灵的现象(可能要多次按键才会作用)。

5. 不知道是不是频繁中断的影响,schedule_work偶尔也会报出错误提示,原因未知。

四、解决思路

1. 中断是个问题,cpu发送数据和接收数据的过程中是很有可能被中断打断的,这个打断会不会导致数据帧出现错误。尤其是有数据下发的点灯命令和频繁按键上报的情况。

2.点灯命令的应答和按键上报的中断同时(或者间隔很短的情况下)会如何?

3.加上互斥锁(iic_idel_Mutex),一个发送流程(包括发送和接收应答)走完,才能进行下一步操作。比如点灯,必须把数据发完,同时接收到应答(或者超时)才能发送下一帧数据。

4.第二把互斥锁(iic_Mutex,不知道是否多余),每一次发送和接收都加锁,接收发送函数结束后解锁。

5.加上自定义的一个工作队列,使用自己的工作队列处理中断事件。

五、加上锁后,基本解决这个问题。

详细请参考一下源代码(部分主要源码,h文件省略了)

/** jc_keyboard.c**  Created on: Dec 25, 2021*      Author: dazhi*/#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <linux/workqueue.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <linux/moduleparam.h>#include "jc_keyboard.h"
#include "jc_keyboard_cmd.h"
#include "jc_keyuser.h"#define JC_KEYBOARD_DRIVER_NAME		"jc_keyboard"
#define JC_KEYBOARD_IRQ_GPIO_NAME	"jc_keyboard_irq_gpio"
#define JC_KEYBOARD_IRQ_NAME		"jc_keyboard_irq"
#define JC_KEYBOARD_MISCDEV_MINOR	(20)
#define JC_KEYBOARD_I2C_TIMEOUT		(4000)	//4000 --> 1s  2022-09-06 dazhi修改,客户处调试暂时已通过。#define NO_POLL_MODE   //不主动查询,需要单片机给出中断才行。
#define USE_SELF_WORK_QUEUE //static int jc_keyboard_inited = -1;  //是否加载成功,2023-01-09#ifdef USE_SELF_WORK_QUEUE
//分配工作队列的指针
static struct workqueue_struct *jc_keyboard_wq;
#endifstatic int debug_print = 0;   //默认不打印调试语句static struct mutex iic_Mutex;      //互斥锁
static struct mutex iic_idel_Mutex;      //iic空闲互斥锁
static volatile unsigned long wait_respon_bits = 0; //需要等待什么应答,每一个位对应一种需求module_param(debug_print,int, 0644);   //加载时传递参数typedef struct {int irq_gpio;struct input_dev *input_dev;struct i2c_client *i2c_client;struct semaphore ioctl_sem;
} JC_KEYBOARD_INFO;static char s_i2c_reply_ret = 0;
static struct miscdevice s_jc_keyboard_miscdevice;
static struct work_struct jc_keyboard_work;
static JC_KEYBOARD_INFO s_jc_keyboard_info;//按键转换值的数组,定义在jc_keyuser.h
static const unsigned char s_user_key_value[] = {0,USER_KEY_VALUE_FUNCTION_1,   //0x1USER_KEY_VALUE_FUNCTION_2,   //0x2USER_KEY_VALUE_FUNCTION_3,   //0x3USER_KEY_VALUE_FUNCTION_4,   //0x4USER_KEY_VALUE_FUNCTION_5,   //0x5USER_KEY_VALUE_FUNCTION_6,   //0x6USER_KEY_VALUE_FUNCTION_7,   //0x7USER_KEY_VALUE_FUNCTION_8,   //0x8USER_KEY_VALUE_FUNCTION_9,   //0x9USER_KEY_VALUE_FUNCTION_10,   //0xaUSER_KEY_VALUE_FUNCTION_11,   //0xbUSER_KEY_VALUE_FUNCTION_12,   //0xcUSER_KEY_VALUE_0,    //索引号13  -- 数字0USER_KEY_VALUE_1,    //索引号14  -- 数字1USER_KEY_VALUE_2,    //索引号15  -- 数字2USER_KEY_VALUE_3,    //索引号16  -- 数字3USER_KEY_VALUE_4,    //索引号17  -- 数字4USER_KEY_VALUE_5,    //索引号18  -- 数字5USER_KEY_VALUE_6,    //索引号19  -- 数字6USER_KEY_VALUE_7,    //索引号20  -- 数字7USER_KEY_VALUE_8,    //索引号21  -- 数字8USER_KEY_VALUE_9,    //索引号22  -- 数字9USER_KEY_VALUE_ASTERISK,    //索引号23  -- USER_KEY_VALUE_POUND,    //索引号24  -- USER_KEY_VALUE_TELL,    //索引号25USER_KEY_VALUE_VOLUME_INCREASE,    //索引号26USER_KEY_VALUE_VOLUME_DECREASE,    //索引号27USER_KEY_VALUE_SWITCH,USER_KEY_VALUE_INSIDE,USER_KEY_VALUE_OUTSIDE,USER_KEY_VALUE_UP,    //索引号31USER_KEY_VALUE_DOWN,    //索引号32USER_KEY_VALUE_PTT,    //索引号33USER_KEY_VALUE_LEFT,    //索引号34USER_KEY_VALUE_RIGHT,    //索引号35USER_KEY_VALUE_OK,    //索引号36USER_KEY_VALUE_KEEP,    //索引号37USER_KEY_VALUE_SET,    //索引号38USER_KEY_VALUE_TEST    //索引号39---> 0x27
};//左侧(中括号中的数)是客户自动自定义的键值 比如KC_L1 --> 1
//右侧是s_user_key_value中对应值得索引号,比如数字0,在数组中排13,就是0xd,
static const unsigned char s_user_map_led_value[] = {[1] = 0x01	, //图示1左1	     //KC_L1   1[3] = 0x02	, //图示2左2	     //KC_L2   3[5] = 0x03	, //图示3左3	     //KC_L3   5[7] = 0x04	, //图示4左4	     //KC_L4   7[9] = 0x05	, //图示5左5	     //KC_L5   9[44] = 0x06	, //图示6左6	     //KC_L6   44[2] = 0x07	, //图示7右1	     //KC_R1   2[4] = 0x08	, //图示8右2	     //KC_R2   4[6] = 0x09	, //图示9右3	     //KC_R3   6[8] = 0x0A	, //图示10右4     //KC_R4	8  [10] = 0x0B	, //图示11右5     //KC_R5	10[45] = 0x0C	, //图示12右6	//KC_R6    45[27] = 0x0D	, //0		//KC_NUM0   27[18] = 0x0E	, //1		//KC_NUM1   18[19] = 0x0F	, //2		//KC_NUM2   19	[20] = 0x10	, //3		//KC_NUM3   20	[21] = 0x11	, //4		//KC_NUM4   21	[22] = 0x12	, //5		//KC_NUM5   22	[23] = 0x13	, //6		//KC_NUM6   23	[24] = 0x14	, //7		//KC_NUM7   24	[25] = 0x15	, //8		//KC_NUM8   25	[26] = 0x16	, //9		//KC_NUM9   26	[28] = 0x17	, //*		//KC_DOT   28	[29] = 0x18	, //#		//KC_CLEAR   29	[13] = 0x19	, //电话(拨号)			//KC_TEL   13[35] = 0x1A	, //音量+		//KC_VOLUME_UP	   35[36] = 0x1B	, //音量-		//KC_VOLUME_DOWN	36	[14] = 0x27	, //TEST		//KC_TESKKEY   14   //原0x1c改0x27[11] = 0x1D	, //内通			//KC_INC      11[12] = 0x1E	, //外通			//KC_EXC      12 [30] = 0x1F	, //上			//KC_UP       30[31] = 0x20	, //下			//KC_DOWN     31[17] = 0x21	, //PTT		//KC_PTT        17	[32] = 0x22	, //左			//KC_LEFT   32[33] = 0x23	, //右			//KC_FIGHT  33[34] = 0x24	, //OK			//KC_OK     34[15] = 0x25	, //保持			//KC_HOLD   15[43] = 0x26	, //设置/摘挂机		//KC_RECOV  16改成43 原来[16] = 0x26	,[42] = 0x27	, //测试/复位		//KC_xxx	[37] = 0x28	, //指示灯-红		//LED_RED  37	[38] = 0x29	, //指示灯-绿		//LED_GREEN 38	[39] = 0x2A	, //指示灯-蓝		//KC_BLUE   39	[40] = 0x2B	 //全部键灯			//ALL_KEY_LED  40
};static const struct of_device_id s_jc_keyboard_match_table[] = {{ .compatible = "jc,keyboard", },{},
};static const struct i2c_device_id s_jc_keyboard_i2c_id[] = {{ JC_KEYBOARD_DRIVER_NAME, 0},{}
};static void s_jc_keyboard_work_func_t(struct work_struct *work) {KEYBOARD_I2C_RECV_MSG_S keyboard_recv_msg;unsigned char i = 0;unsigned char cmd_verify_tmp = 0;
//	pr_err("2023debug s_jc_keyboard_work_func_t\n");memset(&keyboard_recv_msg, 0, sizeof(KEYBOARD_I2C_RECV_MSG_S));mutex_lock(&iic_Mutex);  //加锁if(i2c_master_recv(s_jc_keyboard_info.i2c_client, (char *)&keyboard_recv_msg, sizeof(KEYBOARD_I2C_RECV_MSG_S)) != sizeof(KEYBOARD_I2C_RECV_MSG_S)) {pr_err("Error i2c_master_recv, addr = %#x\n", s_jc_keyboard_info.i2c_client->addr);mutex_unlock(&iic_Mutex);  //开锁return;}mutex_unlock(&iic_Mutex);  //开锁if(debug_print)pr_err("i2c_master_recv: h0 = %#x, h1 = %#x type = %#x, key0=%#x, key1=%#x,key2=%#x,verify=%#x\n",keyboard_recv_msg.cmd_header0,keyboard_recv_msg.cmd_header1,keyboard_recv_msg.cmd_type,keyboard_recv_msg.cmd_key0,keyboard_recv_msg.cmd_key1,keyboard_recv_msg.cmd_key2,keyboard_recv_msg.cmd_verify);cmd_verify_tmp = FRAME_VERIFY(keyboard_recv_msg.cmd_header0,keyboard_recv_msg.cmd_header1,keyboard_recv_msg.cmd_type,keyboard_recv_msg.cmd_key0,keyboard_recv_msg.cmd_key1,keyboard_recv_msg.cmd_key2);if(keyboard_recv_msg.cmd_header0 != FRAME_HEADER_0 || keyboard_recv_msg.cmd_header1 != FRAME_HEADER_1) {pr_err("Error recv cmd header: %#x %#x\n", keyboard_recv_msg.cmd_header0, keyboard_recv_msg.cmd_header1);return;}if(keyboard_recv_msg.cmd_verify != cmd_verify_tmp) {if(debug_print)pr_err("Error verify %#x\n", keyboard_recv_msg.cmd_verify);return;}if((keyboard_recv_msg.cmd_type & 0xfc) == FRAME_CMD_TYPE_KEY_LED_FLASH)  //闪烁的指令{s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(KEY_LED_FLASH_RESPONSE_BIT, &wait_respon_bits);   //置高某一位up(&s_jc_keyboard_info.ioctl_sem);return ;		}//	pr_err("2023debug s_jc_keyboard_work_func_t cmd_type = %d,cmd_key2 = %d\n",keyboard_recv_msg.cmd_type,keyboard_recv_msg.cmd_key2);switch(keyboard_recv_msg.cmd_type) {case FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_PRESS:case FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_RELEASE:if(s_jc_keyboard_info.input_dev) {//	unsigned char key;unsigned char key_val[3] = {keyboard_recv_msg.cmd_key0,keyboard_recv_msg.cmd_key1,keyboard_recv_msg.cmd_key2};if(debug_print)pr_info("key1 = %d key2 = %d key3 = %d\n",key_val[0],key_val[1],key_val[2]);for(i=0;(i<3);i++)//for(i=0;(i<3) && key_val[i];i++){//key = keyboard_recv_msg.cmd_key0? keyboard_recv_msg.cmd_key0:keyboard_recv_msg.cmd_key2;if(key_val[i] < KEY_VALUE_FUNCTION_1 || key_val[i] > KEY_VALUE_TEST) {continue;// pr_err("Error key %#x out of range!\n", key_val[i]);// break;}input_report_key(s_jc_keyboard_info.input_dev, s_user_key_value[key_val[i]], (keyboard_recv_msg.cmd_type == FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_PRESS)? 1:0);}input_sync(s_jc_keyboard_info.input_dev);}break;case FRAME_CMD_TYPE_GET_BRIGHTNESS:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_GET_PANEL_MODEL:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_GET_PANEL_VER:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_SET_BRIGHTNESS:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_RESET:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(RESET_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_KEY_LED_ON:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(KEY_LED_ON_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;case FRAME_CMD_TYPE_KEY_LED_OFF:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;clear_bit(KEY_LED_OFF_RESPONSE_BIT, &wait_respon_bits);   //清零某一位up(&s_jc_keyboard_info.ioctl_sem);break;default:pr_err("Error non-supported cmd %#x\n", keyboard_recv_msg.cmd_type);break;}
}#ifndef NO_POLL_MODE
//读取消息超时的处理
static int s_jc_keyboard_inttimeout_func_t(void) {KEYBOARD_I2C_RECV_MSG_S keyboard_recv_msg;unsigned char cmd_verify_tmp = 0;// if(jc_keyboard_inited != 1)  //驱动没有加载成功!!// 	return -1;
//	pr_err("2023debug s_jc_keyboard_inttimeout_func_t\n");memset(&keyboard_recv_msg, 0, sizeof(KEYBOARD_I2C_RECV_MSG_S));mutex_lock(&iic_Mutex);  //加锁if(i2c_master_recv(s_jc_keyboard_info.i2c_client, (char *)&keyboard_recv_msg, sizeof(KEYBOARD_I2C_RECV_MSG_S)) != sizeof(KEYBOARD_I2C_RECV_MSG_S)) {pr_err("Error i2c_master_recv, addr = %#x\n", s_jc_keyboard_info.i2c_client->addr);mutex_unlock(&iic_Mutex);  //开锁return -1;}mutex_unlock(&iic_Mutex);  //开锁cmd_verify_tmp = FRAME_VERIFY(keyboard_recv_msg.cmd_header0,keyboard_recv_msg.cmd_header1,keyboard_recv_msg.cmd_type,keyboard_recv_msg.cmd_key0,keyboard_recv_msg.cmd_key1,keyboard_recv_msg.cmd_key2);if(keyboard_recv_msg.cmd_header0 != FRAME_HEADER_0 || keyboard_recv_msg.cmd_header1 != FRAME_HEADER_1) {pr_err("Error recv cmd header: %#x %#x\n", keyboard_recv_msg.cmd_header0, keyboard_recv_msg.cmd_header1);return -1;}if(keyboard_recv_msg.cmd_verify != cmd_verify_tmp) {pr_err("Error verify %#x\n", keyboard_recv_msg.cmd_verify);return -1;}switch(keyboard_recv_msg.cmd_type) {case FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_PRESS:case FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_RELEASE:if(s_jc_keyboard_info.input_dev) {unsigned char key = keyboard_recv_msg.cmd_key0? keyboard_recv_msg.cmd_key0:keyboard_recv_msg.cmd_key2;if(key < KEY_VALUE_FUNCTION_1 || key > KEY_VALUE_TEST) {pr_err("Error key %#x out of range!\n", key);break;}input_report_key(s_jc_keyboard_info.input_dev, s_user_key_value[key], (keyboard_recv_msg.cmd_type == FRAME_CMD_TYPE_GET_KEY_VALUE_REPLY_PRESS)? 1:0);input_sync(s_jc_keyboard_info.input_dev);}break;case FRAME_CMD_TYPE_GET_BRIGHTNESS:case FRAME_CMD_TYPE_GET_PANEL_MODEL:case FRAME_CMD_TYPE_GET_PANEL_VER:case FRAME_CMD_TYPE_SET_BRIGHTNESS:case FRAME_CMD_TYPE_RESET:case FRAME_CMD_TYPE_KEY_LED_ON:case FRAME_CMD_TYPE_KEY_LED_OFF:s_i2c_reply_ret = keyboard_recv_msg.cmd_key2;//	up(&s_jc_keyboard_info.ioctl_sem);break;default:pr_err("Error non-supported cmd %#x\n", keyboard_recv_msg.cmd_type);return -1;}return 0;
}
#endifstatic irqreturn_t s_jc_keyboard_i2c_isr(int irq, void *dev_id)
{if(irq != s_jc_keyboard_info.i2c_client->irq) {return IRQ_NONE;}if(debug_print)pr_err("enter jc_keyboard_i2c_isr\n");#ifdef USE_SELF_WORK_QUEUE//将自己的工作和自己的工作队列进行管理,然后再登记if(!queue_work(jc_keyboard_wq, &jc_keyboard_work)) {pr_err("Error queue_work 2023!\n");}
#elseif(!schedule_work(&jc_keyboard_work)) {pr_err("Error schedule_work!\n");}
#endif	return IRQ_HANDLED;
}static int s_jc_keyboard_irq_init(void) {if(s_jc_keyboard_info.irq_gpio <= 0) {pr_err("Error irq_gpio is %d\n", s_jc_keyboard_info.irq_gpio);return -1;}if(s_jc_keyboard_info.i2c_client->irq <= 0) {pr_err("Error irq is %d\n", s_jc_keyboard_info.i2c_client->irq);return -1;}if(gpio_request(s_jc_keyboard_info.irq_gpio, JC_KEYBOARD_IRQ_GPIO_NAME)) {pr_err("Error gpio_request %s\n", JC_KEYBOARD_IRQ_GPIO_NAME);return -1;}if(gpio_direction_input(s_jc_keyboard_info.irq_gpio)) {pr_err("Error gpio_direction_input %d\n", s_jc_keyboard_info.irq_gpio);gpio_free(s_jc_keyboard_info.irq_gpio);return -1;}INIT_WORK(&jc_keyboard_work, s_jc_keyboard_work_func_t);#ifdef USE_SELF_WORK_QUEUE//创建自己的工作队列和自己的内核线程jc_keyboard_wq = create_workqueue("myjc_keyboard_wq");
#endifif(request_irq(s_jc_keyboard_info.i2c_client->irq, s_jc_keyboard_i2c_isr, IRQF_TRIGGER_FALLING, JC_KEYBOARD_IRQ_NAME, NULL)) {pr_err("Error request_irq!\n");gpio_free(s_jc_keyboard_info.irq_gpio);return -1;}return 0;
}static int s_jc_keyboard_irq_exit(void) {if(s_jc_keyboard_info.i2c_client->irq > 0) {free_irq(s_jc_keyboard_info.i2c_client->irq, NULL);}if(s_jc_keyboard_info.irq_gpio > 0) {gpio_free(s_jc_keyboard_info.irq_gpio);}#ifdef USE_SELF_WORK_QUEUE//销毁自己的工作队列和内核线程destroy_workqueue(jc_keyboard_wq);
#endifreturn 0;
}static int s_jc_keyboard_input_init(void) {int i = 0;if(s_jc_keyboard_info.input_dev) {pr_err("Error input_dev is not null!\n");return -1;}if(!(s_jc_keyboard_info.input_dev = input_allocate_device())) {pr_err("Error input_allocate_device!\n");return -1;}s_jc_keyboard_info.input_dev->name = JC_KEYBOARD_DRIVER_NAME;for(i = KEY_VALUE_FUNCTION_1; i <= KEY_VALUE_TEST; i ++) {input_set_capability(s_jc_keyboard_info.input_dev, EV_KEY, s_user_key_value[i]);}if(input_register_device(s_jc_keyboard_info.input_dev)) {pr_err("Error input_register_device!\n");input_free_device(s_jc_keyboard_info.input_dev);s_jc_keyboard_info.input_dev = NULL;return -1;}return 0;
}static int s_jc_keyboard_input_exit(void) {if(!s_jc_keyboard_info.input_dev) {pr_err("Error input_dev is null!\n");return -1;}input_unregister_device(s_jc_keyboard_info.input_dev);input_free_device(s_jc_keyboard_info.input_dev);s_jc_keyboard_info.input_dev = NULL;return 0;
}static long s_jc_keyboard_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long argv) {KEYBOARD_I2C_SEND_MSG_S keyboard_send_msg;if(debug_print)pr_err("enter jc_keyboard_unlocked_ioctl\n");// if(jc_keyboard_inited != 1)  //驱动没有加载成功!!// 	return -1;if(_IOC_TYPE(cmd) != KEYBOARD_IOC_MAGIC) {pr_err("Error cmd %d\n", cmd);return -1;}memset(&keyboard_send_msg, 0, sizeof(KEYBOARD_I2C_SEND_MSG_S));keyboard_send_msg.cmd_header0 = FRAME_HEADER_0;keyboard_send_msg.cmd_header1 = FRAME_HEADER_1;switch(cmd) {case KEYBOARD_IOC_GET_BRIGHTNESS:keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_GET_BRIGHTNESS;set_bit(BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_SET_BRIGHTNESS:if(!argv || copy_from_user(&keyboard_send_msg.cmd, (void *)argv, 1)) {pr_err("Error copy_from_user!\n");return -2;}if(keyboard_send_msg.cmd < FRAME_CMD_TYPE_SET_BRIGHTNESS_MIN || keyboard_send_msg.cmd > FRAME_CMD_TYPE_SET_BRIGHTNESS_MAX) {pr_err("Error brightness out of range!\n");return -3;}keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_SET_BRIGHTNESS;set_bit(SET_BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_GET_PANEL_MODEL:keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_GET_PANEL_MODEL;set_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_GET_PANEL_VER:keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_GET_PANEL_VER;set_bit(PANEL_VER_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_RESET:keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_RESET;set_bit(RESET_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_KEY_LED_FLASH:   //2023-04-28{unsigned char user_val = 0;unsigned char flashtype = 0;if(!argv || copy_from_user(&user_val, (void *)argv, 1)) {pr_err("Error KEYBOARD_IOC_KEY_LED_FLASH copy_from_user!\n");return -4;}keyboard_send_msg.cmd = s_user_map_led_value[user_val & 0x3f];//(user_val & 0x3f);	//闪烁哪个灯flashtype = ((user_val >> 6) & 0x3);   //闪烁类型keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_KEY_LED_FLASH | flashtype;  //发过去的命令是0x80,81,82,83}set_bit(KEY_LED_FLASH_RESPONSE_BIT, &wait_respon_bits);   //置高某一位break;case KEYBOARD_IOC_KEY_LED_ON:case KEYBOARD_IOC_KEY_LED_OFF: {//	int i = KEY_VALUE_FUNCTION_1;unsigned char user_key_code = 0;if(!argv || copy_from_user(&user_key_code, (void *)argv, 1)) {pr_err("Error copy_from_user!\n");return -5;}// for(; i < KEY_VALUE_TEST; i ++) {// 	if(s_user_key_value[i] == user_key_code) {// 		keyboard_send_msg.cmd = i;// 		break;// 	}// }if(user_key_code < sizeof(s_user_map_led_value)/sizeof(s_user_map_led_value[0]))keyboard_send_msg.cmd = s_user_map_led_value[user_key_code];elsekeyboard_send_msg.cmd = 0;  //20220905//2023-01-05  不能识别的按键if(keyboard_send_msg.cmd == 0){pr_err("Error keyboard_send_msg.cmd == 0! user_key_code = %d\n",user_key_code);return -6;}// if(i >= KEY_VALUE_TEST) {// 	if(user_key_code >= KEY_RED_LED && user_key_code <= ALL_KEY_VALUE) {  /*lsr modify 20220512 (KEY_BLUE_LED)*/// 		keyboard_send_msg.cmd = user_key_code;// 	}// 	else {// 		pr_err("Error key code %#x out of range!\n", keyboard_send_msg.cmd);// 		return -1;// 	}// }if(cmd == KEYBOARD_IOC_KEY_LED_ON) {keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_KEY_LED_ON;set_bit(KEY_LED_ON_RESPONSE_BIT, &wait_respon_bits);   //置高某一位}else {keyboard_send_msg.cmd_type = FRAME_CMD_TYPE_KEY_LED_OFF;set_bit(KEY_LED_OFF_RESPONSE_BIT, &wait_respon_bits);   //置高某一位}break;}default:pr_err("Error non-supported cmd %d\n", cmd);return -7;}keyboard_send_msg.cmd_verify = FRAME_VERIFY(keyboard_send_msg.cmd_header0,keyboard_send_msg.cmd_header1,keyboard_send_msg.cmd_type,keyboard_send_msg.cmd,0,0);
#if 1if(debug_print)pr_err("i2c_master_send: h0 = %#x, h1 = %#x type = %#x, %#x, %#x\n",keyboard_send_msg.cmd_header0,keyboard_send_msg.cmd_header1,keyboard_send_msg.cmd_type,keyboard_send_msg.cmd,keyboard_send_msg.cmd_verify);
#endifmutex_lock(&iic_idel_Mutex);   //不让发mutex_lock(&iic_Mutex);  //加锁if(i2c_master_send(s_jc_keyboard_info.i2c_client, (char *)&keyboard_send_msg, sizeof(KEYBOARD_I2C_SEND_MSG_S)) != sizeof(KEYBOARD_I2C_SEND_MSG_S)) {pr_err("Error i2c_master_send!\n");mutex_unlock(&iic_Mutex);  //开锁mutex_unlock(&iic_idel_Mutex);  //开锁return -8;}mutex_unlock(&iic_Mutex);//开锁if(down_timeout(&s_jc_keyboard_info.ioctl_sem, JC_KEYBOARD_I2C_TIMEOUT/4)) {//没有中断的情况会超时
#ifndef NO_POLL_MODEif(s_jc_keyboard_inttimeout_func_t())	
#endif{pr_err("Error down_timeout!\n");mutex_unlock(&iic_idel_Mutex);  //开锁return -9;}}mutex_unlock(&iic_idel_Mutex);  //开锁switch(cmd) {case KEYBOARD_IOC_GET_BRIGHTNESS:if(test_bit(SET_BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_GET_BRIGHTNESS down_timeout! ----test_bit\n");clear_bit(SET_BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits);return -20;}if(!argv || copy_to_user((void *)argv, &s_i2c_reply_ret, 1)) {pr_err("Error copy_to_user!\n");return -10;}break;case KEYBOARD_IOC_GET_PANEL_MODEL:if(test_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_GET_PANEL_MODEL down_timeout! ----test_bit\n");clear_bit(PANEL_MODEL_RESPONSE_BIT, &wait_respon_bits);return -21;}if(!argv || copy_to_user((void *)argv, &s_i2c_reply_ret, 1)) {pr_err("Error copy_to_user!\n");return -10;}break;case KEYBOARD_IOC_GET_PANEL_VER:if(test_bit(PANEL_VER_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_GET_PANEL_VER down_timeout! ----test_bit\n");clear_bit(PANEL_VER_RESPONSE_BIT, &wait_respon_bits);return -22;}if(!argv || copy_to_user((void *)argv, &s_i2c_reply_ret, 1)) {pr_err("Error copy_to_user!\n");return -10;}break;case KEYBOARD_IOC_SET_BRIGHTNESS:if(test_bit(SET_BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_SET_BRIGHTNESS down_timeout! ----test_bit\n");clear_bit(SET_BRIGHTNESS_RESPONSE_BIT, &wait_respon_bits);return -23;}if(s_i2c_reply_ret == FRAME_CMD_REPLY_FAILED) {pr_err("Error execute failed!\n");return -11;}break;case KEYBOARD_IOC_RESET:if(test_bit(RESET_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_RESET down_timeout! ----test_bit\n");clear_bit(RESET_RESPONSE_BIT, &wait_respon_bits);return -24;}if(s_i2c_reply_ret == FRAME_CMD_REPLY_FAILED) {pr_err("Error execute failed!\n");return -11;}break;	case KEYBOARD_IOC_KEY_LED_ON:if(test_bit(KEY_LED_ON_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_KEY_LED_ON down_timeout! ----test_bit\n");clear_bit(KEY_LED_ON_RESPONSE_BIT, &wait_respon_bits);return -25;}if(s_i2c_reply_ret == FRAME_CMD_REPLY_FAILED) {pr_err("Error execute failed!\n");return -11;}break;case KEYBOARD_IOC_KEY_LED_OFF:   //灯的控制部分,不再等待应答,2022-09-05		if(test_bit(KEY_LED_OFF_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_KEY_LED_OFF down_timeout! ----test_bit\n");clear_bit(KEY_LED_OFF_RESPONSE_BIT, &wait_respon_bits);return -26;}//return 0;if(s_i2c_reply_ret == FRAME_CMD_REPLY_FAILED) {pr_err("Error execute failed!\n");return -11;}break;default:if((cmd & 0xfc) == KEYBOARD_IOC_KEY_LED_FLASH)  //闪烁的指令{if(test_bit(KEY_LED_FLASH_RESPONSE_BIT, &wait_respon_bits)){pr_err("Error KEYBOARD_IOC_KEY_LED_FLASH down_timeout! ----test_bit\n");clear_bit(KEY_LED_FLASH_RESPONSE_BIT, &wait_respon_bits);return -27;}return 0;		}break;}return 0;
}static struct file_operations s_jc_keyboard_fops = {.unlocked_ioctl = s_jc_keyboard_unlocked_ioctl,
};static int s_jc_keyboard_misc_init(void) {memset(&s_jc_keyboard_miscdevice, 0, sizeof(struct miscdevice));s_jc_keyboard_miscdevice.minor = JC_KEYBOARD_MISCDEV_MINOR;s_jc_keyboard_miscdevice.name = JC_KEYBOARD_DRIVER_NAME;s_jc_keyboard_miscdevice.fops = &s_jc_keyboard_fops;if(misc_register(&s_jc_keyboard_miscdevice)) {pr_err("Error misc_register!\n");return -1;}sema_init(&s_jc_keyboard_info.ioctl_sem, 0);return 0;
}static int s_jc_keyboard_misc_exit(void) {misc_deregister(&s_jc_keyboard_miscdevice);return 0;
}static int jc_kayboard_command_for_read(struct i2c_client *client)
{int ret = -1;uint8_t tmpbuf[10] = {0};struct i2c_msg msg[1];msg[0].addr = client->addr;msg[0].flags = I2C_M_RD;/*Read*/msg[0].len = 7;msg[0].buf = tmpbuf;ret = i2c_transfer(client->adapter, msg, 1);return ret;
}//static void s_jc_keyboard_exit(void);static int s_jc_keyboard_probe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id) {int ret;
//	jc_keyboard_inited = -1;if(debug_print)pr_err("addr = %#x\n", i2c_client->addr);ret = jc_kayboard_command_for_read(i2c_client);  //尝试着读一次if(ret < 0)  //读不了直接返回{pr_err("error : jc_keyboard_command_for_read,no probe\n");
//		s_jc_keyboard_exit();return ret;}memset(&s_jc_keyboard_info, 0, sizeof(JC_KEYBOARD_INFO));if((s_jc_keyboard_info.irq_gpio = of_get_named_gpio(i2c_client->dev.of_node, "irq-gpio", 0)) < 0) {pr_err("Error of_get_named_gpio %s\n", "irq-gpio");return -1;}if((i2c_client->irq = gpio_to_irq(s_jc_keyboard_info.irq_gpio)) < 0) {pr_err("Error gpio_to_irq %d\n", s_jc_keyboard_info.irq_gpio);return -1;}s_jc_keyboard_info.i2c_client = i2c_client;if(s_jc_keyboard_input_init()) {pr_err("Error s_jc_keyboard_input_init!\n");return -1;}if(s_jc_keyboard_misc_init()) {pr_err("Error s_jc_keyboard_misc_init!\n");s_jc_keyboard_input_exit();return -1;}if(s_jc_keyboard_irq_init()) {pr_err("Error s_jc_keyboard_irq_init!\n");s_jc_keyboard_misc_exit();s_jc_keyboard_input_exit();return -1;}mutex_init(&iic_Mutex);  //初始化互斥锁mutex_init(&iic_idel_Mutex);
//	jc_keyboard_inited = 1;  //加载成功!!return 0;
}static int s_jc_keyboard_remove(struct i2c_client *i2c_client) {// if(jc_keyboard_inited != 1)  //驱动没有加载成功!!// 	return -1;if(s_jc_keyboard_irq_exit()) {pr_err("Error s_jc_keyboard_irq_exit!\n");}if(s_jc_keyboard_misc_exit()) {pr_err("Error s_jc_keyboard_misc_exit!\n");}if(s_jc_keyboard_input_exit()) {pr_err("Error s_jc_keyboard_input_exit!\n");}return 0;
}static struct i2c_driver s_i2c_driver_jc_keyboard = {.probe = s_jc_keyboard_probe,.remove = s_jc_keyboard_remove,.driver = {.name = JC_KEYBOARD_DRIVER_NAME,.owner = THIS_MODULE,.of_match_table = s_jc_keyboard_match_table,},.id_table = s_jc_keyboard_i2c_id,
};// static int s_jc_keyboard_init(void) {
// 	if(i2c_add_driver(&s_i2c_driver_jc_keyboard)) {
// 		pr_err("Error s_jc_keyboard_init i2c_add_driver!\n");
// 		return -1;
// 	}
// 	return 0;
// }// static void s_jc_keyboard_exit(void) {
// 	return;
// //	i2c_del_driver(&s_i2c_driver_jc_keyboard);
// }module_i2c_driver(s_i2c_driver_jc_keyboard);     //2023-03-30  增加 // module_init(s_jc_keyboard_init);
// module_exit(s_jc_keyboard_exit);
// date 需要修改内核的makefile ,注释 886 KBUILD_CFLAGS   += $(call cc-option,-Werror=date-time)
MODULE_DESCRIPTION("Buildtime :"__DATE__" "__TIME__);
MODULE_AUTHOR("dazhi@jc,keyboard,2023-05");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.1.0");    //2023-05-10 版本1.1.0

这里做了一个简单的分析,解决iic通信时的一个互斥问题,但是可能不是很完善,请各位多多包含指教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

送呆萌的她一个皮卡丘(Python实现)

目录 1 呆萌的她 2 思维需要革新 3 送她的一个漂亮皮卡丘 4 Python完整代码奉上 1 呆萌的她 又是一季春风暖阳下, 你是一湾一湾羞涩的春波。 静静感受着&#xff0c; 你垂下的枝膊 在我的脸上轻轻抚摸 一对春燕,低低掠过 涟漪乍起&#xff0c;是你浅浅的笑窝...... 2 思…

(五)「消息队列」之 RabbitMQ 主题(使用 .NET 客户端)

0、引言 先决条件 本教程假设 RabbitMQ 已安装并且正在 本地主机 的标准端口&#xff08;5672&#xff09;上运行。如果您使用了不同的主机、端口或凭证&#xff0c;则要求调整连接设置。 获取帮助 如果您在阅读本教程时遇到问题&#xff0c;可以通过邮件列表或者 RabbitMQ 社区…

56 # 实现 pipe 方法进行拷贝

pipe 是异步的&#xff0c;可以实现读一点写一点&#xff0c;管道的优势&#xff1a;不会淹没可用内存&#xff0c;但是在导入的过程中无法获取到内容 const fs require("fs"); const path require("path");fs.createReadStream(path.resolve(__dirname…

前端 | (七)浮动 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;浮动介绍&#x1f407;元素浮动后的特点&#x1f407;浮动小练习&#x1f525;盒子1右浮动&#x1f525;盒子1左浮动&#x1f525;所有盒子都浮动&#x1f5…

数学建模 插值算法

有问题 牛顿差值也有问题它们都有龙格现象&#xff0c;一般用分段插值。 插值预测要比灰色关联预测更加准确&#xff0c;灰色预测只有2次 拟合样本点要非常多&#xff0c;样本点少差值合适

Spring底层

配置文件 配置优先级 之前讲解过&#xff0c;可以用这三种方式进行配置 那如果这三种都进行了配置&#xff0c;那到底哪一份生效呢&#xff1f; 结论 优先级从大到小 properties>yml>yaml然后就是现在一般都用yml文件进行配置 其他配置方式 除了配置文件外 还有不同…

电压放大器在超声波焊接中的作用以及应用

电压放大器是一种运用于电子设备中的信号放大器&#xff0c;主要作用是将小信号放大为更高幅度的信号。在超声波焊接中&#xff0c;电压放大器起到了重要的作用&#xff0c;它可以将从传感器采集到的微小信号放大为能够被检测和处理的合适大小的信号。 超声波焊接是现代工业生产…

微信怎么自动加好友,通过好友后自动打招呼

很多客户朋友每天花大量的时间用手机搜索添加好友&#xff0c;这样的添加很集中也容易频繁&#xff0c;而且效率还低。对方通过后&#xff0c;有时也不能及时和客户搭建链接&#xff0c;导致客户也流失了。 现在可以实现自动添加和自动打招呼哦&#xff0c;只需要导入数据、设置…

【从零开始学CSS | 第二篇】伪类选择器

目录 前言&#xff1a; 伪类选择器&#xff1a; 常见的伪类选择器&#xff1a; 举例&#xff1a; 小窍门&#xff1a; 总结: 前言&#xff1a; 上一篇文章我们详细的为大家介绍了一些常见的选择器&#xff0c;这几篇我们将再次介绍CSS中的一个常见选择器——伪类选择器&am…

设计模式之适配器模式

写在前面 适配器设计模式属于结构型设计模式的一种&#xff0c;本文一起来看下。 1&#xff1a;介绍 1.1&#xff1a;什么时候适配器设计模式 当现有接口客户端无法直接调用时&#xff0c;我们可以考虑适配器设计模式&#xff0c;来定义一个能够供客户端直接调用的接口&…

软件测试的分类

代码分类&#xff1a; 1、黑盒测试 2、白盒测试 3、灰黑测试 黑盒测试&#xff1a; 把测试的对象看成是一个黑色的盒子的&#xff0c;看不到里面内部的结构&#xff0c;是对软件的一种功能性的测试。 白盒测试&#xff1a; 就是把测试的对象看成是一个透明的盒子&#x…

测试老鸟总结,性能测试-最佳并发和最大并发,性能测试实施...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试&#xf…

curl操作

下载路径&#xff1a;https://curl.se/windows/ 参考&#xff1a;https://blog.csdn.net/weixin_45191386/article/details/130652821 操作&#xff1a; curl http://localhost:8085/api/v1/aaa/bbbb/?ccc 652781344055627776

第四次CCF计算机软件能力认证

第一题&#xff1a;图像旋转 旋转是图像处理的基本操作&#xff0c;在这个问题中&#xff0c;你需要将一个图像逆时针旋转 90 度。 计算机中的图像表示可以用一个矩阵来表示&#xff0c;为了旋转一个图像&#xff0c;只需要将对应的矩阵旋转即可。 输入格式 输入的第一行包含两…

我国版式文档格式OFD前端WEB展示之EasyOFD

EasyOFD an ofd file web shower 一个在web端展示ofd文件的控件&#xff0c;该控件基于CANVAS绘制。 该控件使用了以下外部程序 1&#xff09;jszip&#xff1a;解决解压文件。 2&#xff09;x2js: 解决XML文件到JS转换 3&#xff09;easyjbig2: 解决ofd内部使用jb2文件存储的…

NSSCTF刷web(2)

[NISACTF 2022]bingdundun~ bingdundun处感觉像文件包含,改upload为index 发现确实,猜测会补一个后缀.php 那常规文件包含都不行了,这里还有一个文件上传的功能,考虑phar协议 <?php$phar new Phar("test.phar"); $phar->startBuffering(); $phar->setStu…

【excel细碎小知识点】

目录索引 &符号的用法&#xff1a;实例演示&#xff1a; 数字显示和位数的区别&#xff1a;分列功能的妙用&#xff1a;什么叫做常规类型&#xff1a; &符号的用法&#xff1a; **连接字符串:**转化后都是文本字符串类型。你可以通过修改数据类型进行更多可能的操作 实…

【Go语言开发】简单了解一下搜索引擎并用go写一个demo

写在前面 这篇文章我们一起来了解一下搜索引擎的原理&#xff0c;以及用go写一个小demo来体验一下搜索引擎。 简介 搜索引擎一般简化为三个步骤 爬虫&#xff1a;爬取数据源&#xff0c;用做搜索数据支持。索引&#xff1a;根据爬虫爬取到的数据进行索引的建立。排序&#xf…

prometheus调整默认数据存储时间

调整kubernetes部署的prometheus数据存储时间 由于prometheus是用kuberentes部署的&#xff0c;没办法像传统部署方式那种直接在启动参数增加存储时间的参数。需要在configmap里或者在deployment里添加&#xff0c;我这里使用的方式是在deployement里添加调整存储时间的参数。…

学会在重装系统前如何备份软件,再也不怕失去珍贵的应用!

​Windows系统是电脑的重要组成部分&#xff0c;它不仅提供了友好的用户界面&#xff0c;还承担着许多关键的功能和任务&#xff0c;为我们提供了一个稳定、安全和效率的工作环境&#xff0c;使我们能够充分发挥电脑的潜力&#xff0c;优化工作效率和生活品质。 随着系统使…