Linux駆動のplatformメカニズムを一歩一歩学習する(tiny 210キー駆動)
25525 ワード
1、platform device
2、platform driver
3、Makefile
4、試験手順
/** * @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;
}