LED驱动实例

 

第一个(不完整):

#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/uaccess.h>

/*Comment/uncomment the following line to disable/enable debugging,
 OR define(or NOT) it in Makefile.
*/
#define S3C24XX_LEDS_DEBUG

#undef PDEBUG     /* undef it, just in case */

#ifdef S3C24XX_LEDS_DEBUG
#ifdef __KERNEL__
  /* This one if debugging is on, and kernel space */
#define PDEBUG(fmt, args...) printk( KERN_DEBUG "s3c24xx_leds: " fmt, ## args)
#else
   /* This one for user space */
#define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#endif
#else
#define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif


#define LEDS_NAME "leds"
static int leds_major = 0;
module_param (leds_major, int, S_IRUGO | S_IWUGO);    //模块参数
MODULE_PARM_DESC (leds_major, "leds major number");

#define CMD_LED_ON 1
#define CMD_LED_OFF 0

#define GPFBASE 0x56000050
#define GPFCON (GPFBASE + 0x0)  //控制寄存器
#define GPFDAT (GPFBASE + 0x4)  //数据寄存器
#define GPFUP  (GPFBASE +0x8)   //上拉电阻


static volatile unsigned long *vGPFBASE = NULL;
#define vGPFCON (vGPFBASE +0)
#define vGPFDAT (vGPFBASE +1)
#define vGPFUP  (vGPFBASE +2)
static int s3c24xx_leds_open (struct inode *inode, struct file *filp)
{
 PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);

 //GPFCON, set GPF4~7 output  设置输出引脚控制
 writel ((readl (vGPFCON) & ~0xff00) | 0x5500, vGPFCON);//从内存映射的 I/O 空间读取数据(readl 从 I/O 读取 32 位数据 ( 4 字节 )。)

 //GPFUP Disable 禁止上拉电阻
 writel ((readl (vGPFUP) & ~0xf0) | 0xf0, vGPFUP);

 return 0;
}

static ssize_t s3c24xx_leds_read (struct file *filp, char __user * buf, size_t size, loff_t * offset)
{

 return 0;
}

static int s3c24xx_leds_close (struct inode *inode, struct file *filp)
{
 PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);

 return 0;
}


static int s3c24xx_leds_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{

 PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);

 if (!capable (CAP_SYS_ADMIN)) {
  return -EPERM;
 }
 if (arg < 1 || arg > 4) {
  printk (LEDS_NAME " led number error!\n");
  return -EINVAL;
 }

 switch (cmd) {
 case CMD_LED_ON:
  //点亮
  writel (readl (vGPFDAT) & (~(1 << (4 + arg - 1))), vGPFDAT);
  break;

 case CMD_LED_OFF:
  //熄灭
  writel (readl (vGPFDAT) | (1 << (4 + arg - 1)), vGPFDAT);
  break;
 default:
  printk (LEDS_NAME " Can't support command!\n");
  return -EINVAL;
  break;
 }
 return 0;
}

static struct file_operations s3c24xx_leds_fops = {
 .owner = THIS_MODULE,
 .open = s3c24xx_leds_open,
 .release = s3c24xx_leds_close,
 .ioctl = s3c24xx_leds_ioctl,
 .read = s3c24xx_leds_read,
};

static void __exit hello_exit (void)
{
 unregister_chrdev (leds_major, LEDS_NAME); //注销
 printk (LEDS_NAME " driver exit!\n");
}


static int __init hello_init (void)
{
 int ret = -1;

 PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);

 ret = register_chrdev (leds_major, LEDS_NAME, &s3c24xx_leds_fops); //内核注册函数
 if (ret < 0) {
  printk (LEDS_NAME " can't register major number\n");
  return -EINVAL;
 }
 if (ret > 0) {
  leds_major = ret;
 }

 vGPFBASE = (volatile unsigned long *) ioremap (GPFBASE, 0x20); //物理地址映射到虚拟地址
 if (!vGPFBASE) {
  printk (LEDS_NAME " Can't ioremap!\n");
  return -ENOMEM;
 }

 printk (LEDS_NAME " initilized!\n");
 return 0;
}


module_init (hello_init);   //模块入口
module_exit (hello_exit);  //模块出口

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("StephenYee([email protected])");
MODULE_DESCRIPTION ("S3C2410 leds driver!");

 

 

 

第二个(完整):

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

#define DEVICE_NAME     "leds"  /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define LED_MAJOR       231     /* 主设备号 */

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON    0
#define IOCTL_LED_OFF   1

/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] = {
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,
};

/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] = {
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};

/* 应用程序对设备文件/dev/leds执行open(...)时,
 * 就会调用s3c24xx_leds_open函数
 */
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
    int i;
   
    for (i = 0; i < 4; i++) {
        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
    }
    return 0;
}

/* 应用程序对设备文件/dev/leds执行ioclt(...)时,
 * 就会调用s3c24xx_leds_ioctl函数
 */
static int s3c24xx_leds_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg)
{
    if (arg > 4) {
        return -EINVAL;
    }
   
    switch(cmd) {
    case IOCTL_LED_ON:
        // 设置指定引脚的输出电平为0
        s3c2410_gpio_setpin(led_table[arg], 0);
        return 0;

    case IOCTL_LED_OFF:
        // 设置指定引脚的输出电平为1
        s3c2410_gpio_setpin(led_table[arg], 1);
        return 0;

    default:
        return -EINVAL;
    }
}

/* 这个结构是字符设备驱动程序的核心
 * 当应用程序操作设备文件时所调用的open、read、write等函数,
 * 最终会调用这个结构中指定的对应函数
 */
static struct file_operations s3c24xx_leds_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   s3c24xx_leds_open,    
    .ioctl  =   s3c24xx_leds_ioctl,
};

/*
 * 执行insmod命令时就会调用这个函数
 */
static int __init s3c24xx_leds_init(void)
{
    int ret;

    /* 注册字符设备
     * 参数为主设备号、设备名字、file_operations结构;
     * 这样,主设备号就和具体的file_operations结构联系起来了,
     * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
     * LED_MAJOR可以设为0,表示由内核自动分配主设备号
     */
    ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
    if (ret < 0) {
      printk(DEVICE_NAME " can't register major number\n");
      return ret;
    }
   
    printk(DEVICE_NAME " initialized\n");
    return 0;
}

/*
 * 执行rmmod命令时就会调用这个函数
 */
static void __exit s3c24xx_leds_exit(void)
{
    /* 卸载驱动程序 */
    unregister_chrdev(LED_MAJOR, DEVICE_NAME);
}

/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);

/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("http://www.100ask.net");
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
MODULE_LICENSE("GPL");

 

第三个(完整):

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2   //获得LED灯状态

#define LEDS_NUM 4    //LED灯数量


#define S3C24XX_LEDS_PHY_BASE 0x56000050     //控制寄存器物理地址
#define GPFCON_OFFSET 0x0  
#define GPFDAT_OFFSET 0x1
#define GPFUP_OFFSET 0x2


static int major = 0;   //主设备号
module_param(major, int, S_IRUGO|S_IWUGO);   //模块参数
MODULE_PARM_DESC(major,"s3c24xx leds major number");   //参数描述

struct s3c24xx_leds_dev {          //设备结构体
 unsigned char  led_state;  //led_state [4-7], 1: on, 0: off
 unsigned long *pBase;
 //...
};
struct s3c24xx_leds_dev *devs =NULL;    //定义一个设备结构体对象

static int s3c24xx_leds_open (struct inode *inode, struct file *filp)   //打开函数

 unsigned long * pReg = devs->pBase;
 filp->private_data = (void *)devs;   //获得设备结构体对象信息

 //配置寄存器
 //GPFCON output.
 writel( (readl(pReg +GPFCON_OFFSET) &~0xff00) |0x5500, pReg +GPFCON_OFFSET);
 //GPFUP disable pullup.
 writel( (readl(pReg +GPFUP_OFFSET) &~0xff00) |0x5500, pReg +GPFUP_OFFSET);

 return 0;
}

static int s3c24xx_leds_close (struct inode *inode, struct file *filp)     //关闭函数

 
      filp->private_data = NULL;
 return 0;
}

static ssize_t s3c24xx_leds_read (struct file * filp, char __user *buf, size_t len, loff_t *loff)

 struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;  //获得结构体信息
 unsigned char kbuff;
 
 kbuff = pdev->led_state >>4;  //右移四位(定位引脚)
 
 //完成内核空间到用户空间的拷贝
       //static inline int copy_to_user(void __user *to, const void *from, int n)
      
 if (copy_to_user((void __user *)buf, &kbuff, 1) ) 
 {
  return -EFAULT;
 }
 
 return 0;
}


