Chinaunix首页 | 论坛 | 博客
  • 博客访问: 275074
  • 博文数量: 74
  • 博客积分: 1336
  • 博客等级: 中尉
  • 技术积分: 1057
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-02 09:33
文章分类

全部博文(74)

文章存档

2016年(6)

2015年(4)

2014年(20)

2013年(8)

2012年(16)

2010年(9)

2009年(11)

我的朋友

分类: LINUX

2012-08-03 17:48:45

1IOCTL简介

大部分驱动除了需要具备读写设备的能力外,还需要具备对硬件控制的能力。例如,要求设备报告错误信息,改变波特率,这些操作常常通过 ioctl方法来实现。

ioctl方法原型如下:

n   int (*ioctl)(struct inode *inode,struct file*filp,unsigned int cmd,unsigned long arg)

驱动函数ioctl的一般状态

n   int xxx_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)

{

     //cmd有效的校验

     //检查arg上传送的用户内存是否合法

     switch(cmd)

     {

         case 分辨常数1:处理例程1

                       break

         ………….

     }

     return 0;

}

cmd参数从用户空间传下来,可选的参数arg 以一个unsigned long 的形式传递,不管它是一个整数或一个指针。如果cmd命令不涉及数据传输,则第 3 个参数arg的值无任何意义。

2、编写ioctl控制驱动程序

1)    首先需要定义命令

ioctl 命令编码被划分为几个位段,include/asm/ioctl.h中定义了这些位字段:类型(幻数),序号,传送方向,参数的大小。Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了的幻数。

内核提供了下列宏来帮助定义命令:

n   _IO(typenr)没有参数的命令

n   _IOR(typenrdatatype)从驱动中读数据

n   _IOW(typenrdatatype)写数据到驱动

n   _IOWR(typenrdatatype)双向传送,type number 成员作为参数被传递。

定义命令(范例)

#define MEM_IOC_MAGIC   ‘m’ //定义幻数

#define MEM_IOCSET _IOW(MEM_IOC_MAGIC, 0, int)

#define MEM_IOCGQSET _IOR(MEM_IOC_MAGIC, 1, int)

2)    ioctl函数的实现

通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)。

如何使用ioctl中的arg参数?

如果是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查。

n   int access_ok(int type, const void *addr, unsigned long size)

第一个参数是 VERIFY_READ 或者 VERIFY_WRITE,用来表明是读用户内存还是写用户内存。addr 参数是要操作的用户内存地址,size 是操作的长度。如果 ioctl 需要从用户空间读一个整数,那么size参数等于 sizeof(int)access_ok 返回一个布尔值: 1 是成功(存取没问题)0 是失败(存取有问题),如果该函数返回失败, Ioctl应当返回–EFAULT

ioctl函数实现(参数检查)

if (_IOC_DIR(cmd) & _IOC_READ)

  err = !access_ok(VERIFY_WRITE, (void __user *)arg,

  _IOC_SIZE(cmd));

else if (_IOC_DIR(cmd) & _IOC_WRITE)

  err = !access_ok(VERIFY_READ, (void __user *)arg,

  _IOC_SIZE(cmd));

if (err)

  return -EFAULT;

3、位操作

Linux内核提供了操纵处理器端口位的函数,具体在#include

中进行定义 。常使用的有如下三个:

n   s3c2410_gpio_cfgpin配置某端口某位

n   s3c2410_gpio_setpin 设置某端口某位为高或低

n   s3c2410_gpio_pullup设置某端口某位是否上拉

设置实例:

     s3c2410_gpio_cfgpin(S3C2410_GPA0, S3C2410_GPA0_ADDR0);//S3C2410A组端口的0号引脚设置为地址总线的0号引脚功能。

     s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);//S3C2410E组端口ude8号引脚设置为SDDAT总线的第1号引脚

设置是否上拉实例:

     s3c2410_gpio_pullup(S3C2410_GPB0, 0);//使能B组端口的0号引脚的上拉功能。

     s3c2410_gpio_pullup(S3C2410_GPE8, 0);//使能E组端口的8号引脚的上拉功能。

     1 => disable the pull-up

0 => enable the pull-up

其中:

S3C2410_GPA0_ADDR0

S3C2410_GPE8_SDDAT1

linux/include/asm/hardware/s3c2410/regs-gpio.h 下定义

4IOCTL结合位操作方式控制LED实例:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include     /* copy_*_user */

#include    //for  udev

#include //主要测试setpincfgpin函数

#include

#include

#include

#define GPIO_IOCTL_MAGIC 'G'

#define LED_D09_SWT _IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)

