原理图
应用层程序
#include<stdlib.h>
#include<sys/ioctl.h>
#include "led.h"
int main()
{
int fd;
int i = 1;
fd = open("/dev/led",O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
//需要将用户空间的数据i传送到内核空间,这就存在数据交换
ioctl(fd,LED_ON,&i); //i代表第几盏灯
usleep(5000*100);
ioctl(fd,LED_OFF,&i);
usleep(fd,LED_OFF,&i);
usleep(5000*100);
if(++i == 5)
{
i = 1;
}
}
return 0;
}
#ifndef s5pv210_LED_HH
#define s5pv210_LED_HH
#define LED_MAGIC 'L'
//参数:魔数,序列号,交换数据的大小
#define LED_ON _IOW(LED_MAGIC,0,int)
#define LED_ON _IOW(LED_MAGIC,1,int)
#endif
驱动程序
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include"led.h"
MODULE_LICENSE("Dual BSD/GPL");
//把4个LED作为一个设备
#define LED_MA 500
#define LED_MI 0
#define LED_NUM 1
//LED相关寄存器地址
#define GPF3CON 0x114001E0
#define GPF3DAT 0x114001E4
#define GPX1CON 0x11000C20
#define GPX1DAT 0x11000C24
#define GPFXCON 0x11000C40
#define GPFXDAT 0x11000C44
//内核中使用的是虚拟地址,需要用ioremap将物理地址映射为虚拟地址
//这些指针就是用来保存映射后的虚拟地址
static unsigned int *gpf3con;
static unsigned int *gpf3dat;
static unsigned int *gpx1con
static unsigned int *gpx1dat
static unsigned int *gpx2con
static unsigned int *gpx2dat
//建立映射关系
int fs4412_led_ioremap(void)
{
int ret;
gpf3con = ioremap(FS4412_GPF3CON,4);
if(gpf3con == NULL)
{
printk("ioremap gpf3con\n");
ret = -ENOMEM;
return ret;
}
gpf3dat = ioremap(FS4412_GPF3DAT,4);
if(gpf3dat == NULL)
{
printk("ioremap gpx2dat\n");
ret = -ENOMEM;
return ret;
}
gpfx1con = ioremap(FS4412_GPX1CON,4);
if(gpx1con == NULL)
{
printk("ioremap gpx1con\n");
ret = -ENOMEM;
return ret;
}
gpfx1dat = ioremap(FS4412_GPX1DAT,4);
if(gpx1dat == NULL)
{
printk("ioremap gpx1dat\n");
ret = -ENOMEM;
return ret;
}
gpfx2con = ioremap(FS4412_GPX2CON,4);
if(gpx2con == NULL)
{
printk("ioremap gpx2con\n");
ret = -ENOMEM;
return ret;
}
gpfx2dat = ioremap(FS4412_GPX2DAT,4);
if(gpx2dat == NULL)
{
printk("ioremap gpx2dat\n");
ret = -ENOMEM;
return ret;
}
}
//解除映射,最后映射的,最先解除
void fs4412_led_iounmap(void)
{
iounmap(gpf3con);
iounmap(gpf3dat);
iounmap(gpx1con);
iounmap(gpx1dat);
iounmap(gpx2con);
iounmap(gpx2dat);
}
//初始化,让led输出低电平
void fs4412_led_io_init(void)
{
writel((readl(gpf3con) & ~(0xff << 16) | (0x11 << 16),gpf3con));
writel(readl(gpf3dat) & ~(0x3 << 4),gpf3dat);
writel((readl(gpx1con) & ~(0xf << 0)) |(0x1 << 0),gpx1con);
writel(readl(gpx1dat) & ~(0x1 <<0),gpx1dat);
writel((readl(gpx2con) &~(0xf << 28)) | (0x1<<28),gpx2con);
writel(readl(gpx2dat)&~(0x1<<7),gpx2dat);
}
struct file_operations s5pv210_led_fops = {
.owner = THIS_MODULE,
.open = s5pv210_led_open,
.release = s5pv210_led_release,
.unlocked_ioctl = s5pv210_led_unlocked_ioctl,
};
//ioctl
static long s5pv210_led_unlocked_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
int nr;
//将用户层的arg放入nr中
if(copy_from_user((void*)&nr,(void *)arg,sizeof(nr)))
return -EFAULT;
if(nr < 1 || nr > 4)
return -EINVAL;
switch(cmd)
{
case LED_ON:
fs4412_led_on(nr);
break;
case LED_OFF:
fs4412_led_off(nr);
break;
default:
printk("Invalid argment");
return EINVAL;
}
}
//定义一个字符设备
struct cdev cdev;
//点亮led
void fs4412_led_on(int nr)
{
switch(nr)
case 1:
writel(readl(gpx2dat) | 1 << 7,gpx2dat)
break;
case 2:
writel(readl(gpx1dat) | 1 << 0,gpx1dat)
break;
case 3:
writel(readl(gpf3dat) | 1 << 4,gpf3dat)
break;
case 4:
writel(readl(gpf3dat) | 1 << 5,gpf3dat)
break;
}
//熄灭led
void fs4412_led_on(int nr)
{
switch(nr)
case 1:
writel(readl(gpx2dat) | & ~(1 << 7),gpx2dat)
break;
case 2:
writel(readl(gpx1dat) | & ~(1 << 0),gpx1dat)
break;
case 3:
writel(readl(gpf3dat) | &~(1 << 4),gpf3dat)
break;
case 4:
writel(readl(gpf3dat) | ~(1 << 5),gpf3dat)
break;
}
static int s5pv210_led_open(struct inode*inode,struct file *file)
{
return 0;
}
static int s5pv210_led_release(struct inode*inode,struct file*file)
{
return 0;
}
//加载函数
static int s5pv210_led_init(void)
{
dev_t devno = MKDEV(LED_MA,LED_MI);
int ret;
ret = register_chrdev_region(devno,LED_NUM,"newled");
if(ret < 0)
{
printk("register error\n");
return ret;
}
cdev_init(&cdev,&s5pv210_led_fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev,devno,LED_NUM);
if(ret < 0)
{
printk("cdev_add");
goto err1;
}
//建立映射关系,物理地址-->虚拟地址
ret = fs4412_led_ioremap();
if(ret < 0)
{
goto err2;
}
//led初始化
fs4412_led_io_init();
printk("led init\n");
return 0;
err2:
cdev_del(&cdev);
err1:
unregister_chrdev_region(devno,LED_NUM);
return ret;
}
//字符设备卸载函数
static void s5pv210_led_exit(void)
{
dev_t devno = MKDEV(LED_MA,LED_MI);
//解除映射
fs4412_led_ioupmap();
cdev_del(&cdev);
unregister_chrdev_region(devno,LED_NUM);
printk("LED EXIT\n");
}
module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);