zoukankan      html  css  js  c++  java
  • OK335xS knob driver hacking

    /*************************************************************************
     *                     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
     ",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
    ");
        //    return err;
        }
        err = gpio_request(KNOD_IRQ,"knob_irq");
        if ( err ) {
            pr_err("failed to request gpio knob_irq
    ");
        //    return err;
        }
        err = gpio_request(KNOD_ROTARYA,"knob_rotarya");
        if ( err ) {
            pr_err("failed to request gpio knob_rotarya
    ");
        //    return err;
        }
        err = gpio_request(KNOD_ROTARYB,"knob_rotarab");
        if ( err ) {
            pr_err("failed to request gpio knob_rotarab
    ");
        //    return err;
        }
    
        err = gpio_direction_input(KNOD_IRQ);
        if ( err ) {
            pr_err("failed to set knob_irq ro input module.
    ");
            return err;
        }
        err = gpio_direction_input(KNOD_IRQ_BUTTON);
        if ( err ) {
            pr_err("failed to set knob_irq ro input module.
    ");
            return err;
        }
        err = gpio_direction_input(KNOD_ROTARYA);
        if ( err ) {
            pr_err("failed to set knob_rotarya ro input module.
    ");
            return err;
        }
        err = gpio_direction_input(KNOD_ROTARYB);
        if ( err ) {
            pr_err("failed to set knob_rotaryb ro input module.
    ");
            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 .
    ", 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 .
    ",irq_num_knob_in_button);
            return err;
        }
    
        knob_value_read_count = 1;
    
        printk("knob init success
    ");
    
        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);
    }
    
  • 相关阅读:
    Java8新特性
    为什么要使用ORM技术?和 JDBC 有何不一样?
    HTTP Status 500
    重装Oracle时出现SID已存在问题的解决办法
    数据库模式显示的Swing表格
    自然连接和等值连接
    雷林鹏分享:Java 循环结构
    雷林鹏分享:Java 运算符
    雷林鹏分享:Java 修饰符
    雷林鹏分享:Java 变量类型
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4974730.html
Copyright © 2011-2022 走看看