驱动开发步骤:
1 创建开发目录和文件
创建驱动程序文件。
2 编写可加载模块配置的入口点
即初始化和卸载。
int _init(void)
_init入口点首先调用ddi_soft_state_init函数来初始化软状态,若失败则返回错误代码,若成功,_init调用mod_install加载新模块,若加载失败调用ddi_soft_state_fini并返回失败模块安装的错误代码。
int _info(structmodinfo *modinfop).
_info入口点调用mod_info(&ml, modinfop)返回可加载模块信息。
int _fini(void)
_fini入口点调用mod_remove删除_init入口点所安装的模块及撤销设备所有实例的状态指针和状态结构。
3 编写自动配置入口点
实现驱动模块连接和卸载
static int wrmod_attach()
_attach()入口点首先调用ddi_get_instance()来检索设备信息节点的实例号,该实例号用来调用ddi soft state zalloc () 、ddi getsoft state () 和ddi create minor node ( )等其它函数。
static int wrmod_detach()
detach 必须调用ddi remove minor node() 来解除ddi create minornode () 所分配的一
切. 并且必须撤消wrmod attach () 例程做过的每一件事.
static int wrmod getinfo( dev_info_t *dip , ddi_info_cmd_t cmd , void *arg , void *resultp) / *通过其参数之一返回被请求的设备驱动程序信息.* /
4 编写用户上下文入口点
实现打开关闭设备,获取模块信息等
wrmod open ( dev_t*devp ,int flag ,int otyp ,cred_t * credp)
{
int instance = getminor ( * devp) ;
if ( ( qsp = ddi get soft state ( ) )= =NULL)
ASSERT(qsp - > instance = =instance) ;
}/ * 例程获得对设备访问权. * /
wrmod close (dev t dev ,int flag ,int otyp , cred t * credp)
/* 放弃对设备访问,close ( ) 例程必须撤消open () 例程所做的每一件事. * /
static int wrmod read(dev_t dev ,st ruct uio* uiop ,cred_t * credp) / * 从设备节点读取数
据. * /
static int wrmod write(dev t dev ,st ruct uiocred t * credp) / * 将数据写到设备节
点. * /
5 定义数据结构
所有驱动程序都必须定义一个设备操作结构dev_ops。由于dev_ops结构包含了一个指向cb_ops 字符和块操作结构的指针,因此必须先定义字符和块操作结构cb_ops。可加载驱动程序的modldrv连接结构包含一个指向dev_ops结构的指针. modlinkage模块连接结构包含一个指向modldrv结构的指针. 在这些数据结构中初始化入口点使驱动程序可被动态加载. wrmod相关入口点如图3 所示.
6 创建驱动程序配置文件
7 构建和安装驱动程序
Ø 编译和链接驱动程序
使用-D _KERNAL选项表示该代码用于定义内核模块,DEBUG选项用于ASSERT()语句。
在32位X86架构类似于$cc –D _KERNAL –DDEBUG –c $ld –r –o dummy 。
在64位X86架构solaris系统中需要使用sun studio中bin目录下的cc 和 /usr/bin/amd64/的ld才可以编译连接成功。
命令为:
cc -D_KERNEL -m64 -xarch=sse2a-xmodel=kernel -c dummy.c
ld –r –o dummy
请参照/docs/cd/E24847_01/html/819-7057/#scrolltoc
Ø 在调试的时候可以在临时位置(tmp)放置驱动程序,在/drv目录(64位/usr/kernel/drv/amd64)下建立到tmp/dummy的连接。等调式完成后再将驱动程序拷贝到/usr/kernal/drv目录下(64位驱动需要拷贝到/usr/kernel/drv/amd64目录下),配置文件(conf)不管32位还是64位都要拷贝到/usr/kernel/drv目录下。
cp dummy /tmp//拷贝驱动到tmp
在64位的X86架构上建立连接
ln -s /tmp/dummy / usr/kernel/drv/amd64/dummy
在32位的X86架构上建立连接
ln -s /tmp/dummy / usr/kernel/drv/dummy
cp /usr/kernel/drv//拷贝配置文件
8 加载驱动程序
在加载/卸载模块时可以通过命令$tail –f/var/adm/mssages来查看cmn_err输出的信息。
Ø 加载驱动程序
加载驱动要确保是root用户,使用add_drv命令来加载驱动程序#add_drv dummy。在查看的/var/adm/messages窗口中会看到加载信息。
Ø 显示驱动程序模块信息
可以使用modinfo命令显示驱动的模块信息
Ø 读写设备
可以使用cat 命令来测试,
Ø 卸载驱动程序
确保root用户,使用rem_drv命令。rem drv dummy.
数据结构
struct dev_ops {
int devo_rev; /* Driver build version */
int devo_refcnt; /* device reference count */
int (*devo_getinfo)(dev_info_t *dip,
ddi_info_cmd_t infocmd, void *arg, void**result);
int (*devo_identify)(dev_info_t *dip);
int (*devo_probe)(dev_info_t *dip);
int (*devo_attach)(dev_info_t *dip,ddi_attach_cmd_t cmd);
int (*devo_detach)(dev_info_t *dip,ddi_detach_cmd_t cmd);
int (*devo_reset)(dev_info_t *dip,ddi_reset_cmd_t cmd);
structcb_ops *devo_cb_ops; /* cb_ops pointer for leaf drivers */
structbus_ops *devo_bus_ops; /* bus_ops pointer for nexus drivers */
int (*devo_power)(dev_info_t *dip, intcomponent,
intlevel);
int (*devo_quiesce)(dev_info_t *dip);
};
struct cb_ops {
int (*cb_open)(dev_t *devp, int flag, int otyp,cred_t *credp);
int (*cb_close)(dev_t dev, int flag, int otyp,cred_t *credp);
int (*cb_strategy)(struct buf *bp);
int (*cb_print)(dev_t dev, char *str);
int (*cb_dump)(dev_t dev, caddr_t addr, daddr_tblkno, int nblk);
int (*cb_read)(dev_t dev, struct uio *uiop,cred_t *credp);
int (*cb_write)(dev_t dev, struct uio *uiop,cred_t *credp);
int (*cb_ioctl)(dev_t dev, int cmd, intptr_targ, int mode,
cred_t *credp, int *rvalp);//可以通过填充此函数来完成用户的配置请求
int (*cb_devmap)(dev_t dev, devmap_cookie_t dhp,offset_t off,
size_tlen, size_t *maplen, uint_t model);
int (*cb_mmap)(dev_t dev, off_t off, int prot);
int (*cb_segmap)(dev_t dev, off_t off, struct as*asp,
caddr_t *addrp, off_t len, unsigned intprot,
unsigned int maxprot, unsigned int flags,cred_t *credp);
int (*cb_chpoll)(dev_t dev, short events, intanyyet,
short *reventsp, struct pollhead **phpp);
int (*cb_prop_op)(dev_t dev, dev_info_t *dip,
ddi_prop_op_t prop_op, int mod_flags,
char *name, caddr_t valuep, int *length);
structstreamtab *cb_str; /* streamsinformation */
/*
* The cb_flag fields are here to tell thesystem a
* bit about the device. The bit definitionsare
* in <sys/>.
*/
int cb_flag; /*driver compatibility flag */
int cb_rev; /*cb_ops version number */
int (*cb_aread)(dev_t dev, struct aio_req *aio,cred_t *credp);
int (*cb_awrite)(dev_t dev, struct aio_req *aio,cred_t *credp);
};
实例代码
32位或64位系统区分
The following is an example of the isainfo command executed on an UltraSPARC™
system running the 64-bit operating system:(以下是运行在sun的cpu上的64位系统)
% isainfo -v
64-bit sparcv9 applications
32-bit sparcapplications
When the same command is run on an x86 system runnig the 32–bit Solarisoperating
system(以下是运行在X86架构上的32位系统)
% isainfo -v
32-bit i386applications
When the same command is run on an x86 system running the 64–bit Solarisoperating
system: (以下是运行在X86架构上的64位系统)
% isainfo -v
64-bit amd64applications
32-bit i386 applications
驱动程序创建规则
1、 使用基于驱动程序名的前缀来制定全局变量和函数的唯一变量。
2、 使用cmn_err函数而不是printf来记录驱动程序活动
3、 使用assert来捕捉意外的出错返回。
4、 使用mutex_owned来验证加锁要求和编制相关文档
5、 将单独设备用于每个控制的设备。
6、 驱动程序中尽可能多得使用DDI函数
7、 设备驱动应向只受控于该驱动程序的DMA缓冲区写入数据
8、 需要DMA传输时,使用ddi_umem_alloc函数
9、 所有设备都应支持热插拔和电源管理
10、 将volatie关键字应用于应用了设备寄存器的任何变量
参考文献:
SUN-编写设备驱动程序.pdf
SOLARIS设备驱动程序的研究与开发.pdf
Solaris设备驱动教程.chm
solaris 64-bit
/docs/cd/E24847_01/html/819-7057/#scrolltoc
/docs/cd/E19205-01/820-3035/
/docs/cd/E19205-01/820-3035/gabcd/
/docs/cd/E19205-01/820-3033/gemls/