jz2440:platform设备驱动LED实验(13)

   1.驱动:driver.c 

#include <linux/module.h>

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>

#define DEVICENAME "myled"

static unsigned int major=0;
unsigned int pin=0;
static struct class *myclass=NULL;
static struct class_device  *mydev;

static volatile unsigned long *gpfcon=NULL;
static volatile unsigned long *gpfdat=NULL;

#define GPFCON *gpfcon
#define GPFDAT *gpfdat


static int led_open(struct inode *inode, struct file *file)
{
    //配置LED管脚模式
    GPFCON &=~(0x3<<(pin*2));
    GPFCON |= (0x1<<(pin*2));

    //默认灯全部关闭
    GPFDAT |= ((7<<4));
    return 0;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;

    copy_from_user(&val, buf, count); //    copy_to_user();

    if (val == 1)
    {
        // 点灯
        GPFDAT &= ~(1<<pin);
    }
    else
    {
        // 灭灯
        GPFDAT |= (1<<pin);
    }

    return 0;
}

static struct file_operations my_dev_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,     
    .write  =   led_write,

};


/* 分配/设置/注册一个platform_driver */
static int __devinit led_probe(struct platform_device *pdev)
{
    /* 根据platform_dev资源进行ioremap */
    struct resource *res;
    // 从device中获取资源 resource led_resource[]
    // parms: device ,资源类型,资源索引
    res = platform_get_resource(pdev,IORESOURCE_MEM,0);
    gpfcon = ioremap(res->start,res->end-res->start+1);
    gpfdat= gpfcon+1;

    res=platform_get_resource(pdev,IORESOURCE_IRQ,0);
    pin=res->start;

    /* 注册字符设备   */
    major = register_chrdev(0,DEVICENAME, &my_dev_fops); // 注册, 告诉内核
    myclass = class_create(THIS_MODULE,DEVICENAME);
    mydev=device_create(myclass, NULL, MKDEV(major,0), NULL, "led");
    printk("led_probe,found led\n");
    return 0;
}

static int __devexit led_remove(struct platform_device *pdev)
{
    /* 根据platform_dev资源进行iounmap */
    /* 注销字符设备   */
    iounmap(gpfcon);


    device_unregister(mydev);
    class_destroy(myclass);
    unregister_chrdev(major, DEVICENAME); // 卸载
    printk("led_remove,remove led\n");
    return 0;
}

struct platform_driver led_drv = {
    .probe      = led_probe,  // 初始化
    .remove     = led_remove, // 卸载
    .driver     = {
        .name   = "myled",
    }
};

static int led_drv_init(void)
{
    platform_driver_register(&led_drv);
    return 0;
}

static void led_drv_exit(void)
{
    platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

程序的函数调用堆栈:

  • insmod driver.ko

module_init(led_drv_init);  // 模块初始化

--->led_drv_init

--->platform_driver_register(&led_drv);  // 调用平台设备初始化

--->led_drv的probe()函数,即static int __devinit led_probe(struct platform_device *pdev)函数

---> res = platform_get_resource(pdev,IORESOURCE_MEM,0); //从device.c中的文件获取得到设备相关信息,用于设备的初始化。。。

--->接下来就是字符设备的基本初始化。

  • rmmod driver.ko

module_exit(led_drv_exit);  // 模块卸载

--->led_drv_exit

--->platform_driver_unregister(&led_drv);  // 调用平台设备初始化

--->led_drv的remove()函数,即static int __devinit led_remove(struct platform_device *pdev)函数

---> 字符设备卸载的基本操作。

2.设备 device.c

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>

#include <linux/mtd/partitions.h>
#include <linux/input.h>

#include <linux/module.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>



#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>



/* 分配/设置/注册一个platform_device */

void led_release(struct device *dev)
{
    printk("led device release\r\n");

}
static struct resource led_resource[] = {

    [0]=
    {
        .start      = 0x56000050,
        .end        = 0x56000050 + 8 - 1,
        .flags      = IORESOURCE_MEM,
    },
    [1]=
    {
        .start      = 4,
        .end        = 4,
        .flags      = IORESOURCE_IRQ,
    }
};



static struct platform_device led_dev = {
    .name       = "myled",
    .id     = -1,  // 无此设备
    .dev        = {
        .release = led_release,
    },
    // 资源数量
    .num_resources  = ARRAY_SIZE(led_resource),
    
    .resource   = led_resource,
};


static int led_dev_init(void)
{
    platform_device_register(&led_dev);
    return 0;
}

static void led_dev_exit(void)
{
    platform_device_unregister(&led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

流程与driver.c的流程类似。

注册设备--->主要是将设备相关信息注册到platform,于是drvice.c可以通过 platform_get_resource()函数获取到led_resource[]中的相关数据。所以需要先insmod device.ko

 3.测试APP:

/******************firstdrvtest.c*************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
 
/* firstdrvtest on
  * firstdrvtest off
  */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/led", O_RDWR);//可通过ls /dev/xyz命令查看
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}
 
	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 4);
	return 0;
}

这就是老生常谈了,通过open/write函数调用到driver.c中的与之相匹配的my_dev_fops的.open  和 .write函数,而这两个函数的具体实现去操作寄存器,从而控制LED的点亮熄灭。

总结:通过这个例子和APP.c可以感觉出来,platform模型,其实就是一种驱动的工作模式,他完成的功能和之前的led_drive是一样的,只不过之前是设备和驱动是混在一起的,这个就是把设备和驱动完全分离开,可通过一个驱动框架,去适配这一类的所有设备。真的是很妙的思想。

4.Makefile

########################Makefile##########################
KERN_DIR =/home/zion/Desktop/JZ2440+QT/linux-3.4.2/linux-3.4.2  #这里一定要改为与代码匹配的内核的路径,否则会有各种问题
 
all:
	make -C $(KERN_DIR) M=`pwd` modules 
 
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= driver.o device.o#名字要与led_drv.c匹配

5.试验步骤:

  •  make 生成device.ko和driver.ko
  • arm-linux-gcc app.c 生成可执行文件a.out
  •  insmod device.ko

  • insmod driver.ko 

下图的打印代表调用了 platform_driver_register(&led_drv);中led_drv->probe()函数

 

然后测试程序:

  • ./a.out on即可看到 led的点亮
  • ./a.out on即可看到 led的熄灭
  • 通过demsg即可看到写在代码中的printk的打印

Guess you like

Origin blog.csdn.net/m0_37844072/article/details/120030999