fl2440 led驱动移植详细记录

来源:互联网 发布:三个数据库应用系统 编辑:程序博客网 时间:2024/06/04 17:45

/*********************************************************************程序开始****************************************************************************/

#include <linux/module.h>     /*  Every Linux kernel module must include this head */
#include <linux/init.h>     /*  Every Linux kernel module must include this head */
#include <linux/kernel.h>   /*  printk() */
#include <linux/fs.h>       /*  struct fops */
#include <linux/errno.h>    /*  error codes */
#include <linux/cdev.h>     /*  cdev_alloc()  */
#include <asm/io.h>         /*  ioremap()  */
#include <linux/ioport.h>   /*  request_mem_region() */
#include <linux/slab.h>

#include <asm/ioctl.h>      /*  Linux kernel space head file for macro _IO() to generate ioctl command  */
#ifndef __KERNEL__
#include <sys/ioctl.h>      /*  User space head file for macro _IO() to generate ioctl command */
#endif

#define DEV_AUTHOR          "hurryliu<hurryliu@126.com>"
#define DEV_DESC            "fl2440 led driver"
#define DEV_NAME            "led"
#define DEV_NUM             4
#define NUM                 4
#define ERR                 -1

#define S3C_GPB_BASE        0x56000010  // 定义led的控制寄存器的基地址和偏移
#define S3C_GPB_LEN         0x10
#define GPBCON_OFFSET       0
#define GPBDAT_OFFSET       4
#define GPBUP_OFFSET        8

#define PLATDRV_MAGIC       0x1C   

#define LED_OFF             _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON              _IO (PLATDRV_MAGIC, 0x19)

 /*定义一个魔数。魔数有着特殊的功能。我们定义了led_on和led_off,但是这个宏定义可能和系统的别的重复,因此我们采用魔数机制,定义一个系统未用的魔数,然后让魔数生成我们定义的led_on和led_off,这样,我们的定义就不会和系统的相同了。*/

#ifndef LED_MAJOR
#define LED_MAJOR           0      

#endif

//定义默认的主设备号为0,一般这个定义的设备号是固定不可用的,但是这为自动分配主设备号的逻辑提供了方便。
int    led_num = 4;                        //定义设备个数
static int    LED[NUM] = {5,6,8,10};        //定义设备在datasheet的位置
int    led_major = LED_MAJOR;
int    led_minor = 0;

static volatile unsigned long  gpb_con, gpb_dat;
#define s3c_gpio_write(value, reg) __raw_writel((value), (reg)+fl2440_gpb_membase)  
#define s3c_gpio_read(reg)       __raw_readl((reg)+fl2440_gpb_membase)

/*定义宏,这里用到了__raw_writel和__raw_readl两个宏,这里对这两个宏的功能不做详细介绍,简单的讲就是读操作和写操作寄存器*/

static volatile unsigned long   gpb_con, gpb_dat;
static void __iomem             *fl2440_gpb_membase;
static struct cdev              *led_cdev;

static int fl2440_hw_led_init(void)
{
    if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN,DEV_NAME))/* 申请内存。注意:这里的内存是FL2440中实际的物理内存,他对应了与LED的相关的寄存器*/
    {
        return ERR;
    }

    if( !(fl2440_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )

/* 建立物理内存到虚拟内存的映射。注意:在内核启动之后,所有对内存的操作都是虚拟的,如果要操作实际的物理内存,那么就要使用ioremap建立映射关系*/
    {
       release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);/*申请内存有可能失败,如别的程序占用此资源等原因。这时候就必须要停止申请*/
      return ERR;
    }

    gpb_con = s3c_gpio_read(GPBCON_OFFSET);
    gpb_con &= ~((3<<20)|(3<<16)|(3<<12)|(3<<10));   /*  清零相关位*/
    gpb_con |= ((1<<20)|(1<<16)|(1<<12)|(1<<10)); /*  设置为OUTPUT模式*/
    s3c_gpio_write(gpb_con, GPBCON_OFFSET);

    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
    gpb_dat |= ((1<<10)|(1<<8)|(1<<6)|(1<<5));  /* default: This port set to low level, then turn LED on */
    s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);

    return 0;
}

static void fl2440_hw_exit(void)
{
    {
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
        gpb_dat |= ((1<<10)|(1<<8)|(1<<6)|(1<<5));  /*turn off the all led */
        s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
    }

    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);/* 程序退出的时候,要释放申请的内存 */
    iounmap(fl2440_gpb_membase);     /* 解除映射关系*/
}