#define LED_D10_SWT _IOW(GPIO_IOCTL_MAGIC, 1, unsigned int)

#define LED_D11_SWT _IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)

#define LED_D12_SWT _IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)

#define GPIO_IOC_MAXNR 12

#define LED_SWT_ON     0

#define LED_SWT_OFF 1

#define VIRTUALMEM_MAJOR 0    /*预设的ioport的主设备号*/

static unsigned int gpfcon_old = 0;

static unsigned int gpfdat_old = 0;

static unsigned int gpfup_old  = 0;

static int Virtualmem_major = VIRTUALMEM_MAJOR;

struct resource *IO_port_resource;

/*Virtualmem设备结构体*/

struct Virtualmem_dev                                    

{                                                        

  struct cdev cdev; /*cdev结构体*/                            

};

 

struct Virtualmem_dev *Virtualmem_devp; /*设备结构体指针*/

/*文件打开函数*/

int Virtualmem_open(struct inode *inode, struct file *filp)

{

  /*将设备结构体指针赋值给文件私有数据指针*/

  filp->private_data = Virtualmem_devp;

  return 0;

}

/*文件释放函数*/

int Virtualmem_release(struct inode *inode, struct file *filp)

{

  return 0;

}

/*读函数*/

static ssize_t Virtualmem_read(struct file *filp, char __user *buf, size_t size,

  loff_t *ppos)

{

  int ret = 0;

  struct Virtualmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

  return ret;

}

 

/*写函数*/

static ssize_t Virtualmem_write(struct file *filp, const char __user *buf,

  size_t size, loff_t *ppos)

{

  int ret = 0;

  struct Virtualmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

  return ret;

}

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

{ /*ioctl函数接口:主要接口的实现 */

         unsigned int swt = (unsigned int)arg;

         int err = 0;

         if (_IOC_TYPE(cmd) != GPIO_IOCTL_MAGIC) return -ENOTTY;

         if (_IOC_NR(cmd) > GPIO_IOC_MAXNR) return -ENOTTY;     

         if (_IOC_DIR(cmd) & _IOC_READ)

              err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

         else if (_IOC_DIR(cmd) & _IOC_WRITE)

              err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

         if (err) return -EFAULT;   

         switch (cmd)

         {

              case LED_D09_SWT:

              {

                   if (! capable (CAP_SYS_ADMIN))

                   return -EPERM;

                   s3c2410_gpio_setpin(S3C2410_GPF7, swt);

                   break;                

              }

              case LED_D10_SWT:

              {                                                 

                   s3c2410_gpio_setpin(S3C2410_GPF6, swt);

                   break;                

              }

              case LED_D11_SWT:

              {

                   s3c2410_gpio_setpin(S3C2410_GPF5, swt);

                   break;                

              }

              case LED_D12_SWT:

              {

                   s3c2410_gpio_setpin(S3C2410_GPF4, swt);

                   break;                

              }

              default:

              {

                   printk("Unsupported command\n");

                   break;

              }

         }

         return 0;

}

 

/*文件操作结构体*/

static const struct file_operations Virtualmem_fops =

{

  .owner = THIS_MODULE,

  .read = Virtualmem_read,

  .write = Virtualmem_write,

  .open = Virtualmem_open,

  .release = Virtualmem_release,

  .ioctl = gpio_ioctl, /* 实现主要控制功能*/

};

struct class *my_class= NULL;

/*初始化并注册cdev*/

static void Virtualmem_setup_cdev(struct Virtualmem_dev *dev, int index)

{

  int err, devno = MKDEV(Virtualmem_major, index);

 

  cdev_init(&dev->cdev, &Virtualmem_fops);

  dev->cdev.owner = THIS_MODULE;

  dev->cdev.ops = &Virtualmem_fops;

  err = cdev_add(&dev->cdev, devno, 1);

  if (err)

    printk(KERN_ALERT "Error %d adding Virtualmem%d", err, index);

     my_class =class_create(THIS_MODULE, "ioctldev");

     if(IS_ERR(my_class)) {

         printk("Err: failed in creating class.\n");

         return ;

 }

 class_device_create(my_class,NULL,devno,NULL,"ioctldev");

}

/*设备驱动模块加载函数*/

int Virtualmem_init(void)

