六、【ioctl】应用程序和驱动程序中的ioctl
1、接口函数介绍
很多设备除了读和写之外,还需要驱动提供其它操作能力,例如:获取LCD尺寸、修改串口波特率
(应用层)函数原型:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
(driver)提供接口函数
struct file_operations {
.....
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
......
}
2.6.36之后的内核版本只支持unlocked_ioctl,之前的一些过渡版本两者都支持,更早的版本只支持compat_ioctl
long (*unlocked_ioctl) (struct file * filp, unsigned int cmd , unsigned long arg);
参数:
- filp:指向打开的文件信息结构体。
- cmd:驱动提供给应用程序的命令字。
- arg:应用程序与驱动之间传递的参数。
2、命令字的组成
(1)bit解释
- bit[31:30]:参数传递的方向。应用--->驱动、驱动--->应用、应用<--->驱动、不需要参数.
- bit[29:16]:参数的大小,用数据类型表示大小char int struct xxx。
- bit[15:8]:魔数/幻数,用于区分不同驱动的命令字,一般设置为某个字符的ASCII码。
- bit[7:0]:命令字的序号。
(2)内核中提供的生成命令字的宏
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
_IO---生成没有参数传递的命令字。 _IOR--生成应用从驱动获取参数的命令字。 _IOW--生成应用给驱动传递参数的命令字。 _IOWR--生成双向传参的命令字。
参数:
- type:魔数/幻数,用于区分不同驱动的命令字,一般设置为某个字符的ASCII码
- nr:命令字的序号
- size:参数的大小,用数据类型表示大小char int struct xxx
例如:
#define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) #define LED_ON _IOW('L',0,unsigned int) #define LED_OFF _IOW('L',1,unsigned int)
3、例程(实现beep)
beep_drv.c
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioctl.h> static struct cdev beep; static dev_t dev; static struct class *beep_cls=NULL; static struct device * beep_dev=NULL; static struct resource *beep_res = NULL; static void __iomem *GPIOCBASE = NULL; static void __iomem *GPIOCOUT = NULL; static void __iomem *GPIOCOUTENB= NULL; static void __iomem *GPIOCALTFN0 = NULL; static void __iomem *GPIOCALTFN1 = NULL; #define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) static int beep_open(struct inode* inode,struct file *filp) { printk(KERN_INFO"beep_open\n"); return 0; } static int beep_close(struct inode* inode,struct file *filp) { printk(KERN_INFO"beep_close\n"); return 0; } static ssize_t beep_write(struct file *filp, const char __user *user, size_t size, loff_t *oft) { printk(KERN_INFO"beep_write\n"); return size; } static long beep_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) { switch(cmd) { case BEEP_ON: writel(readl(GPIOCOUT)|((0x01<<14)),GPIOCOUT); break; case BEEP_OFF: writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); default: return -ENOIOCTLCMD; } return 0; } struct file_operations fops= { .open = beep_open, .release = beep_close, .write = beep_write, .unlocked_ioctl = beep_ioctl, }; static int __init beep_init(void) { int ret; printk(KERN_INFO"beep_init\n"); ret = alloc_chrdev_region(&dev, 0, 1,"beep_chrdev"); if(ret!=0) { printk(KERN_INFO"register char dev failed\n"); goto alloc_chrdev_region_err; } beep.owner = THIS_MODULE; cdev_init(&beep,&fops); ret=cdev_add(&beep, dev, 1); if(ret!=0) { printk(KERN_INFO"add char device failed\n"); goto cdev_add_err; } beep_cls = class_create(THIS_MODULE, "beep_class"); if(IS_ERR(beep_cls)) { printk(KERN_INFO"class create failed\n"); ret = -EBUSY; goto class_create_err; } beep_dev= device_create(beep_cls, NULL,dev, NULL, "beep_dev"); if(IS_ERR(beep_dev)) { printk(KERN_INFO"device create failed\n"); ret = -ENOMEM; goto device_create_err; } beep_res = request_mem_region(0xc001c000,0x68,"beep_iomem"); if(beep_res==NULL) { printk(KERN_INFO"request memory region failed\n"); ret = -EBUSY; goto request_mem_region_err; } GPIOCBASE = ioremap(0xc001c000,0x68); if(GPIOCBASE==NULL) { printk(KERN_INFO"ioremap failed"); ret = -EBUSY; goto ioremap_err; } GPIOCOUT = GPIOCBASE+0x00; GPIOCOUTENB = GPIOCBASE+0x04; GPIOCALTFN0 = GPIOCBASE+0x20; GPIOCALTFN1 = GPIOCBASE+0x24; //beep writel(readl(GPIOCOUTENB)|(0x01<<14),GPIOCOUTENB); writel(readl(GPIOCALTFN0) &(~(0x03<<28)),GPIOCALTFN0); writel(readl(GPIOCALTFN0) |(1<<28),GPIOCALTFN0); writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); return 0; ioremap_err: release_mem_region(0xc001c000,0x68); request_mem_region_err: device_destroy(beep_cls , dev); device_create_err: class_destroy(beep_cls); class_create_err: cdev_del(&beep); cdev_add_err: unregister_chrdev_region(dev,1); alloc_chrdev_region_err: return 0; } static void __exit beep_exit(void) { printk(KERN_INFO"xxxx__exit\n"); //取锟斤拷映锟斤拷 iounmap(GPIOCBASE); //锟酵凤拷IO锟节达拷 release_mem_region(0xc001c000,0x68); device_destroy(beep_cls , dev); class_destroy(beep_cls); cdev_del(&beep); unregister_chrdev_region(dev, 1); } module_init(beep_init); module_exit(beep_exit); MODULE_AUTHOR("yqf"); MODULE_DESCRIPTION("beep driver program"); MODULE_LICENSE("GPL");
main.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <sys/ioctl.h> #define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) int main() { int fd; char buff[2]={0}; fd = open("/dev/beep_dev",O_RDWR); if(fd<0) { perror("open bepp dev error!"); } while(1) { ioctl(fd,BEEP_ON); sleep(1); ioctl(fd,BEEP_OFF); sleep(1); } close(fd); }