static int led_open(struct inode *inode, struct file *file)          //如果在应用空间调用open函数,就会调用led_open,打开相应的设备节点
{
    int minor = iminor(inode); /* iminor函数能够将设备节点的次设备号获取出来,根据次设备号,就可以操作该设备*/
    file->private_data =(void *)minor;
    printk("/dev/led%d opened.\n",minor);
    return 0;
}
static int led_release(struct inode *inode,struct file *file)   //如果在应用程序空间调用close函数,就会调用led_release,关闭节点
{
    printk("/dev/led%d closed.\n",iminor(inode));
    return 0;
}
static void led_on(int which_led)
{
    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
    gpb_dat &= ~(0x01<<LED[which_led]);  /*turn on the all led */
    s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}
static void led_off(int which_led)
{
    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
    gpb_dat |= (0x01<<LED[which_led]);  /*turn off the all led */
    s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}

 

static long led_ioctl(struct file *file,unsigned int cmd, unsigned long args)
{
    int which = (int)file->private_data;

    switch(cmd)
    {
        case LED_ON:
            led_on(which);
            break;
        case LED_OFF:
            led_off(which);
             break;
        default:
             printk("%s is not support ioctl command=%d!\n", DEV_NAME, cmd);
             break;
    }
    return 0;
}
static struct file_operations led_fops =
{
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};

static int fl2440_sw_led_init(void)
{
   int result;

   dev_t dev_m;

   if(led_major != 0)
   {//典型的手动分配主设备号
       dev_m = MKDEV(led_major,0);/* 将主设备号和次设备号转换成dev_t类型 */
       result = register_chrdev_region(dev_m,led_num,DEV_NAME);/*to appoint minor number to a device*/
   }//为一个字符驱动获取一个或多个设备编号来使用,dev_m是起始的设备编号,通常为零,led_num是有多少个设备
   else
   {
       result = alloc_chrdev_region(&dev_m,led_minor,led_num,"led");/* dynamic alloc the device number */
       led_major = MAJOR(dev_m);
   }//动态分配设备编号,第一个参数是设备,第二个是参数是第一次设备号,一般为零,第三个参数是设备个数,

   if(result < 0)
   {
       printk("led major allocation failure!\n");
       printk("fl2440 %s driver use major number%d.\n", DEV_NAME, led_major);
       return ERR;    //动态分配失败

   }
   printk("fl2440 %s driver use major number %d.\n", DEV_NAME, led_major);

   if(NULL == (led_cdev=cdev_alloc())) /* Register independent character device */
   {
       printk(KERN_ERR "fl2440  can't register device for led!\n");
       unregister_chrdev_region(dev_m,led_num); /*release the dev*/
       return ERR;//分配失败的时候,要取消分配
   }

   led_cdev->owner = THIS_MODULE;
   cdev_init(led_cdev, &led_fops);     /* 初始化设备*/ 
   if(0!=(result = cdev_add(led_cdev, dev_m, led_num)))
/* add a character device to the system */
   {
       printk(KERN_INFO "led driver can't reigster cdev! ");
       goto error;
   }
   return 0;

error:    //如果在注册的过程中出现失败,那么程序转到此处,反向注销
   cdev_del(led_cdev); /* delete a character device  */
   unregister_chrdev_region(dev_m,led_num);
   return result;
}

 


static int __init fl2440_led_init(void)

/*驱动进来的第一个函数,此函数有module_init指定。在这里,我喜欢放两个函数,一个函数是硬件的初始化,包括内存申请,物理地址和虚拟地址的映射,寄存器的初始化等工作;另一个函数是软件的初始化,包括主次设备号的申请,设备的注册和添加,设备的初始化等工作。*/
{
   int result;
   printk("led module install in your system!\n");

   result=fl2440_hw_led_init();
   if(result != 0)
   {
       printk("led hardware init failure!\n");
       return ERR;
   }
   printk("led hardware init succeed!\n");

   result=fl2440_sw_led_init();
   if(result != 0)
   {
       printk("led software init failure!\n");
       return ERR;
   }
   printk("led software init succeed!\n");
   return 0;
}

static void __exit fl2440_led_exit(void)  

  /*驱动退出时候执行的函数,由module_exit指定,完成一些扫尾工作,比如释放cdev占用的内存,释放原先申请的设备号等。它像你忠实的仆人,为你做清洁,不过记得给小费哦。*/
{
        dev_t dev_m = MKDEV(led_major, led_minor);

        fl2440_hw_exit();
        cdev_del(led_cdev);
        unregister_chrdev_region(dev_m,led_num);
        printk("led module removed from your system!\n");
        return ;
}

module_init(fl2440_led_init);
module_exit(fl2440_led_exit);

MODULE_AUTHOR(DEV_AUTHOR);
MODULE_DESCRIPTION(DEV_DESC);
MODULE_LICENSE("GPL");

/*********************************************************************程序结束***************************************************************************************/

与主次设备号相关的3个宏:
MAJOR(dev_t dev):根据设备号dev获得主设备号;
MINOR(dev_t dev):根据设备号dev获得次设备号;
MKDEV(int major, int minor):根据主设备号major和次设备号minor构建设备号。


 

原创粉丝点击