app代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd;
int on;
fd = open("/dev/led_plat",O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
on = 1;
write(fd, &on,sizeof(on));
sleep(1);
on = 0;
write(fd, &on,sizeof(on));
sleep(1);
close(fd);
return 0;
}
驱动层代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/fs.h>
#include "led_info.h"
struct s5pv210_led_dev{
int major;
struct class * clz;
struct device * dev;
void * reg_base;
struct led_platdata *pdata;
};
unsigned int reg_val;
struct s5pv210_led_dev * led_dev;
int led_pdrv_open(struct inode *node, struct file *filp)
{
int conf_reg_clear;
int conf_reg_data;
int shift;
printk("--------------%s---------------\n",__FUNCTION__);
conf_reg_clear = led_dev->pdata->conf_reg_clear;
conf_reg_data = led_dev->pdata->conf_reg_data;
shift = led_dev->pdata->shift;
reg_val = __raw_readl(led_dev->reg_base);
reg_val &= ~(conf_reg_clear << shift *4);
reg_val |= conf_reg_data << shift *4;
__raw_writel(reg_val,led_dev->reg_base);
return 0;
}
ssize_t led_pdrv_write(struct file *filp, const char __user *buff, size_t count, loff_t * offset)
{
int ret, value;
ret = copy_from_user(&value,buff,count);
if(ret > 0)
{
printk("copy_from_user error!\n");
return -EFAULT;
}
if(value)
{
reg_val = readl(led_dev->reg_base + 4);
reg_val |= 0x3 << 3;
writel(reg_val, led_dev->reg_base + 4);
}
else
{
reg_val = readl(led_dev->reg_base + 4);
reg_val &= ~(0x3 << 3);
writel(reg_val, led_dev->reg_base + 4);
}
return 0;
}
int led_pdrv_release(struct inode *node, struct file *filp)
{
writel(readl(led_dev->reg_base + 4) & ~(0x3 <<3),led_dev->reg_base + 4);
return 0;
}
const struct file_operations fops = {
.open = led_pdrv_open,
.write = led_pdrv_write,
.release = led_pdrv_release,
};
int led_pdrv_probe(struct platform_device *pdev)
{
struct resource * addr_res1, *addr_res2;
struct resource * irq_res;
int irqno,ret;
printk("---------------%s------------------\n",__FUNCTION__);
//1.实例化全局对象
led_dev = kzalloc(sizeof(struct s5pv210_led_dev),GFP_KERNEL);
if(IS_ERR(led_dev))
{
printk("kzalloc error!\n");
return -ENOMEM;
}
//2.申请设备号
led_dev->major = register_chrdev(0,"led_pdrv",&fops);
if(led_dev->major < 0)
{
printk("register_chrdev error!\n");
ret = led_dev->major;
goto err_kfree;
}
//3.创建类
led_dev->clz = class_create(THIS_MODULE,"led_class");
if(IS_ERR(led_dev->clz))
{
printk("class_create error!\n");
ret = PTR_ERR(led_dev->clz);
goto err_unregister;
}
//4.创建设备节点
led_dev->dev = device_create(led_dev->clz,NULL,MKDEV(led_dev->major,0),NULL,"led_plat");
if(IS_ERR(led_dev->dev))
{
printk("device_create error!\n");
ret = PTR_ERR(led_dev->dev);
goto err_class;
}
//参数1:pdev对象
//参数2:资源的类型
//参数3:资源的编号,同一种类型,从0开始编号
addr_res1 = platform_get_resource(pdev,IORESOURCE_MEM,0);
printk("addr_res1: start = 0x%x\n",addr_res1->start);
addr_res2 = platform_get_resource(pdev,IORESOURCE_MEM,1);
printk("addr_res2: start = 0x%x\n",addr_res2->start);
//方法一
irq_res = platform_get_resource(pdev,IORESOURCE_IRQ,0);
printk("irq_res: start = %d\n",irq_res->start);
printk("irq_res: end = %d\n",irq_res->end);
//方法二
irqno = platform_get_irq(pdev,0);
printk("irqno = %d\n",irqno);
led_dev->pdata = pdev->dev.platform_data; //设备层数据传递到驱动层
//5.硬件初始化
led_dev->reg_base = ioremap(addr_res1->start,resource_size(addr_res1));
/*
计算地址大小
static inline resource_size_t resource_size(const struct resource *res)
{
return res->end - res->start + 1;
}
*/
if(IS_ERR(led_dev->reg_base))
{
printk("ioremap error!\n");
ret = PTR_ERR(led_dev->reg_base);
goto err_device;
}
return 0;
err_device:
device_destroy(led_dev->clz,MKDEV(led_dev->major,0));
err_class:
class_destroy(led_dev->clz);
err_unregister:
unregister_chrdev(led_dev->major,"led_pdrv");
err_kfree:
kfree(led_dev);
return ret;
}
int led_pdrv_remove(struct platform_device *pdev)
{
printk("---------------%s------------------\n",__FUNCTION__);
iounmap(led_dev->reg_base);
device_destroy(led_dev->clz,MKDEV(led_dev->major,0));
class_destroy(led_dev->clz);
unregister_chrdev(led_dev->major,"led_pdrv");
kfree(led_dev);
return 0;
}
const struct platform_device_id led_id_table[] = {
{"s5pv210_led", 0x1122},
{"s3c2410_led", 0x1234},
{"s3c6410_led", 0x2233},
};
struct platform_driver led_pdrv = {
.probe = led_pdrv_probe,
.remove = led_pdrv_remove,
.driver = {
.name = "samsung led_drv",//自定义,但是一定要有,没有id_table则用这个名字和pdev匹配,--- ls /sys/bus/platform/drivers/下创建同名目录
},
.id_table = led_id_table,
};
static int __init led_pdrv_init(void)
{
printk("--------------%s--------------\n",__FUNCTION__);
return platform_driver_register(&led_pdrv);
}
static void __exit led_pdrv_exit(void)
{
printk("--------------%s--------------\n",__FUNCTION__);
platform_driver_unregister(&led_pdrv);
}
module_init(led_pdrv_init);
module_exit(led_pdrv_exit);
MODULE_LICENSE("GPL");
设备层代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "led_info.h"
#define LED_GPC0_CONF 0xe0200060
#define LED_GPC0_SIZE 8
//定义一个平台自定义数据
struct led_platdata led_pdata = {
.name = "gpc0_3/4",
.shift = 3,
.conf_reg_clear = 0xff,
.conf_reg_data = 0x11,
.data_reg = 0x3,
};
struct resource led_resource[] = {
[0] = {
.start = LED_GPC0_CONF,
.end = LED_GPC0_CONF + LED_GPC0_SIZE - 1,
.flags = IORESOURCE_MEM,
},
//以下是演示代码
[1] = {
.start = 0x1122,
.end = 0x1122,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 0xe0200160,
.end = 0xe0200160 + 8 - 1, //end地址的计算
.flags = IORESOURCE_MEM,
},
};
//可以不用实现5,但是一定要有,因为卸载的时候会调用
void led_pdev_release(struct device *dev)
{
printk("--------------%s--------------\n",__FUNCTION__);
}
struct platform_device led_pdev = {
.name = "s5pv210_led",
.id = -1,
.dev = {
.release = led_pdev_release,
.platform_data = &led_pdata, //platform_data为void*类型的变量(这样就可以将数据在匹配成功时传递给驱动层probe方法使用)
},
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
};
static int __init led_pdev_init(void)
{
printk("--------------%s--------------\n",__FUNCTION__);
return platform_device_register(&led_pdev);
}
static void __exit led_pdev_exit(void)
{
printk("--------------%s--------------\n",__FUNCTION__);
platform_device_unregister(&led_pdev);
}
module_init(led_pdev_init);
module_exit(led_pdev_exit);
MODULE_LICENSE("GPL");
数据结构fifo.h:
#ifndef __LED_INFO_H__
#define __LED_INFO_H__
//设计一个平台自定义数据类型
struct led_platdata{
char * name;
int shift;//移位数,3
int conf_reg_clear;//配置寄存器的清空
int conf_reg_data; //配置寄存器的值
int data_reg;//数据寄存器的值
};
#endif