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,完成发送。(比如点亮熄灭的命令)
cpp 复制代码
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接收数据,然后根据协议解析数据,完成相关的操作。

cpp 复制代码
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;
}
cpp 复制代码
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.客户在使用的时候,发现多个灯闪的时候,会出现比较大的卡顿现象(后来分析其实是命令丢失,就是第一个点亮或熄灭命令发了很久之后,才开始闪),同时按键也会出现失灵的现象(可能要多次按键才会作用)。

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

四、解决思路

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

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

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

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

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

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

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

cpp 复制代码
/*
 * 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;
#endif

static 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,   //0x1
		USER_KEY_VALUE_FUNCTION_2,   //0x2
		USER_KEY_VALUE_FUNCTION_3,   //0x3
		USER_KEY_VALUE_FUNCTION_4,   //0x4
		USER_KEY_VALUE_FUNCTION_5,   //0x5
		USER_KEY_VALUE_FUNCTION_6,   //0x6
		USER_KEY_VALUE_FUNCTION_7,   //0x7
		USER_KEY_VALUE_FUNCTION_8,   //0x8
		USER_KEY_VALUE_FUNCTION_9,   //0x9
		USER_KEY_VALUE_FUNCTION_10,   //0xa
		USER_KEY_VALUE_FUNCTION_11,   //0xb
		USER_KEY_VALUE_FUNCTION_12,   //0xc

		USER_KEY_VALUE_0,    //索引号13  -- 数字0
		USER_KEY_VALUE_1,    //索引号14  -- 数字1
		USER_KEY_VALUE_2,    //索引号15  -- 数字2
		USER_KEY_VALUE_3,    //索引号16  -- 数字3
		USER_KEY_VALUE_4,    //索引号17  -- 数字4
		USER_KEY_VALUE_5,    //索引号18  -- 数字5
		USER_KEY_VALUE_6,    //索引号19  -- 数字6
		USER_KEY_VALUE_7,    //索引号20  -- 数字7
		USER_KEY_VALUE_8,    //索引号21  -- 数字8
		USER_KEY_VALUE_9,    //索引号22  -- 数字9
		USER_KEY_VALUE_ASTERISK,    //索引号23  -- 
		USER_KEY_VALUE_POUND,    //索引号24  -- 

		USER_KEY_VALUE_TELL,    //索引号25
		USER_KEY_VALUE_VOLUME_INCREASE,    //索引号26
		USER_KEY_VALUE_VOLUME_DECREASE,    //索引号27

		USER_KEY_VALUE_SWITCH,
		USER_KEY_VALUE_INSIDE,
		USER_KEY_VALUE_OUTSIDE,

		USER_KEY_VALUE_UP,    //索引号31
		USER_KEY_VALUE_DOWN,    //索引号32
		USER_KEY_VALUE_PTT,    //索引号33
		USER_KEY_VALUE_LEFT,    //索引号34
		USER_KEY_VALUE_RIGHT,    //索引号35
		USER_KEY_VALUE_OK,    //索引号36

		USER_KEY_VALUE_KEEP,    //索引号37
		USER_KEY_VALUE_SET,    //索引号38
		USER_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;
}
#endif



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(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");
	}
#else
	if(!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");
#endif
	if(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);
#endif
	return 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];
		else
			keyboard_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 1
	if(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);
#endif
	mutex_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_MODE
		if(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通信时的一个互斥问题,但是可能不是很完善,请各位多多包含指教。

相关推荐
树℡独14 小时前
ns-3仿真之应用层(五)
服务器·网络·tcp/ip·ns3
嵩山小老虎14 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模15 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
a413244715 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Configure-Handler15 小时前
buildroot System configuration
java·服务器·数据库
津津有味道15 小时前
易语言TCP服务端接收刷卡数据并向客户端读卡器发送指令
服务器·网络协议·tcp·易语言
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.16 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
Genie cloud16 小时前
1Panel SSL证书申请完整教程
服务器·网络协议·云计算·ssl
一只自律的鸡17 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
17(无规则自律)17 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考