ARM+Linux驅動----點亮開發板的LED

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()這個函數利用哈希列表分配主設備號。具體的算法是搜索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
#################################################


修改KERNELDIR的路徑,主要是進入移植上的源代碼路徑,用內核編譯工具進行編譯
然後上傳Toggle.ko用insmod Toggle.ko 掛載,然後 cat /proc/devices 查看LED設備號然後創建特殊文件節點: mknod /dev/LED c 設備號 0   當設備掛載成功FS2440開發板上的燈量的狀態是0b1010.

然後編譯用戶空間測試代碼: 功能控制燈的全亮(目的在於驗證能正確控制I/O管腳)

#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
#include"sys/types.h"
#include"sys/stat.h"

unsigned char cmd = 0x01;

int main()
{

int fd_led = 0;

if((fd_led = open("/dev/LED",O_RDWR)) < 0 ){
printf("fd_led = %d",fd_led);
printf("open failed!~");
exit(1);
}

if(( write(fd_led,&cmd,1)) != 1)
{
printf("write failed!~");
exit(1);
}

close(fd_led);

return 0;
}

然後編譯:   arm-linux-gcc -o ControlLed.o ControlLed.c  然後用FTP上傳至FS2440的開發板運行,得LED全亮。成功!~

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章