buzzer_drv.c
/*
* linux-3.10.27
* arm-linux-gcc-4.5.1
*
* @ buzzer driver (gpio func)
*/
#include <linux/module.h>
#include <linux/init.h> /* module_init, ... */
#include <linux/kernel.h> /* everything */
#include <linux/cdev.h> /* cdev_init, ... */
#include <linux/fs.h> /* file_operations, */
#include <linux/device.h> /* class_create,... */
#include <linux/platform_device.h>
#include <linux/slab.h> /* kmalloc, ... */
#include <asm/io.h> /* ioremap,... */
#include <linux/uaccess.h> /* copy_from_user, ... */
#include <linux/gpio.h>
#include <linux/timer.h>
/*
* GPD0_0 : buzzer (XpwmTOUT[0] GPD0[0] TOUT_0) high->on
* GPD0_1 : lcd-blacklight (XpwmTOUT[1] GPD0[1] TOUT_1)
*/
#define DEF_DEVICE_NAME "myBuzzer"
#define DEF_MAJOR 0
struct priv_data
{
char *name;
int major;
dev_t dev;
struct cdev *cdev;
struct class *cls;
};
static struct priv_data *priv_data;
#define BUZZER_OFF 0
#define BUZZER_ON 1
struct buzzer_dev
{
int gpio;
char *name;
int state; /* 1 : on, 0 : off , else fliker */
void (* power_on)(struct buzzer_dev *dev);
void (* power_off)(struct buzzer_dev *dev);
void (* last)(struct buzzer_dev *dev, int nms);
};
static void smart210_buzzer_poweron(struct buzzer_dev *dev)
{
gpio_set_value(dev->gpio, BUZZER_ON);
dev->state = 1;
}
static void smart210_buzzer_poweroff(struct buzzer_dev *dev)
{
gpio_set_value(dev->gpio, BUZZER_OFF);
dev->state = 0;
}
static void buzzer_timer_callback(unsigned long data)
{
pr_info("%s called.\n", __func__);
struct buzzer_dev *dev = (struct buzzer_dev *)data;
pr_info("dev : %s .\n", dev->name);
gpio_set_value(dev->gpio, BUZZER_OFF);
}
static struct timer_list buzzer_timer;
static void smart210_buzzer_last(struct buzzer_dev *dev, int nms)
{
gpio_set_value(dev->gpio, BUZZER_ON);
mod_timer(&buzzer_timer, jiffies + nms * HZ / 1000);
}
static struct buzzer_dev buzzers[] = {
{S5PV210_GPD0(0), "mybuzzer", BUZZER_OFF, smart210_buzzer_poweron,
smart210_buzzer_poweroff, smart210_buzzer_last},
};
#if 0
static ssize_t buzzer_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return 0;
}
static ssize_t buzzer_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
return 0;
}
static DEVICE_ATTR(buzzer, 0644, buzzer_show, buzzer_store);
#endif
static int smart210_buzzer_open (struct inode *inode, struct file *pfile)
{
pr_info("%s called.\n", __func__);
return 0;
}
static int smart210_buzzer_close (struct inode *inode, struct file *pfile)
{
int i = 0;
pr_info("%s called.\n", __func__);
//for(i = 0; i < ARRAY_SIZE(buzzers);i++){
// gpio_set_value(buzzers[i].gpio, BUZZER_OFF);
//}
return 0;
}
static ssize_t smart210_buzzer_write (struct file *file,
const char __user *usrbuff, size_t len, loff_t *offset)
{
char recvbuf[8] = {0};
char cmd, opt, last, i;
if(copy_from_user(recvbuf, usrbuff, len)){
return -EINVAL;
}
for(i = 0; i < 8; i++){
printk(KERN_NOTICE "%d ", recvbuf[i]);
}
printk(KERN_NOTICE "\n");
cmd = recvbuf[0];
opt = recvbuf[1];
last = recvbuf[2];
printk(KERN_NOTICE "cmd : %d opt : %d last : %d \n", cmd, opt, last);
if(opt > 0)
return EINVAL;
switch(cmd){
case 0:
buzzers[opt].power_off(&buzzers[opt]);
break;
case 1:
buzzers[opt].power_on(&buzzers[opt]);
break;
default:
buzzers[opt].last(&buzzers[opt], recvbuf[2] * 1000);
break;
}
return 0;
}
const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = smart210_buzzer_open,
.release = smart210_buzzer_close,
.write = smart210_buzzer_write,
};
static int __init buzzer_drv_init(void)
{
int ret = 0, i = 0;
pr_info("%s called.\n", __func__);
priv_data = kmalloc(sizeof(struct priv_data), GFP_KERNEL);
if(!priv_data){
pr_err("no mem.\n");
return -ENOMEM;
}
priv_data->name = DEF_DEVICE_NAME;
priv_data->major = DEF_MAJOR;
if(priv_data->major){
priv_data->dev = MKDEV(priv_data->major, 0);
ret = register_chrdev_region(priv_data->dev, 1, priv_data->name);
}else{
ret = alloc_chrdev_region(&priv_data->dev, 1, 1, priv_data->name);
priv_data->major = MAJOR(priv_data->dev);
}
if(ret){
pr_err("chrdev register err.\n");
goto err0;
}
/* add cdev */
priv_data->cdev = cdev_alloc();
if(!priv_data->cdev){
pr_err("cdev alloc err.\n");
goto err1;
}
cdev_init(priv_data->cdev, &fops);
priv_data->cdev->owner = THIS_MODULE;
priv_data->cdev->ops = &fops;
cdev_add(priv_data->cdev, priv_data->dev, 1);
/* add class */
priv_data->cls = class_create(THIS_MODULE, priv_data->name);
if(!priv_data->cls){
pr_err("class create err.\n");
goto err2;
}
device_create(priv_data->cls, NULL, priv_data->dev, NULL, priv_data->name);
for(i = 0; i < ARRAY_SIZE(buzzers);i++){
ret = gpio_request(buzzers[i].gpio, buzzers[i].name);
if(ret){
pr_err("gpio request fail.\n");
goto err3;
}
gpio_direction_output(buzzers[i].gpio, buzzers[i].state);
gpio_set_value(buzzers[i].gpio, BUZZER_OFF);
}
setup_timer(&buzzer_timer, buzzer_timer_callback, (unsigned long)&buzzers[0]);
return 0;
err3:
device_destroy(priv_data->cls, priv_data->dev);
class_destroy(priv_data->cls);
err2:
cdev_del(priv_data->cdev);
err1:
unregister_chrdev_region(priv_data->dev, 1);
err0:
kfree(priv_data);
return -EINVAL;
}
static void __exit buzzer_drv_exit(void)
{
int i = 0;
pr_info("%s called.\n", __func__);
for(i = 0; i < ARRAY_SIZE(buzzers); i++){
gpio_free(buzzers[i].gpio);
}
device_destroy(priv_data->cls, priv_data->dev);
class_destroy(priv_data->cls);
cdev_del(priv_data->cdev);
unregister_chrdev_region(priv_data->dev, 1);
kfree(priv_data);
}
module_init(buzzer_drv_init);
module_exit(buzzer_drv_exit);
MODULE_LICENSE("GPL");