嵌入式linux:阻塞与非阻塞驱动

编写了一个虚拟的驱动,实现的功能是在读设备时阻塞,直到有数据写入设备,然后才能读出写入的数据。

其中有信号量的操作与阻塞非阻塞IO的操作,

最后写了一个应用程序进行验证

驱动如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*for spinlock and semaphore*/
#include 
#include 
#include 
 
/*for task management*/
#include 
#include 
#include 

#define DEVICE_NAME "pcio"
#define DRIVER_NAME "mypcio"//加载驱动之后会在/dev/目录下发现mypcio,应用程序可以使用

#define PCIO_MAJOR 0 //预设的pcio的主设备号


static int pcio_major = PCIO_MAJOR;
static int pcioOpenCount=0;
static int flag=0;
static int global_var = 0;

struct class *pcio_class;
static struct device *pcioDevice=NULL;

static wait_queue_head_t wqueue;


struct pcio_dev
{
   struct cdev cdev;//cdev结构体
   //信号量
   struct semaphore sem;
   //等待队列头
   //struct wait_queue_head_t* wqueue;
   //unsigned char mem[MYKEY_SIZE];//全局内存
};
struct pcio_dev *pcio_devp;//设备结构体指针

//打开函数
static int pcio_open(struct inode *inode, struct file *file)
{
   pcioOpenCount++;
   return 0;
}
//关闭函数
static int pcio_close(struct inode *inode, struct file *file)
{
   pcioOpenCount--;
   return 0;
}
//读函数
static ssize_t pcio_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
   //等待事件
   if(wait_event_interruptible(wqueue,flag!=0))
   {
      return -ERESTARTSYS;
   }
   //获取信号量
   if(down_interruptible(&pcio_devp->sem))
   {
      return -ERESTARTSYS;
   }
   //从内核空间到用户空间的数据拷贝
   if(copy_to_user(buff,&global_var,sizeof(int)) != 0)
   {
      /*release semaphore*/
      up(&pcio_devp->sem);
      return -EFAULT;
   }
   /*data unaccessible flag*/
   flag = 0;
     
   /*release semaphore*/
   up(&pcio_devp->sem);
   return sizeof(int);
}
//写函数
static ssize_t pcio_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
   /*get semaphore*/
    if(down_interruptible(&pcio_devp->sem))
   {
      return -ERESTARTSYS;
   }
     
   printk("down_interruptible ok!\n");
   if(copy_from_user(&global_var,buff,sizeof(int) != 0))
   {
      /*release semaphore*/
      up(&pcio_devp->sem);
      return -EFAULT;
   }
 
   /*release semaphore*/
   up(&pcio_devp->sem);
 
   /*data ready*/
   flag = 1;
   /*wake up the waiting task*/
   wake_up_interruptible(&wqueue);
   return sizeof(int);
}


 //dev_fops操作指令集
 static struct file_operations pcio_fops =
 {
   .owner =THIS_MODULE,
   .open    =   pcio_open,
   .release =   pcio_close,
   .write   =   pcio_write,
   .read    =   pcio_read,
   //.poll    =   pcio_poll,
   //.unlocked_ioctl = my2416_keys_ioctl,//这里必须是unlocked_ioctl而不是ioctl。
 };
 /*//第三步:混杂设备定义
 static struct miscdevice my2416Ledmisc =
  {
      .minor = MISC_DYNAMIC_MINOR,
      .name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现mykeys,应用程序可以使用
      .fops = &pcio_fops,
 };
*/

static void Pcio_setup_cdev(struct pcio_dev *dev,int index)
{
    int err, devno = MKDEV(pcio_major,index);
    /*初始化cdev,并将相关的文件操作添加进来*/
    cdev_init(&dev->cdev, &pcio_fops);
    dev->cdev.owner = THIS_MODULE;
    //dev->cdev.ops   = &pcio_fops;
    /*注册字符设备*/
    err = cdev_add(&dev->cdev, devno, 1);

    if (err)
        printk("Error %d\n", err);
    else
        printk("have finish add\n");
}

/*驱动模块加载*/
static int pcio_dev_init(void)
{
   int ret;
   dev_t devno;
   printk("DEV:register pcio\n");
   /*创建一个设备号*/
   devno=MKDEV(pcio_major,0);

   /*注册一个设备号*/
   /*如果定义了主设备号采用静态申请的方式*/
   if(pcio_major)
   {
      ret=register_chrdev_region(devno,1,DEVICE_NAME);
   }
   else//动态申请设备号
   {
      ret= alloc_chrdev_region(&devno,0,1,DEVICE_NAME);
      pcio_major=MAJOR(devno);
   }
   if(ret<0)
   {
      printk (DEVICE_NAME " DEV:can't register\n");
      return ret;
   }

   //动态申请设备结构体内存
   pcio_devp=kmalloc(sizeof(struct pcio_dev), GFP_KERNEL);
   if(!pcio_devp)//申请失败
   {
      printk("DEV:kmalloc faile\n");
      ret=-ENOMEM;
      goto fail_malloc;
   }
   printk("DEV:kmalloc succeed\n");
  /*清除空间*/
   memset(pcio_devp,0,sizeof(struct pcio_dev));

   /*创建一个设备*/
   Pcio_setup_cdev(pcio_devp,0);

   //class_create和device_create函数是为了自动在/dev下创建DRIVER_NAME设备文件。
   //创建一个类,这个类存放于sysfs下面
   pcio_class=class_create(THIS_MODULE,DRIVER_NAME);
   if(IS_ERR(pcio_class))
   {
	ret = PTR_ERR(pcio_class);
	printk("DEV:class create faikey\n");
	goto class_create_fail;
   }
   //在/dev目录下创建相应的设备节点
   pcioDevice = device_create(pcio_class,NULL,devno,NULL,DRIVER_NAME);

   if(IS_ERR(pcioDevice))
   {
	ret = PTR_ERR(pcioDevice);
	printk("DEV:device_create faile\n");
	goto device_create_faile;
   }
   //初始化信号量
   sema_init(&pcio_devp->sem,1);
   //初始化等待队列
   init_waitqueue_head(&wqueue);

fail_malloc:
   unregister_chrdev_region(devno,1);//释放设备号
class_create_fail:
   unregister_chrdev_region(MKDEV(pcio_major, 0), 1);//释放设备号
device_create_faile:
   class_destroy(pcio_class);/*注销创建的设备类*/

   return ret;
}

static void __exit pcio_dev_exit(void)
{
   /*注销设备*/
   device_destroy(pcio_class,MKDEV(pcio_major, 0));
    /*注销创建的设备类*/
   class_destroy(pcio_class);
    /*字符设备注销*/
   cdev_del(&pcio_devp->cdev);//注销cdev
   kfree(pcio_devp);/*释放设备结构体内存*/
   unregister_chrdev_region(MKDEV(pcio_major, 0), 1);//释放设备号
   printk(DEVICE_NAME " exit\n");
}

module_init(pcio_dev_init);
module_exit(pcio_dev_exit);

MODULE_AUTHOR("Zhao Yidong ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A sampleIO module");
MODULE_ALIAS("a sample module");
相应的makefile文件如下:

#Makefile for PCIOModule.c.c
#ARCH=arm
#CROSS_COMPILE=arm-linux-gnueabihf-
ifneq ($(KERNELRELEASE),)
	obj-m := PCIOModule.o
else
#bbblack kernel
#KERNELDIR ?= /usr/local/ti-sdk-beagleboard/board-support/linux-3.3.7
#PC kernel
KERNELDIR ?=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
app: app.c
	$(CROSS_COMPILE)gcc -o app app.c
clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif

测试的应用程序如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*
 */
int main(int argc, char** args) {
    int fd, num;

    if (argc >= 2) {
        if (strcmp(args[1], "0") == 0) {
            printf("mode read!\n");
            /*opemn device*/
            fd = open("/dev/mypcio", O_RDWR, S_IRUSR | S_IWUSR);
            if (fd != -1) {
                while (1) {
                    read(fd, &num, sizeof(int));//阻塞的读取,直到有数据才返回
                    printf("mypcio=%d\n",num);
                    if (num == 0) {
                        close(fd);
                        break;
                    }
                }//while
            } else {
                printf("error:device open error!\n");
            }
        } else if (strcmp(args[1], "1") == 0) {
            printf("mode write!\n");

            /*opemn device*/
            fd = open("/dev/mypcio", O_RDWR, S_IRUSR | S_IWUSR);
            if (fd != -1) {
                while (1) {
                    /*writing test*/
                    printf("print number to write: ");
                    scanf("%d", &num);
                    write(fd, &num, sizeof(int));
                    if (num == 0) {
                        close(fd);
                        break;
                    }
                }
            } else {
                printf("error:device open error!\n");
            }
        }
    }

    return 0;
}

最后打开两个终端,运行测试程序如下图,每次在写终端写入一个数,在读取程序终端就会看到写入的数据。

嵌入式linux:阻塞与非阻塞驱动_第1张图片


你可能感兴趣的:(嵌入式linux:阻塞与非阻塞驱动)