#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <asm/io.h>
#define LED_DEVICE_MAJOR 230
#define LED_DEVICE_MINOR 0
#define LED_ON_CMD 0x11
#define LED_OFF_CMD 0x22
#define LED_REGPHY_ADDR 0x11000c40
#define LED_REGPHY_SIZE 8
#define LED_CON_OFFSET 0
#define LED_DAT_OFFSET 4
MODULE_LICENSE("GPL v2");
struct led_device{
dev_t devid;
void __iomem *regaddr;//__iomem增加代码可读性,寄存器地址
struct cdev led_cdev;
};
struct led_device* pled;
//CON是控制工作模式
void led_init(void)
{
//gpio output mode
int regval =readl(pled->regaddr + LED_CON_OFFSET);
regval = regval & ~(0xf << 28);
regval = regval | (0x1 << 28);
writel(regval,pled->regaddr + LED_CON_OFFSET);
return;
}
//DAT控制高低电平
void led_on(void)
{
int regval =readl(pled->regaddr + LED_DAT_OFFSET);
regval = regval & ~(0x1 << 7);
regval = regval | (0x1 << 7);
writel(regval,pled->regaddr + LED_DAT_OFFSET);
return;
}
void led_off(void)
{
int regval =readl(pled->regaddr + LED_DAT_OFFSET);
regval = regval & (0x0 << 7);
writel(regval,pled->regaddr + LED_DAT_OFFSET);
return;
}
static int led_device_open(struct inode *inode, struct file *file)
{
printk("led device_open\n");
//os不能直接使用物理地址,需要映射虚拟地址
pled->regaddr = ioremap(LED_REGPHY_ADDR, LED_REGPHY_SIZE);
if(!pled->regaddr){
printk("Fail to ioremap\n");
return -ENOMEM;
}
printk("reg addr:%p\n",pled->regaddr);
led_init();
return 0;
}
static int led_device_close(struct inode *inode, struct file *file)
{
printk("led device_close\n");
iounmap(pled->regaddr);
return 0;
}
static long led_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case LED_ON_CMD:
printk("led cmd on\n");
led_on();
break;
case LED_OFF_CMD:
printk("led cmd off\n");
led_off();
break;
}
return 0;
}
//led_device_ops提供操作硬件接口
static const struct file_operations led_device_ops = {
.owner = THIS_MODULE,
.open = led_device_open,
.release = led_device_close,
.unlocked_ioctl = led_device_ioctl,
};
static int led_driver_init(void)
{
int err;
printk("led driver init\n");
pled = kmalloc(sizeof(*pled), GFP_KERNEL);
if(!pled){
printk("fail to create dev\n");
return -ENOMEM;
}
//pled->led_cdev.ops = &led_device_ops;
//设备结构体初始化
cdev_init(&pled->led_cdev,&led_device_ops);
//注册设备号,MKDEV只是一个宏为了组合设备号
pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
err = register_chrdev_region(pled->devid, 1, "led-device");
if(err){
printk("Fail to register_chrdev_region\n");
kfree(pled);
return err;
}
//字符设备添加(是为了将设备和设备号绑定在一起)
err = cdev_add(&pled->led_cdev, pled->devid, 1);
if(err){
printk("Fail to cdev_add\n");
unregister_chrdev_region(pled->devid, 1);
kfree(pled);
}
return 0;
}
void led_driver_exit(void)
{
printk("led driver exit\n");
cdev_del(&pled->led_cdev);
unregister_chrdev_region(pled->devid, 1);
kfree(pled);
return;
}
module_init(led_driver_init);
module_exit(led_driver_exit);