static ssize_t s3c24xx_leds_write (struct file * filp, char __user *buf, size_t len, loff_t *loff)
{
 struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
 unsigned char kbuff;
 
 //完成用户空间到内核空间的拷贝
 //static inline int copy_from_user(void *to, const void __user *from, int n)
 
 if (copy_from_user(&kbuff, (void __user *)buf, 1))
 {
  return -EFAULT;
 }

    kbuff = kbuff<<4;   //获得数据
  
 //真正的LED操作
 if ( pdev->led_state != kbuff ) {
 //...
  int i;
  for (i = 0; i< LEDS_NUM;i++) {
   if( (pdev->led_state & (1<< (4+i)))  ^ (kbuff  & (1<<( 4+i))) ) {
    if (pdev->led_state & (1<< (4+i)) ) { // 原来的状态是开,现在设置为关
     writel( (readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0) | (1<< ( 4+i )), pdev->pBase+GPFDAT_OFFSET);
    } else {  // 原来的状态是关,现在设置为开
     writel( ((readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0)) |  ~(1<< ( 4+i)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
    }
   }
  }
 }
 pdev->led_state = kbuff;
 return 0;
}


static int s3c24xx_leds_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
 struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
        unsigned char state;
   //参数正确性验证
 if (arg <1 || arg >4)
  return -EINVAL;
 //权限管理
 if (!capable (CAP_SYS_ADMIN)) {
  return -EPERM;
 }
 
 switch (cmd) {
  case CMD_LED_ON:    //开
  //...
   // writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xe0, pdev->pBase+GPFDAT_OFFSET);
   writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |  ~(1<< ( 4+arg -1)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
   pdev->led_state |=  1<< ( 4+arg -1); 
  break;

  case CMD_LED_OFF:    //关
   //writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xf0, pdev->pBase+GPFDAT_OFFSET);
   writel( readl(pdev->pBase +GPFDAT_OFFSET)  | 1<< ( 4+arg -1), pdev->pBase+GPFDAT_OFFSET);
   pdev->led_state &=  ~(1<< ( 4+arg -1));
  break;

  case CMD_LED_GET_STATE:
   state =  pdev->led_state >>4;
   printk("driver: led_sate:%0x", state& 0x0f );
   return copy_to_user((void *) arg, &state,  1) ?  -EFAULT : 0;
  break;

  default:
   printk("Unknown command\n");
  break;
 }
 
 return 0;
}

static struct file_operations s3c24xx_leds_ops = {
 .owner = THIS_MODULE,
 .open = s3c24xx_leds_open,
 .release  = s3c24xx_leds_close,
 .read = s3c24xx_leds_read,
 .write = s3c24xx_leds_write,
 .ioctl = s3c24xx_leds_ioctl,
 //... 
};


static int __init s3c24xx_leds_init(void)
{
 dev_t dev_no = MKDEV(major, 0);
 int itmp = -1, ret = -1;
 unsigned long *pmem = NULL;
 
 devs = kmalloc(sizeof(struct s3c24xx_leds_dev), GFP_KERNEL);  //分配内存
 if (!devs) {
  ret = -ENOMEM;
  goto out;
 }

 itmp = register_chrdev(dev_no, "leds", &s3c24xx_leds_ops);  //注册
 if(itmp < 0) {
  ret = -ENODEV;
  goto out1;  
 }
 if( itmp > 0 )
  major = itmp;
 printk("major = %d\n", major);

 //地址映射
 pmem = ioremap(S3C24XX_LEDS_PHY_BASE, 12);
 if (!pmem) {
  ret = -ENOMEM;
  goto out2;
 }
 devs->pBase = pmem;   //地址关联
   
 return 0;
out2:
 unregister_chrdev(dev_no, "leds");
out1:
 kfree(devs);
out:
 return ret;
 
}
static void __exit s3c24xx_leds_exit(void)
{
 dev_t dev_no = MKDEV(major, 0);
 
 iounmap(devs->pBase);
 unregister_chrdev(dev_no, "leds");
 
 kfree(devs);
 devs = NULL;
}

module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("StephenYee([email protected])");

 

 

应用程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 #include <unistd.h>
   #include <sys/ioctl.h>

#define LEDS_NAME "/dev/leds"
 
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2
#define LEDS_NUM 4

int main (int argc, char **argv)
{
 int fd = -1, ret= -1;
 int i;
 int leds;
 unsigned char buf;
 fd = open (LEDS_NAME, O_RDWR);
 if (fd < 0) {
  printf ("Can't open %s\n", LEDS_NAME);
  return -1;
 }
 
 //读取当前LED状态
 ret = read(fd, &buf, 1);
 if (ret < 0) {
  printf ("Can't read %s\n", LEDS_NAME);
 }
        for (i = 0; i< LEDS_NUM; i++) {
     printf( "LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
 }
  
 while (1) {
   //通过IOCTL循环点D11
  ioctl (fd, CMD_LED_ON, 2);

   ret = read(fd, &buf, 1);
   for (i = 0; i< LEDS_NUM; i++) {
          printf( "1: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
         }
  sleep (1);
  ioctl (fd, CMD_LED_OFF, 2);
  
  
  ret = read(fd, &buf, 1);
                for (i = 0; i< LEDS_NUM; i++) {
                       printf( "2: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
                }

  sleep (1);

  ioctl (fd, CMD_LED_ON, 3);
  // Get state
  ret = ioctl (fd, CMD_LED_GET_STATE, &leds);
  
  leds &= 0x0f;
  printf("ret = %d, leds =%d\n",ret, leds);
  for (i = 0; i< LEDS_NUM; i++) {
                       printf( " 3: LED%d state is:%s\n", i+1, leds & (1<<i)? "On" :"Off");
    }
    sleep(1);
   
    //设置状态:     
    buf ^= (1<<3); //改变D11的状态
    write(fd, &buf, 1);
    sleep(1);
    //设置状态:     
    buf ^= (1<<1); //改变D11的状态
    write(fd, &buf, 1);
    }
    sleep(1);
 return 0;
}


 

 


 

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