linux内核3.4 led驱动,linux驱动编写:从hello world到 LED驱动

图2.1 LED硬件工程

2.2 LED驱动

LED属于字符类驱动,符合字符类驱动的写法。Linux内核中每个模块实际上是以文件形式存在的,这些文件存放于/dev目录下。而不同的驱动模块具有不同的主设备号和次设备号。主设备号用于区别不同的驱动,而次设备号用于更具体的指向驱动指向的设备。设备号就相当于门牌号,用于唯一区别不同驱动。编写驱动过程中就需要为驱动分配设备号。现在进一步分析LED驱动代码:

#define LED_DATA 0x41200000

#define LED_CTRL 0x41200004

这两行定义了LED数据控制寄存器和数据读写寄存器的内存地址。实际上驱动对LED硬件的配置和读写都是通过配置其寄存器实现的。具体要看GPIO的硬件信息。

dev_t led_devt;

void __iomem *baseaddr;

dev_t定义了设备编号,__iomem定义了linux内核的存储指针。硬件的内存需要映射到linux内核空间才能操作。在使用和配置LED时,需要先将其物理内存映射到linux内核的内存空间。映射函数ioremap就是专门用于IO端口内存映射的。

static int led_major=25;

struct cdev *led_dev;

int led_value;

上述定义了一个led主设备号。通过ls –l命令可以查看/dev下驱动的设备号。当然这是一种不方便的方式,正常情况下设备号也可以自动分配。Cdev是字符设备的结构体,定义如下:

struct cdev {

struct kobject kobj; // 内嵌的kobject对象

struct module *owner; // 所属模块

const struct file_operations *ops; // 文件操作结构体

struct list_head list; //linux内核所维护的链表指针

dev_t dev; //设备号

unsigned int count; //设备数目

};

文件操作结构体实际上提供了软件操作LED的接口,上文讲过驱动都被映射成/dev下的一个文件,软件调用驱动的时候,以打开,读写,关闭对应设备文件来进行操控。我们看一下LED驱动中的文件结构:

struct file_operations led_fops={

.owner=THIS_MODULE,

.read=led_read,

.write=led_write,

.unlocked_ioctl=led_ioctl,

.open=led_open,

.release=led_release,

};

其中包含了文件应该有的四种基本操作open, read, write, release实际上是close。文件结构体还提供了ioctrl函数,这个函数为软件提供了一种更为灵活的操纵底层硬件的方法。

接下来对文件结构体中的每个函数进行分析。

1) led_open

static int led_open(struct inode *inode, struct file *filp){

struct resource *res;

int reg;

printk("begin: open led/n");

filp->private_data=inode->i_cdev;

res=request_mem_region(LED_DATA, 0x10000, "LED");

if(!res){

printk("failed requesting resource/n");

return 0;

}

printk("begin: remap led/n");

baseaddr=ioremap(LED_DATA, 0x10000);

if(!baseaddr){

printk("ERROR: couldn't allocate baseaddr/n");

return 0;

}

printk("baseaddr is %x/n", baseaddr);

printk("begin: read led/n");

reg=ioread32(baseaddr);

printk("begin: write led %d/n", reg);

reg &= 0xFFFFFFF0;

iowrite32(reg, baseaddr+4);

printk("SUCCESS: gpio init/n");

return 0;

}

内核用inode结构在内部表示文件,inode结构中包含了大量有关文件的信息。当我们在linux中创建一个文件时,就会在相应的文件系统创建一个inode与之对应。文件实体和文件的inode是一一对应的。当打开文件时,就获得了inode。通过inode可以获得字符设备结构体i_cdev。Inode在驱动开发中很少进行填充,通常都是用于查看。在使用LED时,需要为其分配内存,首先通过函数request_mem_region来看是否有空闲linux内核内存可分配,如果可以就通过ioremap进行分配,返回linux内存首地址。之后读写LED的时候就可以通过向这个地址写数据来控制LED了。iowrite32(reg, baseaddr+4)是在配置LED,使能了LED。

2) led_write

因为点亮LED是输出数据,所以实际上用不上读操作,只有写操作。

ssize_t led_write(struct file * filp, const char __user *buf, size_t cnt, loff_t *f_ops){

if(copy_from_user(&led_value, buf, cnt))

return -EFAULT;

led_gpio_set();

return 1;

}

void led_gpio_set(void){

iowrite32(led_value, baseaddr);

}

Copy_from_user是linux内核从用户空间获得要写入LED的数据。Led_gpio_set函数中通过iowrite函数将用户空间的数据写入LED。

3) led_release

static int led_release(struct inode *inode, struct file *filp){

iounmap(baseaddr);

release_mem_region(LED_DATA, 0x10000);

return 0;

}

这个是在调用close函数的时候会调用这个函数来释放内存,解除LED内存映射。

现在再来看初始化和驱动清除函数:

static void led_setup_dev(int index){

int err;

int devno;

devno=MKDEV(led_major, index);

printk("MKDEV devno %d/n", devno);

cdev_init(led_dev, &led_fops);

printk("cdev_init %d/n", devno);

led_dev->owner=THIS_MODULE;

led_dev->ops=&led_fops;

err=cdev_add(led_dev, devno, 1);

if(err)

printk(KERN_ERR "ERROR: %d adding LED%d", err, index);

printk("SUCCESS: add dev %d/n", devno);

}

static int __init led_init(void){

int result;

printk("INIT:------------/n");

result=alloc_chrdev_region(&led_devt, 0, 1, "LED");

led_major=MAJOR(led_devt);

if(result printk(KERN_ERR "ERROR: allocate chrdev %d", led_devt);

return result;

}

printk("SUCCESS: allocate chrdev %d/n", led_devt);

led_dev=cdev_alloc();

if(!led_dev){

printk(KERN_ERR "ERROR: allocate device mem %d", led_devt);

result=-ENOMEM;

goto fail_malloc;

}

printk("SUCCESS: allocate device mem %d/n", led_devt);

led_setup_dev(0);

printk("SUCCESS: init device %d/n", led_devt);

return 0;

fail_malloc:

unregister_chrdev_region(led_devt, 1);

return result;

}

static void __exit led_cleanup(void){

cdev_del(led_dev);

unregister_chrdev_region(led_devt, 1);

printk("SUCCESS: exit device %d/n", led_devt);

}

LED驱动初始化操作首先要为led_devt动态分配设备号,然后可以通过MAJOR来得到主设备号。这个主设备号在将字符设备添加到驱动中会用到。获得了设备号后就对led字符设备结构体led_dev分配空间,然后调用函数led_setup_sev完成对LED设备的初始化,添加等。这些就是一般过程了。作为初学者,先会用,以后在驱动的调试中会深入去理解。

清除函数就是释放LED字符设备空间。

2.3 LED软件操作

现在来看如何在用户端操作LED,上代码:

#include #include #include #include

int main(int argc, char *argv[]){

int fd;

fd=open("/dev/LED", O_RDWR);

if(fd printf("ERROR: cannot open/n");

return 0;

}

int value=0xF;

write(fd, &value, 4);

//close(fd);

return 0;

}

Open函数打开LED字符设备,然后通过write函数来点亮LED。

2.4 实验

首先加载led.ko:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值