先说原理图
其中XpwmTOUT0接的GPD0_0
对于Makefile:
TARGET := beep
obj-m += $(TARGET).o
ROOTFS = /home/flinn/tmp/rootfs
KERNEL = /home/flinn/tiny4412-SDK/linux-4.19.27
all:
make -C $(KERNEL) M=`pwd` modules
clean:
make -C $(KERNEL) M=`pwd` clean
install:
#make -C $(KERNEL) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS)
sudo cp $(TARGET).ko $(ROOTFS)
beep.c
/*
* beep sdriver on linux-4.19.27(without dt)
* arm-linux-gcc-6.2.1
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/slab.h> /* kmalloc, */
#include <linux/cdev.h>
#include <linux/uaccess.h> /* copy_from_usr, */
#include <asm/io.h> /* ioremap, */
#define DEV_NAME "beep"
struct beep_dev
{
struct cdev *cdev;
dev_t dev;
int major;
char name[32];
struct class *class;
};
static struct beep_dev *beep_dev = NULL;
/*
* resource
* GPD0_0 for buzzer
*
* Note(s) : GPD0_1 as LCD
*/
struct _gpio
{
unsigned long con; /* configuration register */
unsigned long data; /* data register */
unsigned long pud; /* pull-up/ pull-down register */
unsigned long drv; /* drive strength control register */
unsigned long conpdn; /* power down mode configuration register */
unsigned long pudpdn; /* power down mode pull-up/ pull-down register 0x000 */
};
#define BEEP_GPIOD_CON_ADDR 0x114000A0
static struct _gpio *beep_gpio = NULL;
static int beep_open (struct inode *inode, struct file *file)
{
printk(KERN_INFO "open \n");
/* set GPMD_0 as output */
beep_gpio->con |= (1 << 0);
return 0;
}
static int beep_close (struct inode *inode, struct file *file)
{
printk(KERN_INFO "close \n");
return 0;
}
static ssize_t beep_write (struct file *file, const char __user *usrbuf, size_t len, loff_t *off)
{
char cmd,opt;
char rev_buf[8] = {0};
printk(KERN_INFO "write \n");
if(copy_from_user(rev_buf,usrbuf,8))
return -EFAULT;
cmd = rev_buf[0];
opt = rev_buf[1];
printk(KERN_NOTICE "cmd : %d opt : %d \n", cmd, opt);
if(cmd == 0)
{
// off
beep_gpio->data &= ~(1<<0);
}
else if(cmd == 1)
{
// on
beep_gpio->data |= (1<<0);
}
else
{
// do nothing.
}
return 0;
}
static long beep_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_INFO "ioctl \n");
return 0;
}
const struct file_operations beep_ops =
{
.owner = THIS_MODULE,
.open = beep_open,
.release = beep_close,
.unlocked_ioctl = beep_ioctl,
.write = beep_write,
};
static int beep_drv_init(void)
{
int ret = 0;
printk(KERN_INFO "beep drv init.\n");
beep_dev = kmalloc(sizeof(struct beep_dev),GFP_KERNEL);
if(!beep_dev)
return -ENOMEM;
strcpy(beep_dev->name, DEV_NAME);
ret = alloc_chrdev_region(&beep_dev->dev, 0, 1, beep_dev->name);
beep_dev->major = MAJOR(beep_dev->dev);
if(ret < 0)
{
kfree(beep_dev);
return ret;
}
beep_dev->cdev = cdev_alloc();
if(!beep_dev->cdev)
{
unregister_chrdev_region(beep_dev->dev,1);
kfree(beep_dev);
return -EFAULT;
}
cdev_init(beep_dev->cdev,&beep_ops);
beep_dev->cdev->owner = THIS_MODULE;
beep_dev->cdev->ops = &beep_ops;
cdev_add(beep_dev->cdev,beep_dev->dev,1);
beep_dev->class = class_create(THIS_MODULE,beep_dev->name);
ret = PTR_ERR(beep_dev->class);
if (IS_ERR(beep_dev->class))
{
cdev_del(beep_dev->cdev);
unregister_chrdev_region(beep_dev->dev,1);
kfree(beep_dev);
return -EFAULT;
}
device_create(beep_dev->class,NULL,beep_dev->dev,NULL,beep_dev->name,beep_dev);
beep_gpio = (struct _gpio *)ioremap(BEEP_GPIOD_CON_ADDR, 0x20);
return 0;
}
static void beep_drv_exit(void)
{
printk(KERN_INFO "beep drv exit.\n");
iounmap(beep_gpio);
device_destroy(beep_dev->class, beep_dev->dev);
class_destroy(beep_dev->class);
cdev_del(beep_dev->cdev);
unregister_chrdev_region(beep_dev->dev,1);
kfree(beep_dev);
}
module_init(beep_drv_init);
module_exit(beep_drv_exit);
MODULE_LICENSE("GPL");