OK6410 LED字符设备驱动编写

本次驱动编写使用:OK6410+linux3.0.1

 

在内核内部使用struct cdev结构来表示字符设备。因此在内核调用字符设备前需要分配并且注册一个cdev结构体。在代码中需要包括<linux/cdev.h>头文件。

字符设备驱动中需要编写相关的驱动函数,从而与应用程序中的open()、write()等函数形成对应。编写驱动函数后利用file_operations使驱动函数与应用程序中的函数形成对应关系。接着利用函数cdv_init初始化cdev结构体

void cdev_init(struct cdev *cdev,struct file_operations *fops)

接着利用函数cdv_add()告诉内核该结构的信息。

int cdev_add(struct cdev *dev,dev_t num,unsigned int count)

其中dev_t num表示设备号。每个字符设备都有自己的主设备号和次设备号,其中主设备号用于区分驱动程序,次设备号由内核所用,用于指定设备文件所指的设备。dev_t类型在<linux/types.h>中定义,是一个32位数,其中12位表示主设备号,20位表示次设备号。

为了获得设备号,可以使用函数register_chrdev_region()获得设备号

int register_chrdev_region(dev_t first, unsigned int count, char *name)

如果我们明确知道想要的设备号的话,使用该函数会有好的效果。但是当我们不知道哪些设备号被调用了的时候,使用动态分配设备号的方法会更好。使用该方法也有利于你的程序在不同的板子上运行,避免别的板子的系统中已经使用你想要用的设备号。

动态分配函数为:

int alloc_chrdev_region(dev_t *dev, 
                        unsigned int firstminor,
                        unsigned int count,
                        char *name)

当不使用设备时应该将设备编号进行释放,使用以下函数

void unregister_chrdev_region(dev_t first,unsigned int count)

想要移除一个字符设备,则使用cdev_del()

void cdev_del(struct cdev *dev)

在一些代码中会看到有的驱动不使用cdev接口进行定义,而是用以下方法:

int register_chrdev(unsigned int major,
                    const char *name,
                    struct file_operations *fops);

这是一种尚未升级到2.6版本接口的老代码。虽然这些接口仍然可以工作,但是新的代码应该尽量使用新的接口。

 

当加载驱动模块后,使用mknod创建字符设备节点后,可对该设备文件进行操作

或者是使用创建类和类设备的方法创建相应的设备文件,这样在加载了驱动模块后可直接使用。具体方法参见class_create()和device_create()的用法

 

驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>

struct class *led_class;
struct class_device *led_class_device;
volatile unsigned long *gpmcon = NULL;
volatile unsigned long *gpmdat = NULL; 
struct cdev cdev;
dev_t dev;

int led_open(struct inode *inode,struct file *file)
{
	gpmcon = (volatile unsigned long *)ioremap(0x7f008820,4);
	writel(0x1111,gpmcon);
	gpmdat = (volatile unsigned long *)ioremap(0x7f008824,4); /* 指针加1是以数据类型相加的*/
	printk("open!\n");
	return 0;
}

ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
 
    printk("$$$$$$$$$$led_read$$$$$$$$$\n");
 
     return count;
 
}

ssize_t led_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	char mbuf[10];
	printk("write!\n");
	copy_from_user(mbuf,buf,count);
	    switch(mbuf[0])
    {
        case 1:
            //writel(0x01, gpmdat);
			*gpmdat &= ~(0x0F);
            break;
 
        case 0:
            //writel(0x0F,gpmdat);
			*gpmdat |= 0x0F;
            break;
		default:
			break;
	return count;
    }
}



struct file_operations led = {
	.owner = THIS_MODULE,
	.open  = led_open,
	.read  = led_read,
	.write = led_write,
};


int led_init(void)
{

        cdev_init(&cdev,&led);

	alloc_chrdev_region(&dev,0,1,"led_chrdev");

	cdev_add(&cdev,dev,1);

	led_class = class_create(THIS_MODULE,"led");
	/*if(IS_ERR(led_class))
	return PTR_ERR(led_class);*/

	led_class_device = device_create(led_class,NULL,dev,NULL,"led1");
	/* if(unlikely(IS_ERR(led_class)))
	return PTR_ERR(led_class); */
   
	return 0;
}

void led_exit(void)
{
	cdev_del(&cdev);
	unregister_chrdev_region(dev,1);
	device_unregister(led_class_device);
	class_destroy(led_class);
}

MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);

测试代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main(int argc, char **argv)
{
    int fd;
    char val = 1;

    fd = open("/dev/led1",O_RDWR);
    
    if(fd < 0)
    { 
        printf("Can't open\n");
    }
    printf("argv[1] = %s\n",argv[1]);
    if(argc != 2)
    {
        printf("Wrong usage!,Usage: test <on|off>\n");
    }

    else if(strcmp(argv[1],"on") == 0)
     {
         val = 1;
         write(fd,&val,1);
         printf("check!\n");
     }
    else if(strcmp(argv[1],"off") == 0)
     {
         val = 0;
         write(fd,&val,1);
     }
    printf("end!\n");
    close(fd);
    return 0;
}

Makefile

KERDIR := /home/wade/arm/cha_driver/linux-3.0.1.new

all:
	make -C $(KERDIR) M=`pwd` modules

clean:
	make -C $(KERDIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m  += drv.o

 

展开阅读全文

没有更多推荐了,返回首页