solaris驱动开发

时间:2025-03-22 12:59:58

驱动开发步骤:

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/