ARM-第八次

1.主设备号:表示某类设备
2.次设备号:表示某个设备

文件描述符fd

  • 通过文件描述符的下标得到文件指针,通过文件指针的地址,访问文件信息得到(文件标志,当前位置偏移量,v节点指针),调用一次则占用一个文件下标即一个文件信息

驱动函数


C库:  fopen fclose fread fwrite fseek ftell

系统
调用  open  close  read  write  lseek  lseek

驱动
程序  open release read write  llseek  llseek(驱动程序里完成核心操作,辅助操作系统代替完成)

驱动函数的代码

/*======================================================================
    A globalmem driver as an example of char device drivers  
   
    The initial developer of the original code is Hiro Wang
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#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/system.h>
#include <asm/uaccess.h>

#include <linux/slab.h>

#define GLOBALMEM_SIZE  0x1000  /*全局内存最大4K字节*/
#define MEM_CLEAR 0x1  /*清0全局内存*/
#define GLOBALMEM_MAJOR 288    /*预设的globalmem的主设备号*/

static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct globalmem_dev                                     
{                                                        
  struct cdev cdev; /*cdev结构体*/                       
  unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/        
};

struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)//参数是系统调用open()函数时,内核空间执行的辅助代码传给驱动函数open
{
  /*将设备结构体指针赋值给文件私有数据指针*/
  filp->private_data = globalmem_devp;
  return 0;
}
/*文件释放函数*/
int globalmem_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctl设备控制函数 */
/* old version
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)*/
  //可以去操作状态寄存器与控制寄存器(read,write只能操作数据寄存器,想要得到状态,或者修改运行状态只能用ioctl驱动函数)
static long globalmem_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/

  switch (cmd)//请求做什么
  {
    case MEM_CLEAR://对那个空间做清空操作
      memset(dev->mem, 0, GLOBALMEM_SIZE);      
      filp->f_pos = 0;
      printk(KERN_INFO "globalmem is set to zero\n");
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

/*读函数*/
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)//第一个参数:文件描述符,第二个参数:buf里面保存的是用户空间的地址,第三个参数:读size个字节,第四个参数:当前位置
{
  unsigned long p =  *ppos;//当前位置,在文件里多少字节
  unsigned int count = size;
  int ret = 0;
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

  /*分析和获取有效的写长度*/
  if (p >= GLOBALMEM_SIZE)//表示p指向的全局内存外面去了,越界,即是异常判断
    return count ?  - ENXIO: 0;
  if (count > GLOBALMEM_SIZE - p)//期望读的字节数大与还剩下的字节数
    count = GLOBALMEM_SIZE - p;//

  /*内核空间->用户空间*/
  if (copy_to_user(buf, (void*)(dev->mem + p), count))//从p指向的当前位置开始读内核空间地址的count字节的内容读用户空间
  {
    ret =  - EFAULT;//失败,返回一个非法值
  }
  else
  {
    *ppos += count;//
    ret = count;//
    
    printk(KERN_INFO "read %d bytes(s) from %d\n", count, (int)p);
  }

  return ret;
}

/*写函数*/
static ssize_t globalmem_write(struct file *filp, const char __user *buf,
  size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  
  /*分析和获取有效的写长度*/
  if (p >= GLOBALMEM_SIZE)
    return count ?  - ENXIO: 0;
  if (count > GLOBALMEM_SIZE - p)
    count = GLOBALMEM_SIZE - p;
    
  /*用户空间->内核空间*/
  if (copy_from_user(dev->mem + p, buf, count))//把用户空间里的内容count个字节写到内核空间里去
    ret =  - EFAULT;
  else
  {
    *ppos += count;//当前位置指示器要变大
    ret = count;//实际写的字节数
    
    printk(KERN_INFO "written %d bytes(s) from %d\n", count, (int)p);
  }

  return ret;
}

