LED驱动实例5(信号量+udev)

来源:互联网 发布:个性定制软件 编辑:程序博客网 时间:2024/04/27 17:08
 

#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>
#include <asm/semaphore.h>

#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2

#define LEDS_NUM 4


#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;
 //...
 //spinlock_t spin;
 struct semaphore sem;   //信号量
};
struct s3c24xx_leds_dev *devs =NULL;

static struct class *s3c24xx_leds_class;   //创建LED 类

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;
   
// spin_lock(&pdev->spin);
    down(&pdev->sem); //  该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用
    kbuff = pdev->led_state >>4;
//    spin_unlock(&pdev->spin);
    up(&pdev->sem);  //释放信号量

    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;
 if (copy_from_user(&kbuff, (void __user *)buf, 1))
 {
  return -EFAULT;
 }

    kbuff = kbuff<<4;
  
 //真正的LED操作
 // spin_lock(&pdev->spin);
    down(&pdev->sem);
 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)) ) { // orignal led state is on,now set to off
     writel( (readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0) | (1<< ( 4+i )), pdev->pBase+GPFDAT_OFFSET);
    } else {  // orignal led state is off,now set to on
     writel( ((readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0)) |  ~(1<< ( 4+i)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
    }
   }
  }
 }
 pdev->led_state = kbuff;

   //    spin_unlock(&pdev->spin);
    up(&pdev->sem);
   
    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:
  //...
    // spin_lock(&pdev->spin);
            down(&pdev->sem);
   // 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); 
        //    spin_unlock(&pdev->spin);
            up(&pdev->sem);
        break;

  case CMD_LED_OFF:
           // spin_lock(&pdev->spin);
              down(&pdev->sem);
   //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));
            //    spin_unlock(&pdev->spin);
             up(&pdev->sem);
  break;

  case CMD_LED_GET_STATE:
         // spin_lock(&pdev->spin);
             down(&pdev->sem);
   state =  pdev->led_state >>4;
            //    spin_unlock(&pdev->spin);
            up(&pdev->sem);
   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);

    s3c24xx_leds_class = class_create(THIS_MODULE, "leds");//在驱动初始化的代码里调用class_create为该设备创建一个class,
                                                                                            //再为每个设备调用 class_device_create创建对应的设备
    if(IS_ERR(s3c24xx_leds_class)) {
         printk("Err: failed in creating class.\n");
         ret = -EINVAL;
         goto out2;
    }

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
    class_device_create(s3c24xx_leds_class,MKDEV(major, 0), NULL, "leds");
#else
    device_create(s3c24xx_leds_class,NULL,MKDEV(major, 0), NULL, "leds");//在/dev目录下创建相应的设备节点
#endif

 //地址映射
 pmem = ioremap(S3C24XX_LEDS_PHY_BASE, 12);
 if (!pmem) {
  ret = -ENOMEM;
  goto out2;
 }
 devs->pBase = pmem;

  //  spin_lock_init(&devs->lock);

  //semaphore initialize.
//  sem_init(&devs->sem, 1);
 init_MUTEX(&devs->sem);
   
 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);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
   class_device_destroy(s3c24xx_leds_class, MKDEV(major, 0));
#else
   device_destroy(s3c24xx_leds_class, MKDEV(major, 0));
#endif   
 
    class_destroy(s3c24xx_leds_class);
   
 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(stephenyee@farsight.com.cn)");

 

应用程序:

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


 

原创粉丝点击