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\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); }