/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{//orig:
  loff_t ret = 0;
  switch (orig)
  {
    //文件头开始偏移
    case 0:   /*相对文件开始位置偏移*/
      if (offset < 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((unsigned int)offset > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      //当前位置偏移量等于要偏移的字节数
      filp->f_pos = (unsigned int)offset;
      ret = filp->f_pos;//返回实际偏移的数
      break;
    case 1:   /*相对文件当前位置偏移*/
      if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      if ((filp->f_pos + offset) < 0)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos += offset;
      ret = filp->f_pos;
      break;
    case 2:   /*相对文件尾位置偏移*/
      if (offset > 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((0-offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos = GLOBALMEM_SIZE + offset;
      ret = filp->f_pos;
      break;
    default:
      ret =  - EINVAL;
      break;
  }
  return ret;
}

/*文件操作结构体*/
static const struct file_operations globalmem_fops =
{//const表示只读变量
//以下是内核驱动函数
  .owner = THIS_MODULE,//固定填THIS
  .llseek = globalmem_llseek,
  .read = globalmem_read,
  .write = globalmem_write,
  .unlocked_ioctl = globalmem_ioctl,
  .open = globalmem_open,
  .release = globalmem_release,//相当于close
};

/*初始化并注册cdev*/
//传进来一个指针和此设备号
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
  int err, devno = MKDEV(globalmem_major, index);//将设备加入内核管理

  //cdev_init第一个传的结构体第一个成员的地址
  //第二个是一个结构体类型变量的初始化
  //有了结构体变量的首地址,就能查看别的所有的成员内容
  cdev_init(&dev->cdev, &globalmem_fops);//将fops里的对象的指针赋值给dev_cdev,open之后,找到与打开的设备号相同的设备
  dev->cdev.owner = THIS_MODULE;
 // dev->cdev.ops = &globalmem_fops;
  err = cdev_add(&dev->cdev, devno, 1);//添加到字符设备链表里面
  if (err)
    printk(KERN_NOTICE "Error %d when adding cdev %d", err, index);
}

/*设备驱动模块加载函数*/
static int __init globalmem_init(void)
{
  int result = -1;
  dev_t devno = MKDEV(globalmem_major, 0);//globalmem_major:主设备号,0:次设备号,dev_t:整型

  /* 申请设备号*/
  if (globalmem_major)//向内核提问,这个设备号是否已经被占用了,如果占用了就失败了,没有占用我就占用成功
  {
    result = register_chrdev_region(devno, 1, "globalmem");//在第一个目录下有一个设备文件,名字为globalmem
  }
  if(result < 0)  /* 动态申请设备号 */
  {//占用了,就向内核提问,有没有空的主设备号
    result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
    globalmem_major = MAJOR(devno);//将主设备号放到globalmem_major中
  }  
  if (result < 0)
    return result;
    
  /* 动态申请设备结构体的内存*/
  globalmem_devp = (struct globalmem_dev *)kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);//kmalloc内核不能用C库的malloc,只能用kmalloc,需要空间只是一小块是就用GFP_KERNEL
  if (!globalmem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(globalmem_devp, 0, sizeof(struct globalmem_dev));//空间里是不确定的内容,所以要memset清除
  
  globalmem_setup_cdev(globalmem_devp, 0);
  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return result;
}

/*模块卸载函数*/
static void __exit globalmem_exit(void)
{
  cdev_del(&globalmem_devp->cdev);   /*注销cdev*/
  kfree(globalmem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);

测试驱动函数的代码

/*包含操作系统的头文件*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

/*包含C库的头文件*/
#include <stdio.h>

/*包含第三方库的头文件*/


/*包含自定义的头文件*/

int main(void)
{
    int fd=-1;
    char buf[20]="1234567890123456789";
    char outbuf[20]="";
    fd=open("/dev/globalmem",O_RDWR);
    if(fd<0)
    {   
        printf("Open failed\n");
        return 1;
    }
    write(fd,buf,20);
    lseek(fd,0,SEEK_SET);
    read(fd,outbuf,20);
    printf("Outbuf:%s\n",outbuf);

    ioctl(fd,1,0);

    write(fd,"hello",6);
    lseek(fd,0,SEEK_SET);
    read(fd,outbuf,6);
    printf("Outbuf:%s\n",outbuf);

    close(fd);
    return 0;
}

  • 运行结果,首先将globalmem驱动函数下载到开发板的
Paste_Image.png
  • 运行结果,再者将写好的测试驱动程序的代码编译,如下编译,-Wall,能很好的把一些看不到的警告显示出来
Paste_Image.png
  • 运行结果,再将测试代码下载到开发板里,结果出现顺序乱掉是因为驱动程序里的printk函数的优先级比printf的优先级高
Paste_Image.png
Paste_Image.png

1.以上的代码的问题:读和写只能非阻塞,不能阻塞(即程序不能进行睡眠)
2.如果同时打开两个设备文件的话,则会发生进程问题

升级版

1.驱动函数

/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#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/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000  /*È«ŸÖfifo×îŽó4K×ÖœÚ*/
#define FIFO_CLEAR 0x1  /*Çå0È«ŸÖÄÚŽæµÄ³€¶È*/
#define GLOBALFIFO_MAJOR 253    /*Ô€ÉèµÄglobalfifoµÄÖ÷É豞ºÅ*/

static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoÉ豞œá¹¹Ìå*/
struct globalfifo_dev                                     
{                                                        
  struct cdev cdev; /*cdevœá¹¹Ìå*/                       
  unsigned int current_len;    /*¿¿¿¿*/
  unsigned char mem[GLOBALFIFO_SIZE]; /*¿¿¿¿*/        
  wait_queue_head_t r_wait; /*¿¿¿¿¿*/     
  wait_queue_head_t w_wait; /*¿¿¿¿¿*/     
};

struct globalfifo_dev *globalfifo_devp; /*É豞œá¹¹ÌåÖžÕë*/
/*ÎÄŒþŽò¿ªº¯Êý*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
  /*œ«É豞œá¹¹ÌåÖžÕëž³ÖµžøÎÄŒþËœÓÐÊýŸÝÖžÕë*/
  filp->private_data = globalfifo_devp;
  return 0;
}
/*ÎÄŒþÊͷź¯Êý*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctlÉ豞¿ØÖƺ¯Êý */
static long globalfifo_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalfifo_dev *dev = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/

  switch (cmd)
  {
    case FIFO_CLEAR:
      dev->current_len = 0;
      memset(dev->mem,0,GLOBALFIFO_SIZE);
      printk(KERN_INFO "globalfifo is set to zero\n");      
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
  unsigned int mask = 0;
  struct globalfifo_dev *dev = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
  
  poll_wait(filp, &dev->r_wait, wait);
  poll_wait(filp, &dev->w_wait, wait);  
  /*fifo·Ç¿Õ*/
  if (dev->current_len != 0)//¿¿¿¿¿¿¿0
  {
    mask |= POLLIN | POLLRDNORM;//¿¿¿¿|¿¿
  }
  /*fifo·ÇÂú*/
  if (dev->current_len != GLOBALFIFO_SIZE)//¿¿¿¿¿¿¿¿¿¿¿
  {
    mask |= POLLOUT | POLLWRNORM; /*¿¿¿¿|¿¿*/
  }
     
  return mask;
}


/*globalfifo¶Áº¯Êý*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
  loff_t *ppos)
{
  int ret;
  struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
  DECLARE_WAITQUEUE(wait, current); //¿¿¿¿¿¿,¿¿¿THis¿¿,¿¿¿¿wait¿¿

  add_wait_queue(&dev->r_wait, &wait); //¿¿¿¿¿¿¿¿¿¿

  /**/
  //printk("Driver:"KERN_INFO"Have no data in device!\n");
  //¿¿¿¿¿1,¿¿¿¿¿¿,¿¿¿0,¿¿¿¿¿
  //¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿0,¿¿¿¿¿¿,¿read¿¿¿¿¿¿
  if((filp->f_flags & O_NONBLOCK) && dev->current_len == 0){
     //printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
     ret =  - EAGAIN;
     //¿¿¿¿¿¿¿goto
     goto out;
     //¿¿¿¿¿¿¿,¿¿¿¿¿¿¿,¿¿¿¿¿
  }else if(!(filp->f_flags & O_NONBLOCK)){
     //printk("Driver:"KERN_INFO"device is set as BLOACK!\n");
     //printk("Driver:"KERN_INFO"Before schedule function!\n");
     //¿¿¿¿¿¿¿¿¿,¿¿¿¿,interruptible¿¿¿¿,¿¿dev->current_len>0¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿write¿¿,¿¿dev_current_len¿¿¿¿¿¿¿¿
     wait_event_interruptible(dev->r_wait,dev->current_len > 0);
     //printk("Driver:"KERN_INFO"After schedule function!\n");
     if (signal_pending(current))//¿¿¿¿¿¿¿¿¿¿¿¿,¿
     //Èç¹ûÊÇÒòΪÐźŻœÐÑ
     {
        ret =  - ERESTARTSYS;
        goto out;
     }
  }
  //printk("Driver:"KERN_INFO"Start read data!\n");

  /*¿¿¿¿¿¿¿¿¿¿¿*/
  if (count > dev->current_len)
    count = dev->current_len;//¿¿¿¿¿¿¿¿¿

  if (copy_to_user(buf, dev->mem, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    //¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿
    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoÊýŸÝÇ°ÒÆ
    dev->current_len -= count; //ÓÐЧÊýŸÝ³€¶ÈŒõÉÙ
    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
     
    wake_up_interruptible(&dev->w_wait); //¿¿¿¿¿¿¿,¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿
    ret = count;
  }
out:
   remove_wait_queue(&dev->r_wait, &wait); //
   set_current_state(TASK_RUNNING);//¿¿¿¿¿¿¿¿¿¿¿¿¿
   return ret;
}


/*globalfifoÐŽ²Ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
  size_t count, loff_t *ppos)
{
  struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
  int ret;
  DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ
  add_wait_queue(&dev->w_wait, &wait); //œøÈëÐŽµÈŽý¶ÓÁÐÍ·
  if ((filp->f_flags & O_NONBLOCK)&& (dev->current_len >= GLOBALFIFO_SIZE))
  {
     ret =  - EAGAIN;
     goto out;
  }
  else if(!(filp->f_flags & O_NONBLOCK))
  {
    //¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿,¿¿¿¿¿¿¿¿¿
     wait_event_interruptible(dev->w_wait,dev->current_len < GLOBALFIFO_SIZE);
     if (signal_pending(current))//¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿
     {
        ret =  - ERESTARTSYS;
        goto out;
     }
  }

  /**/
  if (count > GLOBALFIFO_SIZE - dev->current_len)
    count = GLOBALFIFO_SIZE - dev->current_len;
  //¿¿¿¿¿¿¿
  if (copy_from_user(dev->mem + dev->current_len, buf, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    dev->current_len += count;
    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
      ->current_len);

    wake_up_interruptible(&dev->r_wait); //¿¿¿¿¿¿
    
    ret = count;//¿¿¿¿¿¿¿¿¿
  }

  out: 
  remove_wait_queue(&dev->w_wait, &wait); //
  set_current_state(TASK_RUNNING);//¿¿¿¿¿¿¿¿¿¿¿¿¿
  return ret;
}


/*ÎÄŒþ²Ù×÷œá¹¹Ìå*/
static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  .unlocked_ioctl = globalfifo_ioctl,
  .poll = globalfifo_poll,//¿¿¿¿¿¿¿¿
  .open = globalfifo_open,
  .release = globalfifo_release,
};

/*³õÊŒ»¯²¢×¢²ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
  int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
//  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*É豞Çý¶¯Ä£¿éŒÓÔغ¯Êý*/
static __init int globalfifo_init(void)
{
  int ret = 0;
  dev_t devno = MKDEV(globalfifo_major, 0);

  /* ÉêÇëÉ豞ºÅ*/
  if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "globalfifo");
  if(ret < 0)  /* ¶¯Ì¬ÉêÇëÉ豞ºÅ */
  {
    ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    globalfifo_major = MAJOR(devno);
  }
  if (ret < 0)
    return ret;
  /* ¶¯Ì¬ÉêÇëÉ豞œá¹¹ÌåµÄÄÚŽæ*/
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if (!globalfifo_devp)    /*ÉêÇëʧ°Ü*/
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }

  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

  init_waitqueue_head(&globalfifo_devp->r_wait); /*¿¿¿¿¿¿¿*/
  init_waitqueue_head(&globalfifo_devp->w_wait); /**/

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}


/*Ä£¿éжÔغ¯Êý*/
static __exit void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev);   /*×¢Ïúcdev*/
  kfree(globalfifo_devp);     /*ÊÍ·ÅÉ豞œá¹¹ÌåÄÚŽæ*/
  unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*ÊÍ·ÅÉ豞ºÅ*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit);

数据结构

  • 对同类型的元素进行管理的学科
    1.元素之间的逻辑关系(1)同属一个集合 (2)元素之间存在一对一的关系:线性关系,线性表(栈(stack):先进后出(在一端做插入,同一端做删除),队列(queue):先进先出(在一端做插入,在另一端做删除)) (3)一对多的关系:树形关系 (4)多对多的关系:图形关系
    2.元素之间的存储方式(1)顺序存储(数组定义,动态分配)(2)链式存储(节点的空间只能动态分配)

select(多功能复用)

最终版

1.驱动函数

/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#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/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000  /*È«ŸÖfifo×îŽó4K×ÖœÚ*/
#define FIFO_CLEAR 0x1  /*Çå0È«ŸÖÄÚŽæµÄ³€¶È*/
#define GLOBALFIFO_MAJOR 253    /*Ô€ÉèµÄglobalfifoµÄÖ÷É豞ºÅ*/

static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoÉ豞œá¹¹Ìå*/
struct globalfifo_dev                                     
{                                                        
  struct cdev cdev; /*cdevœá¹¹Ìå*/                       
  unsigned int current_len;    /*fifoÓÐЧÊýŸÝ³€¶È*/
  unsigned char mem[GLOBALFIFO_SIZE]; /*È«ŸÖÄÚŽæ*/        
  struct semaphore sem; /*²¢·¢¿ØÖÆÓõÄÐźÅÁ¿*/           
  wait_queue_head_t r_wait; /*×èÈû¶ÁÓõĵȎý¶ÓÁÐÍ·*/     
  wait_queue_head_t w_wait; /*×èÈûÐŽÓõĵȎý¶ÓÁÐÍ·*/     
};

struct globalfifo_dev *globalfifo_devp; /*É豞œá¹¹ÌåÖžÕë*/
/*ÎÄŒþŽò¿ªº¯Êý*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
  /*œ«É豞œá¹¹ÌåÖžÕëž³ÖµžøÎÄŒþËœÓÐÊýŸÝÖžÕë*/
  filp->private_data = globalfifo_devp;
  return 0;
}
/*ÎÄŒþÊͷź¯Êý*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctlÉ豞¿ØÖƺ¯Êý */
static long globalfifo_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalfifo_dev *dev = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/

  switch (cmd)
  {
    case FIFO_CLEAR:
        down(&dev->sem); //¿¿¿¿p¿¿      
      dev->current_len = 0;
      memset(dev->mem,0,GLOBALFIFO_SIZE);//¿¿¿¿¿¿
      up(&dev->sem); //¿¿¿¿v¿¿
         
      printk(KERN_INFO "globalfifo is set to zero\n");      
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
  unsigned int mask = 0;
  struct globalfifo_dev *dev = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
  
  down(&dev->sem);
  
  poll_wait(filp, &dev->r_wait, wait);
  poll_wait(filp, &dev->w_wait, wait);  
  /*fifo·Ç¿Õ*/
  if (dev->current_len != 0)
  {
    mask |= POLLIN | POLLRDNORM; /*±êÊŸÊýŸÝ¿É»ñµÃ*/
  }
  /*fifo·ÇÂú*/
  if (dev->current_len != GLOBALFIFO_SIZE)
  {
    mask |= POLLOUT | POLLWRNORM; /*±êÊŸÊýŸÝ¿ÉÐŽÈë*/
  }
     
  up(&dev->sem);
  return mask;
}


/*globalfifo¶Áº¯Êý*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
  loff_t *ppos)
{
  int ret;
  struct globalfifo_dev *dev = filp->private_data; //
  DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ

  down(&dev->sem); //p¿¿,¿¿¿¿p¿¿¿¿¿¿¿,¿¿¿¿
  add_wait_queue(&dev->r_wait, &wait); //œøÈë¶ÁµÈŽý¶ÓÁÐÍ·

  /* µÈŽýFIFO·Ç¿Õ */
  if (dev->current_len == 0)//¿¿¿¿¿¿
  {
    printk("Driver:"KERN_INFO"Have no data in device!\n");
    if (filp->f_flags &O_NONBLOCK)//¿¿¿¿¿¿¿¿¿¿
    {
      printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
      ret =  - EAGAIN;
      goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //¿¿¿¿¿¿¿¿¿¿¿¿
    up(&dev->sem);//¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿

    printk("Driver:"KERN_INFO"device is set as BLOCK!\n");
    printk("Driver:"KERN_INFO"Before schedule function!\n");

    schedule(); //¿¿¿¿¿¿¿¿¿
    printk("Driver:"KERN_INFO"After schedule function!\n");
    if (signal_pending(current))//¿¿¿¿¿¿¿¿¿
    //Èç¹ûÊÇÒòΪÐźŻœÐÑ
    {
      ret =  - ERESTARTSYS;
      goto out2;
    }

    down(&dev->sem);//¿¿p¿¿
  }
  printk("Driver:"KERN_INFO"Start read data!\n");

  /* ¿œ±ŽµœÓû§¿ÕŒä */
  if (count > dev->current_len)
    count = dev->current_len;

  if (copy_to_user(buf, dev->mem, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoÊýŸÝÇ°ÒÆ
    dev->current_len -= count; //ÓÐЧÊýŸÝ³€¶ÈŒõÉÙ
    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
     
    wake_up_interruptible(&dev->w_wait); //»œÐÑÐŽµÈŽý¶ÓÁÐ
    
    ret = count;
  }
  out: up(&dev->sem); //ÊÍ·ÅÐźÅÁ¿
  out2:remove_wait_queue(&dev->w_wait, &wait); //ŽÓžœÊôµÄµÈŽý¶ÓÁÐÍ·ÒƳý
  set_current_state(TASK_RUNNING);
  return ret;
}


/*globalfifoÐŽ²Ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
  size_t count, loff_t *ppos)
{
  struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
  int ret;
  DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ

  down(&dev->sem); //»ñÈ¡ÐźÅÁ¿
  add_wait_queue(&dev->w_wait, &wait); //œøÈëÐŽµÈŽý¶ÓÁÐÍ·

  /* µÈŽýFIFO·ÇÂú */
  if (dev->current_len == GLOBALFIFO_SIZE)
  {
    if (filp->f_flags &O_NONBLOCK)
    //Èç¹ûÊÇ·Ç×èÈû·ÃÎÊ
    {
      ret =  - EAGAIN;
      goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //žÄ±äœø³Ì׎̬Ϊ˯Ãß
    up(&dev->sem);

    schedule(); //µ÷¶ÈÆäËûœø³ÌÖŽÐÐ
    if (signal_pending(current))
    //Èç¹ûÊÇÒòΪÐźŻœÐÑ
    {
      ret =  - ERESTARTSYS;
      goto out2;
    }

    down(&dev->sem); //»ñµÃÐźÅÁ¿
  }

  /*ŽÓÓû§¿ÕŒä¿œ±ŽµœÄں˿Ռä*/
  if (count > GLOBALFIFO_SIZE - dev->current_len)
    count = GLOBALFIFO_SIZE - dev->current_len;

  if (copy_from_user(dev->mem + dev->current_len, buf, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    dev->current_len += count;
    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
      ->current_len);

    wake_up_interruptible(&dev->r_wait); //»œÐѶÁµÈŽý¶ÓÁÐ
    
    ret = count;
  }

  out: up(&dev->sem); //ÊÍ·ÅÐźÅÁ¿
  out2:remove_wait_queue(&dev->w_wait, &wait); //ŽÓžœÊôµÄµÈŽý¶ÓÁÐÍ·ÒƳý
  set_current_state(TASK_RUNNING);
  return ret;
}


/*ÎÄŒþ²Ù×÷œá¹¹Ìå*/
static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  .unlocked_ioctl = globalfifo_ioctl,
  .poll = globalfifo_poll,
  .open = globalfifo_open,
  .release = globalfifo_release,
};

/*³õÊŒ»¯²¢×¢²ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
  int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*É豞Çý¶¯Ä£¿éŒÓÔغ¯Êý*/
static __init int globalfifo_init(void)
{
  int ret = 0;
  dev_t devno = MKDEV(globalfifo_major, 0);

  /* ÉêÇëÉ豞ºÅ*/
  if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "globalfifo");
  if(ret < 0)  /* ¶¯Ì¬ÉêÇëÉ豞ºÅ */
  {
    ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    globalfifo_major = MAJOR(devno);
  }
  if (ret < 0)
    return ret;
  /* ¶¯Ì¬ÉêÇëÉ豞œá¹¹ÌåµÄÄÚŽæ*/
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if (!globalfifo_devp)    /*ÉêÇëʧ°Ü*/
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }

  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

  sema_init(&globalfifo_devp->sem,1);   /*³õÊŒ»¯ÐźÅÁ¿*/
  init_waitqueue_head(&globalfifo_devp->r_wait); /*³õÊŒ»¯¶ÁµÈŽý¶ÓÁÐÍ·*/
  init_waitqueue_head(&globalfifo_devp->w_wait); /*³õÊŒ»¯ÐŽµÈŽý¶ÓÁÐÍ·*/

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}


/*Ä£¿éжÔغ¯Êý*/
static __exit void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev);   /*×¢Ïúcdev*/
  kfree(globalfifo_devp);     /*ÊÍ·ÅÉ豞œá¹¹ÌåÄÚŽæ*/
  unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*ÊÍ·ÅÉ豞ºÅ*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit);
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

