您的位置:首页 > 其它

OK335xS knob driver hacking

2015-11-18 15:35 302 查看
/*************************************************************************
*                     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*)0->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,1);
input_sync(knob_input);
}
else
{
// 抬起来了
input_report_key(knob_input,KEY_OK,0);
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 = 1,olda = 0, oldb = 0,oolda=0,ooldb=0;

knob_new_jiffies = jiffies;

/**
* 这里貌似永远也进不了判断。
* 貌似是为了处理抖动的问题
*/
timer_flag = 0;
if (timer_flag) {
if (knob_new_jiffies - knob_old_jiffies < 3) {
goto RE;
} else {
timer_flag = 0;
}
}

/**
* 获取旋转值对应的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 == 0) && (oolda == ooldb) && (oolda == 1)) {
if ((olda == 1) && (oldb == 0))
knob_value = KNOB_LEFT | KNOB_UPDATA    ;
if ((olda == 0) && (oldb == 1))
knob_value = KNOB_RITH | KNOB_UPDATA    ;
}

oolda = olda;
ooldb = oldb;

olda = newa;
oldb = newb;
}

if (knob_value & KNOB_RITH ) {
knob_value_l = 0;
knob_value_r = 1;
/*
if (knob_value_read_count)
complete(&knob_completion);
knob_value_read_count = 0;
*/
input_report_key(knob_input,KEY_RIGHT,1);
input_sync(knob_input);
input_report_key(knob_input,KEY_RIGHT,0);
input_sync(knob_input);
}
if (knob_value & KNOB_LEFT ) {
knob_value_l = 1;
knob_value_r = 0;
/*
if (knob_value_read_count)
complete(&knob_completion);
knob_value_read_count = 0;
*/
input_report_key(knob_input,KEY_LEFT,1);
input_sync(knob_input);
input_report_key(knob_input,KEY_LEFT,0);
input_sync(knob_input);
}
knob_old_jiffies = jiffies;

knob_value = 0;
return IRQ_NONE;

RE:
knob_value = 0;
return IRQ_NONE;
}

static int knob_open(struct input_dev *dev)
{
return 0;
}
static int knob_close(struct input_dev *dev)
{
return 0;
}
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 = 1;

printk("knob init success\n");

return 0;
}

static int __init  knobm_init()
{
knob_init();
return 0;
}
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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: