ARM+Linux驱动----点亮开发板的LED


使用FS2440开发板2.6.4内核

1)关于fs_operations的问题
fs_operations数据结构是有文件系统(虚拟文件系统VFS)提供的,其主要作用是向上(应用层)提供统一的系统调用接口,比如open(),read(),write(),ioctl()等文件(Linux把所有的设备也当作文件)操作,向下屏蔽各种不同平台的差异。fs_operations内部是一个指针实现,链接了向上的接口和向下的具体实现。设备驱动的层次在文件系统之下,就是这个道理。因为设备驱动文件包含了对与底层硬件具体实现,虚拟文件系统通过fs_operation与之链接。注意一点:字符设备是没有对应的文件系统的,所以字字符设备的fs_operations需要在驱动文件当中提供。

2)关于主设备号和次设备号
linux 2.6.4设备通过一个dev_t来定义自身的设备号。其实也就是一个unsigned int 类型的数据。在这个32位的数据里面,前12位用于保存主设备号,后10位用于保存次设备号。所有功能相同且使用同一个驱动程序的设备有相同的主设备号,然后需要用次设备好去具体区分。理论上,一个主设备可以带有1024个次设备。

3)关于register_chrdev_region()和alloca_chrdev_region()函数的大致实现
register_chrdev_region()和alloca_chrdev_region()函数的功能类似与向windows的注册表添加一个注册项。注册项是一个char_device_struct的结构体数组。register_chrdev_region()和alloca_chrdev_region()函数在运行过程中都会调用__register_chrdev_region().
__register_chrdev_region()这个函数利用哈希列表分配主设备号。www.bkjia.com具体的算法是搜索char_device_struct的结构体散列桶,找到空位,就用空位的位号作为设备的主设备号。建议任何驱动都使用动态分配主设备号的原则。通过register_chrdev_region()和alloca_chrdev_region()最后获得了一个可以用的主设备号,然后要通过MKDEV(主,次)(次设备好自己手动指定),获得最后的设备号用cdev_add()向系统注册。

4)对硬件的操作: 可以使用内核提供的接口 s3c2410_gpio_setpin(管脚号,管脚状态代号)设置管脚状态 (设置GPnDAT) , s3c2410_gpio_cfgpin(管脚号,管脚功能代号) 设置管脚功能(相当于设置GPnCON)

源代码:

include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

#include"linux/module.h"
#include"linux/types.h"
#include"linux/fs.h"
#include"linux/errno.h"
#include"linux/mm.h"
#include"linux/sched.h"
#include"linux/init.h"
#include"linux/cdev.h"
#include"asm/io.h"
#include"asm/uaccess.h"
#include"asm/arch-s3c2410/regs-gpio.h"  // S3C2410_GPF4    and    S3C2410_GPF4_OUTP
#include "asm/arch-s3c2410/hardware.h"
#define DEVICE_NAME       "leds"

 

#define LIGHT_MAJOR 0  //i want a device number dynamatically


struct light_dev{             //Make the device an object , now it declar a device class   and you can think it making an abstract
struct cdev cdev;     //Declar a char device abstract
unsigned char value; //usr can read and write this car
};

struct light_dev *light_devp;   //now programe make a half abstract for the divice,and in light_init(),when kmalloc() allocate a space,it can think manke a real_abstraction
dev_t  light_major = LIGHT_MAJOR;

 

void light_GPIO_init(void){
s3c2410_gpio_cfgpin( S3C2410_GPF4  , S3C2410_GPF4_OUTP );
s3c2410_gpio_cfgpin( S3C2410_GPF5  , S3C2410_GPF5_OUTP );
s3c2410_gpio_cfgpin( S3C2410_GPF6  , S3C2410_GPF6_OUTP );
s3c2410_gpio_cfgpin( S3C2410_GPF7 , S3C2410_GPF7_OUTP );   
}


void light_on(void){
s3c2410_gpio_setpin(S3C2410_GPF4 , 0);
s3c2410_gpio_setpin(S3C2410_GPF5 , 0);
s3c2410_gpio_setpin(S3C2410_GPF6 , 0);
s3c2410_gpio_setpin(S3C2410_GPF7 , 0);
}

void light_off(void){
s3c2410_gpio_setpin(S3C2410_GPF4 , 1);
s3c2410_gpio_setpin(S3C2410_GPF5 , 1);
s3c2410_gpio_setpin(S3C2410_GPF6 , 1);
s3c2410_gpio_setpin(S3C2410_GPF7 , 1);
}

 

int light_open(struct inode * inode , struct file * filp)  
{
//when device driver open.fill the device struct
struct light_dev * dev;
dev = container_of(inode->i_cdev,struct light_dev,cdev );  //contariner_of is a macros
//make the device struct be a private in device file
filp -> private_data = dev; //make the releationship between the file and device in Linux,because Linux regard the device as a file!~

return 0;
}

ssize_t light_release(struct  inode * inode, struct file * filp )
{
return 0;
}

ssize_t light_read(struct file * filp , char __user  *   buff , size_t count , loff_t * f_pos)
{
struct light_dev * dev = filp->private_data;

if(copy_to_user(buff , &(dev->value) , 1))
{
return - EFAULT;
}
else
{
return 1;
}

}


ssize_t light_write(struct file * filp , const char __user  * buff , size_t count , loff_t * f_pos)
{
struct light_dev *dev = filp -> private_data;

if(copy_from_user(&(dev->value) , buff ,1) )
{
return -EFAULT;
}
else
{
if(dev->value)
{
light_on();
}
else{
light_off();
}
return 0;
}

 

}


static int light_ioctl(struct inode * inode ,struct file * filp , unsigned int  _cmd , unsigned long arg )
{
struct light_dev * dev = filp-> private_data;

switch(_cmd){
case 1:
dev->value = 1;  //just a flag
light_on();
break;
case 0:
dev->value = 0;  //just a flag
light_off();
break;
default: return  -ENOTTY;    //CMD CAN NOT SUPPORT
}
return 0;
}

static struct file_operations light_fops = {
//fill the file_operations struct
.owner  =  THIS_MODULE,
.read     =  light_read,
.write    =  light_write,
.ioctl      =  light_ioctl,
.open    =  light_open,
.release = light_release,

};

static void light_setup_cdev(struct light_dev * dev  , int index)
{
dev_t devno = MKDEV(light_major , index);   //make a device number and save it in a 32-intergred
int err = 0;
cdev_init(&dev->cdev , &light_fops );       //only fill the struct cdev
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &light_fops;         //? Re

if(( err = cdev_add(&dev->cdev , devno , 1) ) == 1){ // some key !!!!!!!!!!!!!!!!
printk(KERN_NOTICE " Error adding LED  ");
}

}

 

int light_init(void){
int result = 0;
dev_t devno = 0;
devno = MKDEV(light_major,0);

if(light_major){

if(( result = register_chrdev_region(devno,1,"LED"))  < 0){
printk(KERN_NOTICE  "add a register item failed");
printk(DEVICE_NAME "Allocate the device nomber failed!~");           
return  result;
}

}else{

if( ( result  = alloc_chrdev_region(&devno,0,1,"LED") )  <  0  ){
printk(KERN_NOTICE "add a register item failed");
printk(DEVICE_NAME "Allocate the device number failed!~");
return result;
}
light_major = MAJOR(devno);   // fill the majot_device_number to var light_major

}

//allocate zhe spcae to device struct
if( ( light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL)) <0 ){
result = -ENOMEM;
printk(KERN_NOTICE "kmalloc failed!~");       
goto failmalloc;
}

//Init the memory
memset(light_devp,0,sizeof(struct light_dev));

light_setup_cdev(light_devp,0);
light_GPIO_init();


s3c2410_gpio_setpin(S3C2410_GPF4 , 0);
s3c2410_gpio_setpin(S3C2410_GPF5 , 1);
s3c2410_gpio_setpin(S3C2410_GPF6 , 0);
s3c2410_gpio_setpin(S3C2410_GPF7 , 1);

 

//all successful~
return 0;

failmalloc: unregister_chrdev_region(devno,1);
printk(DEVICE_NAME "unregister~");
return result;
}


void light_cleanup(void){  
cdev_del(&light_devp->cdev);  //add the &light_devp->cdev to  system as well,~Remember add the device point and del the device point
kfree(light_devp);
unregister_chrdev_region(MKDEV(light_major,0),1);   
}

module_init(light_init);
module_exit(light_cleanup);

MODULE_AUTHOR("Cz.");
MODULE_LICENSE("Dual BSD/GPL");

 

Makefile:

#################################################
obj-m := ToggleLed.o
KERNELDIR := /usr/local/arm/3.4.1/arm-linux/yle2440_2.6.12
default:
$(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules
install:
insmod LED.ko
uninstall:
rmmod LED.ko
#################################################

  • 1
  • 2
  • 下一页

相关内容