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