Linux駆動のplatformメカニズムを一歩一歩学習する(tiny 210キー駆動)


1、platform device
/** * @Author: ZP1015 * * @Copyright:SCUT. * * @Function:Platform device driver for button * * @Creat:2015-06-10 * * @Modify:2015-12-29 **/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <linux/gpio_keys.h>
/** gpio_keys_button gpio_keys_platform_data     **/

static struct gpio_keys_button mini210_button[] = {
    [0] = {
            .gpio   =   S5PV210_GPH2(0),
            .type   =   0,
            .desc   =   "KEY0",
    },
    [1] = {
            .gpio   =   S5PV210_GPH2(1),
            .type   =   1,
            .desc   =   "KEY1",
    },
    [2] = {
            .gpio   =   S5PV210_GPH2(2),
            .type   =   2,
            .desc   =   "KEY2",
    },
    [3] = {
            .gpio   =   S5PV210_GPH2(3),
            .type   =   3,
            .desc   =   "KEY3",
    },
    [4] = {
            .gpio   =   S5PV210_GPH3(0),
            .type   =   4,
            .desc   =   "KEY4",
    },
    [5] = {
            .gpio   =   S5PV210_GPH3(1),
            .type   =   5,
            .desc   =   "KEY5",
    },
    [6] = {
            .gpio   =   S5PV210_GPH3(2),
            .type   =   6,
            .desc   =   "KEY6",
    },
    [7] = {
            .gpio   =   S5PV210_GPH3(3),
            .type   =   7,
            .desc   =   "KEY7",
    }

};

static struct gpio_keys_platform_data mini210_button_data = {
    .buttons = mini210_button,
    .nbuttons = ARRAY_SIZE(mini210_button),
};

static void button_platform_device_release(struct device * dev)
{
    return ;
}

static struct platform_device button_platform_device = {
    .name           = "button_platform_device_driver",
    .id             = -1,
    .dev            = {
          .release  = button_platform_device_release,
          .platform_data = &mini210_button_data,      
    }
};
static int __init button_platform_device_init(void)
{
    printk("button_platform_device add ok!
"
); return platform_device_register(&button_platform_device); } static void __exit button_platform_device_exit(void) { printk("button_platform_device remove ok!
"
); platform_device_unregister(&button_platform_device); } MODULE_AUTHOR("ZP1015"); MODULE_LICENSE("GPL"); module_init(button_platform_device_init); module_exit(button_platform_device_exit);

2、platform driver
/** * @Author: ZP1015 * * @Copyright:SCUT. * * @Function:platform driver for button * * @Creat:2015-6-12 * * @Modify:2015-12-29 **/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <linux/gpio_keys.h>

/*          */
struct buttons_status_copy_to_user {
    int keynum;/*    */
    int buttons_cur_press;/*         */
};

static volatile int key_values[] = {
    0, 0, 0, 0, 0, 0, 0, 0
};

#define DEVICE_NAME "mybuttons_platfor_driver"

#define GLOBAL_LED_MAJOR 250

static unsigned int global_led_major = GLOBAL_LED_MAJOR; 
static struct cdev *button_cdev = NULL; 
static struct class *buttons_class = NULL;

struct buttons_status_copy_to_user buttons_status_statistics;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;

