本文介绍new_1型字符设备驱动基本软件框架,以imx6q的pwm驱动为例
1、构建设备控制器结构
定义pwm控制器类型,其中需要注意的是第一个成员必须是LW_DEV_HDR类型,表示设备头。第二个程序为LW_LIST_LINE_HEADER类型,用于管理fdnode链表。后面可以添加任意与驱动程序有关的私有变量,如寄存器基址等。如程序清单1.1所示。
程序清单 1.1 pwm控制器结构
/***************************************************************************************
PWM 控制器类型定义
***************************************************************************************/
typedef struct {
LW_DEV_HDR PWMC_devHdr; /* 必须是第一个结构体成员 */
LW_LIST_LINE_HEADER PWMC_fdNodeHeader;
addr_t PWMC_BaseAddr;
} __IMX6Q_PWM_CONTROLER, *__PIMX6Q_PWM_CONTROLER;
2、构建Open函数
open函数中主要添加fdnode节点,这也是new_1型字符设备驱动的特点。SylixOS中通过API_IosFdNodeAdd()去添加一个fdnode,并最终返回fdnode。如程序清单2.1所示。
程序清单 2.1 open函数
/************************************************************************************
** 函数名称: imx6qPwmOpen
** 功能描述: 打开 PWM 设备
** 输 入 : pDev 设备
** pcName 设备名字
** iFlags 标志
** iMode 模式
** 输 出 : 文件节点
** 全局变量:
** 调用模块:
************************************************************************************/
static LONG imx6qPwmOpen (__PIMX6Q_PWM_CONTROLER pPwmDev,
PCHAR pcName,
INT iFlags,
INT iMode)
{
PLW_FD_NODE pFdNode;
BOOL bIsNew;
addr_t atBaseAddr;
if (pcName == LW_NULL) {
_ErrorHandle(ERROR_IO_NO_DEVICE_NAME_IN_PATH);
return (PX_ERROR);
} else {
pFdNode = API_IosFdNodeAdd(&pPwmDev->PWMC_fdNodeHeader, (dev_t)pPwmDev, 0,
iFlags, iMode, 0, 0, 0, LW_NULL, &bIsNew);
if (pFdNode == LW_NULL) {
printk(KERN_ERR "imx6qWdtOpen(): failed to add fd node!\n");
return (PX_ERROR);
}
if (LW_DEV_INC_USE_COUNT(&pPwmDev->PWMC_devHdr) == 1) {
/*
* 初始化 PWM 定时器,默认设置参数如下
*/
atBaseAddr = pPwmDev->PWMC_BaseAddr;
imx6qPwmHwInit(atBaseAddr, 260, 256, 200);
imx6qPwmEnable(atBaseAddr);
return ((LONG)pFdNode);
}
}
/*
* 如果设备打开失败进行如下操作
*/
if (pFdNode) {
API_IosFdNodeDec(&pPwmDev->PWMC_fdNodeHeader, pFdNode, NULL);
pFdNode = LW_NULL;
}
LW_DEV_DEC_USE_COUNT(&pPwmDev->PWMC_devHdr);
return (PX_ERROR);
}
3、构建close、ioctl函数
close与ioctl函数相对比较简单。close函数中主要禁用pwm并将打开数减一。ioctl中主要是提供命令修改pwm的相关参数。如程序清单3.1所示。
程序清单3.1 close、ioctl函数
/************************************************************************************
** 函数名称: imx6qPwmClose
** 功能描述: 关闭 PWM 设备
** 输 入 : pFdEntry 文件结构
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
************************************************************************************/
static INT imx6qPwmClose (PLW_FD_ENTRY pFdEntry)
{
__PIMX6Q_PWM_CONTROLER pPwmDev = (__PIMX6Q_PWM_CONTROLER)pFdEntry->FDENTRY_pdevhdrHdr;
addr_t atBaseAddr = pPwmDev->PWMC_BaseAddr;
imx6qPwmDisable(atBaseAddr);
LW_DEV_DEC_USE_COUNT(&pPwmDev->PWMC_devHdr);
return (PX_ERROR);
}
/************************************************************************************
** 函数名称: imx6qPwmIoctl
** 功能描述: 控制 PWM 设备
** 输 入 : pFdEntry 文件结构
** iCmd 命令
** lArg 参数
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
************************************************************************************/
static INT imx6qPwmIoctl (PLW_FD_ENTRY pFdEntry, INT iCmd, LONG lArg)
{
__PIMX6Q_PWM_CONTROLER pPwmDev = (__PIMX6Q_PWM_CONTROLER)pFdEntry->FDENTRY_pdevhdrHdr;
addr_t atBaseAddr = pPwmDev->PWMC_BaseAddr;
UINT32 usRegVal = (UINT32)lArg;
switch (iCmd) {
case PWM_FREN: /* PWM 工作周期 */
imx6qPwmHwInit(atBaseAddr, usRegVal, 256, 0);
imx6qPwmEnable(atBaseAddr);
return (ERROR_NONE);
case PWM_DUTY: /* 设置占空比 */
imx6qPwmHwInit(atBaseAddr, 0, 256, usRegVal);
imx6qPwmEnable(atBaseAddr);
return (ERROR_NONE);
default:
return (PX_ERROR);
}
}
4、构建驱动安装函数
驱动安装函数主要目的是将驱动注册到系统中。首先需要构建file_operations结构存放open、close、ioctl等操作,再使用结构iosDrvInstallEx2()将此结构注册到系统中,并获取对应的驱动编号,供设备安装时使用。如程序清单4.1所示。
程序清单4.1 安装驱动
/************************************************************************************
** 函数名称: imx6qPwmDrv
** 功能描述: 安装 PWM 驱动
** 输 入 : NONE
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
************************************************************************************/
INT imx6qPwmDrv (VOID)
{
struct file_operations fileop;
if (_G_iPwmDrvNum) {
return (ERROR_NONE);
}
lib_memset(&fileop, 0, sizeof(struct file_operations));
fileop.owner = THIS_MODULE;
fileop.fo_create = imx6qPwmOpen;
fileop.fo_open = imx6qPwmOpen;
fileop.fo_close = imx6qPwmClose;
fileop.fo_ioctl = imx6qPwmIoctl;
fileop.fo_write = imx6qPwmWrite;
fileop.fo_read = imx6qPwmRead;
_G_iPwmDrvNum = iosDrvInstallEx2(&fileop, LW_DRV_TYPE_NEW_1);
DRIVER_LICENSE(_G_iPwmDrvNum, "Dual BSD/GPL->Ver 1.0");
DRIVER_AUTHOR(_G_iPwmDrvNum, "Zhang.Xu");
DRIVER_DESCRIPTION(_G_iPwmDrvNum, "PWM driver.");
return (_G_iPwmDrvNum > 0) ? (ERROR_NONE) : (PX_ERROR);
}
5、创建PWM设备
创建pwm设备函数主要是通过API_IosDevAddEx()创建设备文件节点,并绑定对应的驱动编码即上一节中生成的驱动编号。这使得上层应用在打开此设备文件节点时可以获取对应的驱动编码,然后找到对应的驱动程序。如程序清单5.1所示。
程序清单 5.1 创建pwm设备
/************************************************************************************
** 函数名称: imx6qPwmDevAdd
** 功能描述: 创建 PWM 设备
** 输 入 : uiIndex ID
** 输 出 : ERROR_CODE
** 全局变量:
** 调用模块:
************************************************************************************/
INT imx6qPwmDevAdd (UINT uiIndex)
{
__PIMX6Q_PWM_CONTROLER pPwmDev;
CPCHAR pcBuffer;
if (uiIndex >= PWM_NUM) {
printk(KERN_ERR "imx6qWdtDevAdd(): wdt index invalid!\n");
return (PX_ERROR);
}
pPwmDev = &_G_pwm[uiIndex];
switch (uiIndex) {
case 0:
pcBuffer = "/dev/pwm0";
pPwmDev ->PWMC_BaseAddr = PWM1_BASE_ADDR;
pwm1_iomux_config();
break;
case 1:
pcBuffer = "/dev/pwm1";
pPwmDev ->PWMC_BaseAddr = PWM2_BASE_ADDR;
pwm2_iomux_config();
break;
case 2:
pcBuffer = "/dev/pwm2";
pPwmDev ->PWMC_BaseAddr = PWM3_BASE_ADDR;
pwm3_iomux_config();
break;
case 3:
pcBuffer = "/dev/pwm3";
pPwmDev ->PWMC_BaseAddr = PWM4_BASE_ADDR;
pwm4_iomux_config();
break;
}
if (API_IosDevAddEx(&pPwmDev->PWMC_devHdr,
pcBuffer,
_G_iPwmDrvNum,
DT_CHR) != ERROR_NONE) {
printk(KERN_ERR "imx6qWdtDevAdd(): can not add device : %s.\n", strerror(errno));
return (PX_ERROR);
}
return (ERROR_NONE);
}
6、调用接口
最后一步就是调用驱动安装和设备创建这两个函数接口。一般情况下驱动安装在bspInit.c中的halDrvInit()中被调用,而设备创建在bspInit.c的halDevInit()中被调用。