{

  int result;

  dev_t devno = MKDEV(Virtualmem_major, 0);

 

  /* 申请设备号*/

  if (Virtualmem_major)

    result = register_chrdev_region(devno, 1, "ioctldev");

  else  /* 动态申请设备号 */

  {

    result = alloc_chrdev_region(&devno, 0, 1, "ioctldev");

    Virtualmem_major = MAJOR(devno);

  } 

  if (result < 0)

    return result;

 

  /* 动态申请设备结构体的内存*/

  Virtualmem_devp = kmalloc(sizeof(struct Virtualmem_dev), GFP_KERNEL);

  if (!Virtualmem_devp)    /*申请失败*/

  {

    result =  - ENOMEM;

    goto fail;

  }

  memset(Virtualmem_devp, 0, sizeof(struct Virtualmem_dev));

 

  Virtualmem_setup_cdev(Virtualmem_devp, 0);

     if ((IO_port_resource=request_region((unsigned long)S3C2410_GPFCON, 0x0c,"ioctldev"))==NULL)

         goto fail;

     else{ 

         printk("In the init process! \n");

         gpfcon_old = (unsigned int) inl((unsigned long)S3C2410_GPFCON);

         gpfdat_old = (unsigned int) inl((unsigned long)S3C2410_GPFDAT);

         gpfup_old = (unsigned int) inl((unsigned long)S3C2410_GPFUP);

         printk("S3C2410_GPFCON is %8X\n",gpfcon_old);

         printk("S3C2410_GPFUP  is %8X\n",gpfup_old);

         printk("S3C2410_GPFDAT is %8X\n",gpfdat_old);

//全部熄灭 LED        

         s3c2410_gpio_setpin(S3C2410_GPF4, 1);

         s3c2410_gpio_setpin(S3C2410_GPF5, 1);

         s3c2410_gpio_setpin(S3C2410_GPF6, 1);

         s3c2410_gpio_setpin(S3C2410_GPF7, 1);

      

         return 0;

}

  fail: unregister_chrdev_region(devno, 1);

  return result;

}

/*模块卸载函数*/

void Virtualmem_exit(void)

{

     if (IO_port_resource!=NULL) release_region((unsigned long)S3C2410_GPFCON, 0x0c);

  cdev_del(&Virtualmem_devp->cdev);   /*注销cdev*/

  kfree(Virtualmem_devp);     /*释放设备结构体内存*/

 

//全部点亮 LED

     s3c2410_gpio_setpin(S3C2410_GPF4, 0);

     s3c2410_gpio_setpin(S3C2410_GPF5, 0);

     s3c2410_gpio_setpin(S3C2410_GPF6, 0);

     s3c2410_gpio_setpin(S3C2410_GPF7, 0);    

  

  class_device_destroy(my_class, MKDEV(Virtualmem_major, 0));

  class_destroy(my_class); 

 

  unregister_chrdev_region(MKDEV(Virtualmem_major, 0), 1); /*释放设备号*/

}

MODULE_AUTHOR("AK-47");

MODULE_LICENSE("Dual BSD/GPL");

module_param(Virtualmem_major, int, S_IRUGO);

module_init(Virtualmem_init);

module_exit(Virtualmem_exit);

5、测试代码

#include

#include

#include

#include

#include

#include

#include

#define GPIO_IOCTL_MAGIC 'G'

#define LED_D09_SWT _IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)

#define LED_D10_SWT _IOW(GPIO_IOCTL_MAGIC, 1, unsigned int)

#define LED_D11_SWT _IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)

#define LED_D12_SWT _IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)

#define GPIO_IOC_MAXNR 12

#define LED_SWT_ON     0

#define LED_SWT_OFF 1

#define DEVICE_FILENAME "/dev/ioctldev"

int main( )

{

     int dev;

     dev = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);

     if(dev >= 0)

     {

         sleep(1);

         ioctl(dev, LED_D09_SWT,LED_SWT_ON);

         sleep(1);             

         ioctl(dev, LED_D09_SWT,LED_SWT_OFF);

         sleep(1);

         ioctl(dev, LED_D10_SWT,LED_SWT_ON);

         sleep(1);         

         ioctl(dev, LED_D10_SWT,LED_SWT_OFF);

         sleep(1);

         ioctl(dev, LED_D11_SWT,LED_SWT_ON);

         sleep(1);         

         ioctl(dev, LED_D11_SWT,LED_SWT_OFF);

         sleep(1);    

         ioctl(dev, LED_D12_SWT,LED_SWT_ON);

         sleep(1);             

         ioctl(dev, LED_D12_SWT,LED_SWT_OFF);     

     }

     close(dev);

     return 0;

}

6、测试驱动程序

加载驱动程序后,由于采用了4.1.104.1.11节所述的自动创建设备节点的方法,省去了手动创建的麻烦。创建设备节点,然后运行测试程序,会观察到4个发光二级管依次点亮。

阅读(948) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~