static void mini210_buttons_status(struct gpio_keys_button *_data)
{
    struct gpio_keys_button *button_data = (struct gpio_keys_button *)_data;

    /*          */
    printk("KEY %d: %d
"
, button_data->type,key_values[button_data->type]); /*TO DO:For IRQ_TYPE_EDGE_FALLING*/ key_values[button_data->type] = key_values[button_data->type] + 1; buttons_status_statistics.buttons_cur_press = key_values[button_data->type]; buttons_status_statistics.keynum = button_data->type; ev_press = 1; wake_up_interruptible(&button_waitq); } static irqreturn_t button_interrupt(int irq, void *dev_id) { struct gpio_keys_button *button_data = (struct gpio_keys_button *)dev_id; mini210_buttons_status(button_data); return IRQ_HANDLED; } static int mini210_buttons_open(struct inode *inode, struct file *file) { return 0; } static int mini210_buttons_close(struct inode *inode, struct file *file) { return 0; } /*mini210_buttons_read() */ /*read */ /* */ /* */ /* */ /* , ev_press */ /* , buffer */ /* , , */ /* , */ /* */ static int mini210_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; /* ev_press */ /* , */ if (!ev_press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; /* , button_waitq */ /* , ev_press */ /* poll_wait */ /* select */ else wait_event_interruptible(button_waitq, ev_press); /* , */ /* ,ev_press 1 */ /* */ } ev_press = 0; /*TO DO:For IRQ_TYPE_EDGE_FALLING*/ #if 1 err = copy_to_user((struct buttons_status_copy_to_user *)buff, (struct buttons_status_copy_to_user *)(&buttons_status_statistics), min(sizeof(buttons_status_statistics), count)); /*TO DO:For IRQ_TYPE_EDGE_BOTH*/ #else err = copy_to_user((int*)buff, (const int *)(&key_values), min(sizeof(key_values), count)); memset((int *)key_values,0,sizeof(key_values)); #endif return err ? -EFAULT : min(sizeof(key_values), count); } static unsigned int mini210_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; /*poll_wait() */ /* button_waitq */ /* , mini210_button_read ev_press 1 */ /* */ /* select */ poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static struct file_operations button_fops = { .owner = THIS_MODULE, .open = mini210_buttons_open, .release = mini210_buttons_close, .read = mini210_buttons_read, .poll = mini210_buttons_poll, }; static int __devinit button_probe(struct platform_device *pdev) { int ret; int err,irq; dev_t devno; int i = 0; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; printk("button probe start!
"
); /*1. cdev*/ button_cdev = cdev_alloc();// cdev cdev_init(button_cdev,&button_fops);// cdev button_cdev->owner = THIS_MODULE; /*2. */ devno = MKDEV(global_led_major,0);// dev_t if (devno) { ret = register_chrdev_region(devno,1,DEVICE_NAME);// } else { ret = alloc_chrdev_region(&devno,0,1,DEVICE_NAME);// global_led_major = MAJOR(devno); } if (ret < 0) { return ret; } /*3. */ err = cdev_add(button_cdev,devno,1);// cdev if (err) { printk(KERN_NOTICE"Error %d adding button_cdev",err); goto cdev_add_fail; } printk(KERN_NOTICE"button_platform_driver init ok!
"
); /*4. , /sys/class/ */ buttons_class = class_create(THIS_MODULE,"mybuttons_class");// device_create(buttons_class,NULL,devno,NULL,"mybuttons"); /*5. */ for (i = 0; i < pdata->nbuttons; i++) { if (!pdata->buttons[i].gpio) continue; /* */ irq = gpio_to_irq(pdata->buttons[i].gpio); /* */ err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, pdata->buttons[i].desc, (void *)&(pdata->buttons[i])); if (err) goto request_irq_fail; } ev_press = 1; return 0; request_irq_fail: cdev_del(button_cdev); unregister_chrdev_region(devno,1); device_destroy(buttons_class,devno); class_destroy(buttons_class); i--; /* */ for (; i >= 0; i--) { if (!pdata->buttons[i].gpio) continue; irq = gpio_to_irq(pdata->buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&(pdata->buttons[i])); } return -EBUSY; cdev_add_fail: unregister_chrdev_region(devno,1); return -1; } static int __devexit button_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = NULL; dev_t devno; int i,irq= 0; devno = MKDEV(global_led_major,0);// dev_t printk("button_remove!
"
); cdev_del(button_cdev); unregister_chrdev_region(devno,1); device_destroy(buttons_class,devno); class_destroy(buttons_class); if(!pdev) return 0; pdata = pdev->dev.platform_data; for (i=0; i < pdata->nbuttons; i++) { if (!pdata->buttons[i].gpio) continue; irq = gpio_to_irq(pdata->buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&(pdata->buttons[i])); } return 0; } static struct platform_driver button_platform_driver = { .probe = button_probe, .remove = __devexit_p(button_remove), .driver = { .name = "button_platform_device_driver", .owner = THIS_MODULE, } }; static int __init button_platform_driver_init(void) { printk("button_platform_driver_init!
"
); return platform_driver_register(&button_platform_driver); } static void __exit button_platform_driver_exit(void) { printk("button_platform_driver_exit!
"
); platform_driver_unregister(&button_platform_driver); } module_init(button_platform_driver_init); module_exit(button_platform_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZP1015");

3、Makefile
ifneq ($(KERNELRELEASE),)

obj-m :=button_platform.o

else

KERNELDIR :=/home/ZP1015/Desktop/linux
ARM_LINUX_DIR =-I $(KERNELDIR)/arch/arm/mach-s5pv210/include/mach/  \
               -I $(KERNELDIR)/arch/arm/include/asm

all:
     make -C $(KERNELDIR) $(ARM_LINUX_DIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
    rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~

endif

4、試験手順
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct buttons_status_copy_to_user {
    int keynum;
    int buttons_cur_press;
};

int main()
{
    int fd,ret;
    struct buttons_status_copy_to_user buttons_status_statistics;
    fd = open("/dev/mybuttons", 0);
    if(fd < 0) {
        printf("open error!");
        return -1;
    }
    while(1) {
        ret = read(fd, &buttons_status_statistics,\
            sizeof(struct buttons_status_copy_to_user));
        printf("you press the key %d ,%d
"
, buttons_status_statistics.keynum+1,\ buttons_status_statistics.buttons_cur_press); } close(fd); return 0; }