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;
}


 

 


 

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