freeBSD字符驱动程序 初级

在/dev/目录下创建字符设备节点
struct cdev * make_dev(struct cdevsw *devsw, 字符设备开关表
                       int unit,          
                       uid_t uid, 
                       gid_t gid, 
                       int mode,
                       const char *fmt, ...)
删除字符设备
void destroy_dev(struct cdev *dev)

头文件:#include <sys/conf.h>

DEV_MODULE是对DECLARE_MODULE的封装
#define    DEV_MODULE(name, evh, arg)                    \
static moduledata_t name##_mod = {                    \
    #name,                                \
    evh,                                \
    arg                                    \
};                                    \
DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE)

定义IOCTL命令宏如下:
_IO(x,y)    定义一个不传输数据的ioctl命令 此方式data是没有用的
_IOR(x,y,z) 定义个读操作的ioctl命令 从设备向用户空间传递数据
_IOW(x,y,z) 定义个写操作的ioctl命令 从用户空间向设备传递数据
_IOWR(x,y,z) 顶一个读写操作的ioctl命令  设备和用户空间相互传递数据
 
x:一个8位的魔术 可以选择任何值,在整个驱动中保持一致
y:一个序号  将各个ioctl命令进行区分
z:传输的数据类型  _IO没有此参数
头文件: #include <sys/ioccom.h>

 内核代码如下:

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/malloc.h>

static d_open_t   test_open;
static d_close_t  test_close;
static d_read_t   test_read;
static d_write_t  test_write;
static d_ioctl_t  test_ioctl;

//使用vmstat -m | grep testbuff 可以查看内存使用情况
//使用malloc分配内存采用M_TECDEV 自己定义的malloc_type
MALLOC_DECLARE(M_TECDEV);
MALLOC_DEFINE(M_TECDEV,"testbuff","test buff for chare dev");

//ioctl命令
#define TEST_CLEAR_BUFF     _IO('Y',1)
#define TEST_SET_BUFF_SIZE  _IOW('Y',2,int)

//字符设备开关
static struct cdevsw test_cdev={
     .d_version = D_VERSION,//驱动程序支持的freebsd版本号 D_VERSION
     .d_open = test_open,
     .d_close = test_close,
     .d_read = test_read,
     .d_write = test_write,
     .d_ioctl = test_ioctl, 
     .d_name = "tecdev"//驱动名字
};
static int countbuff;
static  char *buff;
static struct cdev *dev;

static int test_ioctl(struct cdev *dev,
                     u_long cmd,
                     caddr_t data,
                     int fflag,
                     struct thread *td)
{
    int error = 0;

    switch(cmd)
    {
        case TEST_CLEAR_BUFF:
             memset(buff,0,1024);
             countbuff = 0;
        case TEST_SET_BUFF_SIZE:
             countbuff = *(int *)data;
        default:
             error = ENOTTY;
             break;
    }

    return error;
}

static int test_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
    uprintf("Open testdev\n");
    return 0;
}

static int test_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
{
    uprintf("close testcdev\n");
    return 0;
}

static int test_read(struct cdev *dev, struct uio *uio, int ioflag)
{
    int error = 0;
    int count = 0;

    count = MIN(countbuff,uio->uio_resid);
    if(0 == count)
       return 0;
    error = uiomove(buff,count,uio);
    if(error != 0)
    {
        uprintf("read error\n");
    }
    countbuff = 0;
    return 0;
}

static int test_write(struct cdev *dev, struct uio *uio, int ioflag)
{
    int error = 0;

    error =  copyin(uio->uio_iov->iov_base,buff,MIN(1024-1,uio->uio_iov->iov_len));
    if(error != 0)
    {
        uprintf("copy from user error\n");
    }
    countbuff = MIN(1024-1,uio->uio_iov->iov_len);
    return 0;
}

//模块事件处理程序
static  int handleevent(module_t mod, int event, void *arg)
{
    int error = 0;

    switch(event)
    {
        case MOD_LOAD:
            buff = malloc(1024,M_TECDEV,M_WAITOK); 
            dev = make_dev(&test_cdev,0,UID_ROOT,GID_WHEEL,0600,"testcdv");
            break;
        case MOD_UNLOAD:
            destroy_dev(dev);
            free(buff,M_TECDEV);
            break;
        default:
            error = EOPNOTSUPP;
    }
    return error;
}

//DEV_MODULE是对DECLARE_MODULE的封装
DEV_MODULE(tecdev,handleevent,NULL);

猜你喜欢

转载自blog.csdn.net/yldfree/article/details/83270755