分类: 嵌入式
2010-06-02 16:15:04
int result;
result = register_chrdev(MAJOR_NR, DEVICE_NAME, &gpio_fops);
2,模块卸载函数clearup_module(),该函数主要完成对设备的卸载:
unregister_chrdev(MAJOR_NR, DEVICE_NAME);
3,设备驱动程序的file operations结构。File operations结构是一个定义在
中的数据结构,内核通过它访问驱动程序:
structfile operations{
int(*seek)(structinode* ,structfile*,offt,int);
int(*read)(structinode* ,structfile*,char,int);
int(*write)(structinode* ,structfile*,off t,int);
int(*readdir)(structinode* ,structfile*,structdirent* ,int);
int(*select)(structinode* ,structfile*,int,select table*);
int(*ioctl)(structinode* ,structfile*,unsinedint,unsignedlong);
int(*mmap)(structinode* ,structfile*,structvm area struct*);
int(*open)(structinode* ,structfile*);
int(*release)(structinode* ,structfile*);
int(*fsync)(structinode* ,structfile*);
int(*fasync)(structinode* ,structfile*,int);
int(*check media change)(structinode*,structfile*);
int(*revalidate)(dev tdev);
}
下面是一个网友写的gpio的驱动程序和应用程序,供大家学习:
******************************************************************************
熟悉ioctl
在编写ioctl代码之前,需要选择对应不同命令的编号。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非串行设备输入流修改波特率,如果每一个ioctl命令都是唯一的,应用程序进行这种操作时就会得到一个EINVAL错误,而不是无意间成功地完成了意想不到的操作。
要按Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Doucumention/ioctl-number.txt这两个文件。头文件定义了要使用的位字段:类型(幻数)、序数、传送方向以及参数大小等。ioctl-number.txt文件中罗列了内核所使用的幻数,选择自己的幻数要避免和内核冲突。以下是对include/asm/ioctl.h中定义的宏的注释:
#define _IOC_NRBITS 8 //序数(number)字段的字位宽度,8bits
#define _IOC_TYPEBITS 8 //幻数(type)字段的字位宽度,8bits
#define _IOC_SIZEBITS 14 //大小(size)字段的字位宽度,14bits
#define _IOC_DIRBITS 2 //方向(direction)字段的字位宽度,2bits
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) //序数字段的掩码,0x000000FF
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) //幻数字段的掩码,0x000000FF
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) //大小字段的掩码,0x00003FFF
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) //方向字段的掩码,0x00000003
#define _IOC_NRSHIFT 0 //序数字段在整个字段中的位移,0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //幻数字段的位移,8
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) //大小字段的位移,16
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) //方向字段的位移,30
/*
* Direction bits.
*/
#define _IOC_NONE 0U //没有数据传输
#define _IOC_WRITE 1U //向设备写入数据,驱动程序必须从用户空间读入数据
#define _IOC_READ 2U //从设备中读取数据,驱动程序必须向用户空间写入数据
/*
*_IOC 宏将dir,type,nr,size四个参数组合成一个cmd参数,如下图:
*
*/
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
/*
* used to create numbers
*/
//构造无参数的命令编号
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
//构造从驱动程序中读取数据的命令编号
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
//用于向驱动程序写入数据命令
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
//用于双向传输
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/*
*used to decode ioctl numbers..
*/
//从命令参数中解析出数据方向,即写进还是读出
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
//从命令参数中解析出幻数type
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
//从命令参数中解析出序数number
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
//从命令参数中解析出用户数据大小
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
/* ...and for the drivers/sound files... */
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
********************************************************************************
//gpio.h
#ifndef __GPIO_H
#define __GPIO_H
#include<linux/ioctl.h>
#define GPIO_PIN_LOW 0
#define GPIO_PIN_HIGH 1
#define GPIO_PIN_ERR (~0)
#define GPIO_IOC_MAGIC 0xd0
#define GPIO_SET_PIN _IO(GPIO_IOC_MAGIC,0)
#define GPIO_SET_ALL_PIN _IO(GPIO_IOC_MAGIC,1)
#define GPIO_CLR_PIN _IO(GPIO_IOC_MAGIC,2)
#define GPIO_CLR_ALL_PIN _IO(GPIO_IOC_MAGIC,3)
#define GPIO_SET_PIN_OUT _IO(GPIO_IOC_MAGIC,4)
#define GPIO_SET_PIN_IN _IO(GPIO_IOC_MAGIC,5)
#define GPIO_MAXNR 6
#endif
********************************************************************************
//gpio.c
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include<linux/module.h>
#include<linux/sched.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include"gpio.h"
static int gpio_open(struct inode *inode,struct file *filp);
static int gpio_release(struct inode *inode,struct file *filp);
static int gpio_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long param);
#define GPIO_MAJOR_NR 126 //主设备号
#define MAJOR_NR major //主设备号
#define DEVICE_NAME "gpio" //设备名称
#define MAX_PORT 1 /* 1 devices */
#define GPIO_CONC_ADDR 0x01d20010 //PortC的配置寄存器地址
#define GPIO_DATA_ADDR 0x01d20014 //PortC的数据寄存器地址
static int major=GPIO_MAJOR_NR;
MODULE_LICENSE("GPL");
static struct file_operations gpio_fops=
{
//owner:THIS_MODULE,
ioctl:gpio_ioctl,
open:gpio_open,
release:gpio_release
};
int __init gpio_init(void)
{
int result;
result = register_chrdev(MAJOR_NR, DEVICE_NAME, &gpio_fops);
if (result < 0) {
printk(KERN_ERR DEVICE_NAME ":Unable to get major %d\n", MAJOR_NR);
return result;
}
if (MAJOR_NR == 0) {
MAJOR_NR = result;
}
printk(KERN_INFO DEVICE_NAME ": init OK\n");
return 0;
}
void __exit gpio_cleanup(void)
{
unregister_chrdev(MAJOR_NR, DEVICE_NAME);
}
static int gpio_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int gpio_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int gpio_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int num;
volatile u32 *Regconc,*Regdata;
num = MINOR(inode->i_rdev);
if (num >= MAX_PORT) {
return -ENODEV;
}
if (_IOC_TYPE(cmd) != GPIO_IOC_MAGIC) {
return -ENOTTY;
}
if (_IOC_NR(cmd) >= GPIO_MAXNR) {
return -ENOTTY;
}
Regconc = (volatile u32 *)(GPIO_CONC_ADDR);
Regdata = (volatile u32 *)(GPIO_DATA_ADDR);
//需要查看PortC的PCONC和PDATC寄存器的相关参数来对I/O引脚进行配置
switch(cmd){
case GPIO_SET_PIN://设置某个I/O引脚为为高电平
if (arg < 16) {
*Regdata |= 1u << arg;
}
break;
case GPIO_SET_ALL_PIN://设置多个I/O引脚为高电平
*Regdata |= arg;
break;
case GPIO_CLR_PIN:// 设置某个I/O引脚为为低电平
if (arg < 16) {
*Regdata &= ~(1u << arg);
}
break;
case GPIO_CLR_ALL_PIN: //设置多个I/O引脚为低电平
*Regdata &= ~arg;
break;
case GPIO_SET_PIN_OUT://设置某个I/O引脚为输出引脚
if (arg < 16) {
*Regconc &= ~(1u << (2*arg + 1));
*Regconc |= 1u << (2*arg);
}
break;
case GPIO_SET_PIN_IN: //设置某个I/O引脚为输入引脚
if (arg < 32) {
*Regconc &= ~(1u << (2*arg));
*Regconc &= ~(1u << (2*arg + 1));
}
break;
}
return 0;
}
*******************************************************************************
//test.c PortC驱动程序的测试程序。循环点亮Led0,Led1,Led2 10次
#include<stdio.h>
#include<fcntl.h>
#include"gpio.h"
int main(int argc, char * argv[])
{
int fd;
int loop;
fd = open("/dev/gpio",O_RDONLY);
if (fd < 0) {
printf("open error\n");
return 0;
}
ioctl(fd,GPIO_SET_PIN_OUT,1);
ioctl(fd,GPIO_SET_PIN_OUT,2);
ioctl(fd,GPIO_SET_PIN_OUT,3);
loop = 0;
while (loop < 10) {
ioctl(fd,GPIO_CLR_PIN,3);
ioctl(fd,GPIO_SET_PIN,1);
sleep(1);
ioctl(fd,GPIO_CLR_PIN,1);
ioctl(fd,GPIO_SET_PIN,2);
sleep(1);
ioctl(fd,GPIO_CLR_PIN,2);
ioctl(fd,GPIO_SET_PIN,3);
sleep(1);
loop++;
}
close(fd);
return 0;
}
************************************************************************