1.int nfds的值为:最大的文件描述符的下标+1,即为有几个文件描述符
2.fd_set:是一个结构体的类型

  • 简单代替读写操作的命令
    (1) cat /dev/设备文件/ (读操作)
    (2)sudo echo afghajhi > /dev/globalfifo (写操作)
    (3)select 则要写测试代码(调用select函数)
open 设备文件
while(1)
{
    FD_CLR
    FD_SET
    select();//返回值为设备文件有数据可读的个数
    if(FD_ISSET())
    {
    }
    if(FD_ISSET())
    {
    }
}
判断返回值(<0错误,>0正确,=0)
  • 测试驱动程序的读和写,运行结果为如下
Paste_Image.png
Paste_Image.png

LED驱动函数

1.GPIO:IO口,通用输入/输出

驱动函数

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME "leds"

static unsigned long led_table [] = {
    S3C2410_GPB(5),
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
};

static unsigned int led_cfg_table [] = {
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
};

static int sbc2440_leds_ioctl(
    struct inode *inode, 
    struct file *file, 
    unsigned int cmd, 
    unsigned long arg)
{
    switch(cmd) {
    case 0:
    case 1:
        if (arg > 4) {
            return -EINVAL;
        }
        s3c2410_gpio_setpin(led_table[arg], !cmd);
        return 0;
    default:
        return -EINVAL;
    }
}

static struct file_operations dev_fops = {
    .owner  =   THIS_MODULE,
    .ioctl  =   sbc2440_leds_ioctl,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};

static int __init dev_init(void)
{
    int ret;

    int i;
    
    for (i = 0; i < 4; i++) {
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
        s3c2410_gpio_setpin(led_table[i], 0);
    }

    ret = misc_register(&misc);

    printk (DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit dev_exit(void)
{
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

测试代码

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

int main(int argc,char *argv[])
{
    int cmd=-1;
    int ledno=-1;
    int fd=-1;
    if(argc<3)
    {
        printf("Usage is testleds 0 2\n");
        return 1;
    }
    sscanf(argv[1],"%d",&cmd);
    sscanf(argv[2],"%d",&ledno);
    if(cmd<0 ||cmd>1)
    {   
        printf("The second arguments is error\n");
        return 2;
    }
    if(ledno<1||ledno>4)
    {
        printf("The third arguments is error\n");
        return 3;
    }
    ledno--;
    fd=open("/dev/leds",O_RDWR);
    if(fd<0)
    {
        printf("Open /dev/leds failed\n");
        return 4;
    }
    ioctl(fd,cmd,ledno);
    close(fd);
    return 0;
}

  • 运行结果为
Paste_Image.png

volatile

1.是可变的关键字

  • 一个整型变量,如果没有前面的关键字,编译器可以会用寄存器去访问,提高效率,但当另一个进程要进行修改这个变量时,此时这两个得到的值就不同,所以不要编译器帮我们进行优化,此时要加volatile关键字
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容