OK335xS knob driver hacking

时间:2023-12-27 11:08:25
/*************************************************************************
* OK335xS knob driver hacking
* 说明:
* 本文主要是为了分析knob设备的创建,驱动层如何注册,发送信息。
*
* 2015-11-18 晴 深圳 南山平山村 曾剑锋
************************************************************************/ #include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/mux.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <asm/uaccess.h>
#include <linux/completion.h>
#include <linux/input.h> /**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* Table 2-2. L4_WKUP Peripheral Memory Map (continued)
* +----------------+---------------------+-------------------+---------------------------------+
* | Region Name | Start Address (hex) | End Address (hex) | Size Description |
* +----------------+---------------------+-------------------+---------------------------------+
* | Control Module | 0x44E1_0000 | 0x44E1_1FFF | 128KB Control Module Registers |
* +----------------+---------------------+-------------------+---------------------------------+
*/
#define Control_Module_address 0x44E10000
/**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* Table 9-10. CONTROL_MODULE REGISTERS (continued)
* +--------+-----------------------+----------------------+----------------+
* | Offset | Acronym | Register Description | Section |
* +--------+-----------------------+----------------------+----------------+
* | 9B0h | conf_xdma_event_intr0 | | Section 9.3.51 |
* +--------+-----------------------+----------------------+----------------+
*/
#define CONFIG_XDMA_EVENT_INTR0_offset 0x9B0
/**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* Table 9-10. CONTROL_MODULE REGISTERS (continued)
* +--------+-----------------------+----------------------+----------------+
* | Offset | Acronym | Register Description | Section |
* +--------+-----------------------+----------------------+----------------+
* | 868h | conf_gpmc_a10 | | Section 9.3.51 |
* +--------+-----------------------+----------------------+----------------+
*/
#define CONFIG_GPMC_A10_offset 0x868
/**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* Table 9-10. CONTROL_MODULE REGISTERS (continued)
* +--------+-----------------------+----------------------+----------------+
* | Offset | Acronym | Register Description | Section |
* +--------+-----------------------+----------------------+----------------+
* | 86Ch | conf_gpmc_a11 | | Section 9.3.51 |
* +--------+-----------------------+----------------------+----------------+
*/
#define CONFIG_GPMC_A11_offset 0x86C
/**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* Table 9-10. CONTROL_MODULE REGISTERS (continued)
* +--------+-----------------------+----------------------+----------------+
* | Offset | Acronym | Register Description | Section |
* +--------+-----------------------+----------------------+----------------+
* | 994h | conf_mcasp0_fsx | | Section 9.3.51 |
* +--------+-----------------------+----------------------+----------------+
*/
#define CONFIG_MACSP0_FSX_offset 0x994 #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio)) #define GET_STRUCT_ADDR (ptr,type,member)\
((unsigned long )ptr - (unsigned long)((type*)->member)) /**
* AM335X_INTR0 --> [XDMA_EVENT_INTR0//TIMER4/CLKOUT1/SPI1_CS1/PR1_PRU1_PRU_R31_16/EMU2/GPIO0_19]
*/
#define KNOD_IRQ GPIO_TO_PIN(0,19)
/**
* AM335x_BOTT_INT --> [MCASP0_FSX/EHRPWM0B//SPI1_D0/MMC1_SDCD/PR1_PRU0_PRU_R30_1/PR1_PRU0_PRU_R31_1/GPIO3_15]
*/
#define KNOD_IRQ_BUTTON GPIO_TO_PIN(3,15)
/**
* reference: AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual
* 9.3.51 conf_<module>_<pin> Register (offset = 800h–A34h)
* Table 9-61. conf_<module>_<pin> Register Field Descriptions
* +-------+-------------------------+------------+--------------------------------------------+-
* | Bit | Field | Type Reset | Description |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 31-20 | Reserved | R 0h | |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 19-7 | Reserved | R 0h | |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 6 | conf_<module>_<pin>_sle | R/W X | Select between faster or slower slew rate |
* | | wctrl | | 0: Fast |
* | | | | 1: Slow |
* | | | | Reset value is pad-dependent. |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 5 | conf_<module>_<pin>_rx | R/W 1h | Input enable value for the PAD |
* | | active | | 0: Receiver disabled |
* | | | | 1: Receiver enabled |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 4 | conf_<module>_<pin>_pu | R/W X | Pad pullup/pulldown type selection |
* | | typesel | | 0: Pulldown selected |
* | | | | 1: Pullup selected |
* | | | | Reset value is pad-dependent. |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 3 | conf_<module>_<pin>_pu | R/W X | Pad pullup/pulldown enable |
* | | den | | 0: Pullup/pulldown enabled |
* | | | | 1: Pullup/pulldown disabled |
* | | | | Reset value is pad-dependent. |
* +-------+-------------------------+------------+--------------------------------------------+-
* | 2-0 | conf_<module>_<pin>_m | R/W X | Pad functional signal mux select. |
* | | | | mode Reset value is pad-dependent. |
* +-------+-------------------------+------------+--------------------------------------------+-
*
* Table 9-2. Mode Selection
* +---------+------------------------+
* | MUXMODE | Selected Mode |
* +---------+------------------------+
* | 000b | Primary Mode = Mode 0 |
* +---------+------------------------+
* | 001b | Mode 1 |
* +---------+------------------------+
* | 010b | Mode 2 |
* +---------+------------------------+
* | 011b | Mode 3 |
* +---------+------------------------+
* | 100b | Mode 4 |
* +---------+------------------------+
* | 101b | Mode 5 |
* +---------+------------------------+
* | 110b | Mode 6 |
* +---------+------------------------+
* | 111b | Mode 7 |
* +---------+------------------------+
*
* 0x37 = 0b0011 0111
*/
#define PINMUX_KNOD_IRQ 0X37
/**
* AM335X_IO_ROTARYA --> [GPMC_A10/GMII2_RXD1/RGMII2_RD1/RMII2_RXD1/GPMC_A26/PR1_MII1_RXDV/MCASP0_AXR0/GPIO1_26]
*/
#define KNOD_ROTARYA GPIO_TO_PIN(1,26)
#define PINMUX_KNOD_ROTARYA 0x37
/**
* AM335X_IO_ROTARYB --> [GPMC_A11/GMII2_RXD0/RGMII2_RD0/RMII2_RXD0/GPMC_A27/PR1_MII1_RXER/MCASP0_AXR1/GPIO1_27]
*/
#define KNOD_ROTARYB GPIO_TO_PIN(1,27)
#define PINMUX_KNOD_ROTARYB 0x37 #define KNOB_LEFT 0X01
#define KNOB_RITH 0X02
#define KNOB_UPDATA 0X10
#define KNOB_DEBOUNCED_TIMERS 10 /**
* completion是一种轻量级的机制,它允许一个线程告诉另一个线程工作已经完成。
*/
DECLARE_COMPLETION(knob_completion); /**
* 用于表示输入设备数据结构
*/
struct input_dev * knob_input;
int knob_value;
int irq_num_knob_in;
int irq_num_knob_in_button;
int knob_value_l;
int knob_value_r;
int knob_value_read_count; /**
* 全局变量jiffies用来记录自系统启动以来产生的节拍的总数。
* 启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。
* 一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是Hz。
*/
static unsigned long knob_new_jiffies;
static unsigned long knob_old_jiffies; static irqreturn_t knob_botton_irq_handle(int irq ,void *dev_id)
{
int tmp;
tmp = gpio_get_value(KNOD_IRQ_BUTTON);
printk("botton = %d\n ",tmp);
if (tmp)
{
// 按下去了
input_report_key(knob_input,KEY_OK,);
input_sync(knob_input);
}
else
{
// 抬起来了
input_report_key(knob_input,KEY_OK,);
input_sync(knob_input);
}
return IRQ_NONE;
} static irqreturn_t knob_irq_handle(int irq ,void *dev_id)
{
int newa, newb ,i;
static int timer_flag = ,olda = , oldb = ,oolda=,ooldb=; knob_new_jiffies = jiffies; /**
* 这里貌似永远也进不了判断。
* 貌似是为了处理抖动的问题
*/
timer_flag = ;
if (timer_flag) {
if (knob_new_jiffies - knob_old_jiffies < ) {
goto RE;
} else {
timer_flag = ;
}
} /**
* 获取旋转值对应的gpio的值
*/
newa = gpio_get_value(KNOD_ROTARYA);
newb = gpio_get_value(KNOD_ROTARYB); if ((newa == olda) && (newb == oldb)) {
goto RE;
} else {
if ((newa == newb) && (newa == ) && (oolda == ooldb) && (oolda == )) {
if ((olda == ) && (oldb == ))
knob_value = KNOB_LEFT | KNOB_UPDATA ;
if ((olda == ) && (oldb == ))
knob_value = KNOB_RITH | KNOB_UPDATA ;
} oolda = olda;
ooldb = oldb; olda = newa;
oldb = newb;
} if (knob_value & KNOB_RITH ) {
knob_value_l = ;
knob_value_r = ;
/*
if (knob_value_read_count)
complete(&knob_completion);
knob_value_read_count = 0;
*/
input_report_key(knob_input,KEY_RIGHT,);
input_sync(knob_input);
input_report_key(knob_input,KEY_RIGHT,);
input_sync(knob_input);
}
if (knob_value & KNOB_LEFT ) {
knob_value_l = ;
knob_value_r = ;
/*
if (knob_value_read_count)
complete(&knob_completion);
knob_value_read_count = 0;
*/
input_report_key(knob_input,KEY_LEFT,);
input_sync(knob_input);
input_report_key(knob_input,KEY_LEFT,);
input_sync(knob_input);
}
knob_old_jiffies = jiffies; knob_value = ;
return IRQ_NONE; RE:
knob_value = ;
return IRQ_NONE;
} static int knob_open(struct input_dev *dev)
{
return ;
}
static int knob_close(struct input_dev *dev)
{
return ;
}
static int knob_init()
{
int err;
/* Allocating GPIOs and setting direction */ /**
* Table 2-1. L3 Memory Map
* +-------------------+---------------------+-------------------+-------+---------------------------+
* | Block Name | Start_address (hex) | End_address (hex) | Size | Description |
* +-------------------+---------------------+-------------------+-------+---------------------------+
* | GPMC | 0x0000_0000(1) | 0x1FFF_FFFF | 512MB | 8-/16-bit External Memory |
* | (External Memory) | | | | (Ex/R/W) |
* +-------------------+---------------------+-------------------+-------+---------------------------+
*/
void __iomem * base = ioremap(Control_Module_address , 0x1FFF);
__raw_writel(PINMUX_KNOD_IRQ ,(base + CONFIG_XDMA_EVENT_INTR0_offset ));
__raw_writel(PINMUX_KNOD_ROTARYA ,(base + CONFIG_GPMC_A10_offset ));
__raw_writel(PINMUX_KNOD_ROTARYB ,(base + CONFIG_GPMC_A11_offset ));
// pad Pulldown selected
__raw_writel(0x27, (base + CONFIG_MACSP0_FSX_offset)); /**
* #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
* 通过这里,我们也就知道内核中的GPIO_TO_PIN是如何工作的了
* 这里需要注意的就是,首先要配置GPIO的硬件选项,gpio_request属于内核软件层的内容
* 当然,下面并没有做当出现错误的时候,需要释放前面已经申请了gpio口
*/
err = gpio_request(KNOD_IRQ_BUTTON,"knob_irq_button");
if ( err ) {
pr_err("failed to request gpio knob_irq\n");
// return err;
}
err = gpio_request(KNOD_IRQ,"knob_irq");
if ( err ) {
pr_err("failed to request gpio knob_irq\n");
// return err;
}
err = gpio_request(KNOD_ROTARYA,"knob_rotarya");
if ( err ) {
pr_err("failed to request gpio knob_rotarya\n");
// return err;
}
err = gpio_request(KNOD_ROTARYB,"knob_rotarab");
if ( err ) {
pr_err("failed to request gpio knob_rotarab\n");
// return err;
} err = gpio_direction_input(KNOD_IRQ);
if ( err ) {
pr_err("failed to set knob_irq ro input module.\n");
return err;
}
err = gpio_direction_input(KNOD_IRQ_BUTTON);
if ( err ) {
pr_err("failed to set knob_irq ro input module.\n");
return err;
}
err = gpio_direction_input(KNOD_ROTARYA);
if ( err ) {
pr_err("failed to set knob_rotarya ro input module.\n");
return err;
}
err = gpio_direction_input(KNOD_ROTARYB);
if ( err ) {
pr_err("failed to set knob_rotaryb ro input module.\n");
return err;
} /**
* input_allocate_device()函数在内存中为输入设备结构体分配一个空间,并对其主要的成员进行了初始化.
*/
knob_input = input_allocate_device();
if (knob_input == NULL)
return -ENOMEM; /**
* set_bit()告诉input输入子系统支持哪些事件,哪些按键。
* EV_KEY: 设定evbit支持EV_KEY
* KEY_LEFT: 支持向左按键
* KEY_RIGHT: 支持向右按键
* KEY_OK: 支持OK按键,目前不知道OK代表那个键
*/
set_bit( EV_KEY, knob_input->evbit);
set_bit( KEY_LEFT, knob_input->keybit);
set_bit( KEY_RIGHT, knob_input->keybit);
set_bit( KEY_OK , knob_input->keybit); /**
* cat cat /proc/bus/input/devices
* ......
* I: Bus=0000 Vendor=0000 Product=0000 Version=0000
* N: Name="input_knob"
* P: Phys=
* S: Sysfs=/devices/virtual/input/input_knob_t
* U: Uniq=
* H: Handlers=kbd event1
* B: PROP=0
* B: EV=3
* B: KEY=1 0 0 0 0 0 0 0 1680 0 0 0
*/
knob_input->name = "input_knob";
knob_input->dev.init_name = "input_knob_t";
knob_input->open = knob_open;
knob_input->close = knob_close; /**
* 注册为输入设备
*/
err = input_register_device(knob_input);
if (err)
return err; // 申请gpio口对应的中断
irq_num_knob_in = gpio_to_irq(KNOD_IRQ);
irq_num_knob_in_button = gpio_to_irq(KNOD_IRQ_BUTTON); /**
* 1. 在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:
* int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
* 1. irq是要申请的硬件中断号。
* 2. handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
* 3. irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
* 4. devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
* 5. dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
* 6. request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
* 2. 这里的dev_id参数是&irq_num_knob_in,在knob_irq_handle()中并没有用到,这里应该可以传NULL
*/
//IRQF_TRIGGER_FALLING
err = request_irq(irq_num_knob_in, &knob_irq_handle, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "IRQ_KNOB",&irq_num_knob_in );
if ( err ) {
pr_err(" cannot register irq %d .\n", irq_num_knob_in);
return err;
} err = request_irq(irq_num_knob_in_button, &knob_botton_irq_handle, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,"IRQ_KNOB_BOTTON",&irq_num_knob_in_button);
if ( err ) {
pr_err(" cannot register irq %d .\n",irq_num_knob_in_button);
return err;
} knob_value_read_count = ; printk("knob init success\n"); return ;
} static int __init knobm_init()
{
knob_init();
return ;
}
static void __exit knobm_exit()
{
/**
* 这里仅仅释放了irq_num_knob_in中断,并没有释放irq_num_knob_in_button中断,
* 具体原因未知。
*/
free_irq(irq_num_knob_in,&irq_num_knob_in);
/**
* 注销input设备
*/
input_unregister_device(knob_input);
}