字符设备驱动编写流程以及大概框架

来源:互联网 发布:淘宝销售的案例 编辑:程序博客网 时间:2024/06/05 08:28

Linux设备驱动:
Linux设备驱动分为以下三类:
(1)字符设备:键盘,打印机
(2)块设备:硬盘,NAND
(3)网络设备:网卡


对于字符设备是最基本,最常见的设备:
对字符设备的驱动主要完成以下动作:
1、定义一个结构体static struct file_operations变量,其内定义一些设备的open,read,write,close等控制函数
2、在结构体外分别实现结构体中定义的这些函数
3、向内核中注册或删除驱动模块


块设备与字符设备的驱动结构是不同的,但是对于用户来说没有什么区别,块设备比字符设备要复杂,在I/O操作上极为不同表现在缓冲,I/O调度,请求队列等。
1、操作的硬件接口实现不一样;
2、数据块的数据有一定的格式


网络设备不同于字符和块设备,但是有字符和快设备的部分功能


字符设备注册有两种方式,可以以混杂设备注册:


字符设备步骤:
(1)分配cdev: struct cdev *cdev_alloc(void);
(2)初始化cdev: void cdev_init(struct cdev *cdev, const struct file_operations *fops);
(3)添加cdev: int cdev_add(struct cdev *p, dev_t dev, unsigned count)


在2.6版本之前字符设备注册方式:
int register_chrdev(unsigned int major,const char*name,struct file_operations *fops);
其中参数major如果等于0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册。
major 是感兴趣的主设备号, name 是驱动的名子(出现在 /proc/devices), fops 是缺省的 file_operations 结构. 
一个对 register_chrdev 的调用为给定的主编号注册 0 - 255 的次编号,并且为每一个建立一个缺省的 cdev 结构. 
使用这个接口的驱动必须准备好处理对所有 256 个次编号的 open 调用( 不管它们是否对应真实设备 ), 
它们不能使用大于 255 的主或次编号.register_chrdev函数的major参数如果等于0,则表示采用系统动态分配的主设备号。
它所做的事情为:
(1). 注册设备号, 通过调用 __register_chrdev_region() 来实现
(2). 分配一个cdev, 通过调用 cdev_alloc() 来实现
(3). 将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现
(4). 将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要.


注销接口函数
int unregister_chrdev(unsignedintmajor,constchar*name)


注册代码简单流程:
在init函数中注册字符设备
#define DEV_NAME "i2c"
#define IIC_MAJOR 145
static int __init init_2420(void)
{
int result;
....
result = register_chrdev(IIC_MAJOR,DEV_NAME,&fops);
...
}
static void __exit eit_2420(void)
{
unregister_chrdev(IIC_MAJOR,DEV_NAME);
}


在2.6新版本中接口函数分别改为
int register_chrdev_region(dev_t first, unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);


1.一个 cdev 一般它有两种定义初始化方式:静态的和动态的。
(1)静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
(2)动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。
2.注册一个独立的cdev设备的基本过程如下:
(1)、为struct cdev 分配空间(如果已经将struct cdev 嵌入到自己的设备的特定结构体中,并分配了空间,这步略过!)
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops=&my_ops;


(2)、初始化struct cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)


(3)、初始化cdev.owner
cdev.owner = THIS_MODULE;


(4)、cdev设置完成,通知内核struct cdev的信息(在执行这步之前必须确定你对struct cdev的以上设置已经完成!)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)


注册模板:
struct cdev *cdev;
#define GSDEV_NR_DEVS 1
#define DEV_NAME "i2c"
#define DEV_MAJOR 256
static struct class *i2c_cls;
static const struct file_operations fops =
{
  .owner = THIS_MODULE,
  .open = my_open,
  .release = my_release,
  .read = my_read,
  .write = my_write,
};


方式1(动态)
static int __init init_2420(void)
{
    dev_t devno;
    cdev = cdev_alloc();
    cdev->owner = THIS_MODULE;
    cdev->ops = &fops;
    devno = MKDEV(DEV_MAJOR,0); //创建设备号(主设备号,次设备号)
    
  // if( register_chrdev_region(devno,1,DEVICE_NAME))//获取字符设备号
   // {
    // if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
   // {
   //         printk("my_sdio:[ERROR] Misc device register failed\n");
   //         return -1;
  //   }
  //   gs_major = MAJOR(devno);
  //  }
    ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//注册字符设备到内核
       i2c_cls = class_create(THIS_MODULE,DEVICE_NAME);//创建设备类别文件
       if(IS_ERR(i2c_cls))
       {
        printk("class_create failed");
       }
       else
       {
           device_create(i2c_cls,NULL,devno,NULL,DEVICE_NAME);创建设备文件
       }
}


static void __exit eit_2420(void)
{
    device_destroy(i2c_cls,MKDEV(gs_major,0));
    class_destroy(i2c_cls);
    cdev_del(cdev);
    //unregister_chrdev_region(MKDEV(gs_major,0),GSDEV_NR_DEVS);


}


方式2(静态)
major = DEV_MAJOR;
static int __init init_2420(void)
{
    dev_t devno;
    struct cdev my_cdev;
    cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
    devno = MKDEV(major,0); //创建设备号(主设备号,次设备号)
    
    if( register_chrdev_region(devno,1,DEVICE_NAME))//获取字符设备号
    {
    if(alloc_chrdev_region(&devno,0,1,DEVICE_NAME))
    {
            printk("my_sdio:[ERROR] Misc device register failed\n");
            return -1;
    }
    major = MAJOR(devno);
    }
    ret = cdev_add(cdev,devno,GSDEV_NR_DEVS);//注册字符设备到内核
 
}


static void __exit eit_2420(void)
{
    unregister_chrdev_region(MKDEV(major,0),GSDEV_NR_DEVS);//释放占用设备号
    cdev_del(cdev);//注销设备
}


按混杂设备注册方式
注册方式更为简单,不用创建设备节点
static const struct file_operations fops =
{
  .owner = THIS_MODULE,
  .open = my_open,
  .release = my_release,
  .read = my_read,
  .write = my_write,
};
static struct miscdevice my__dev =
{
  .minor = MISC_DYNAMIC_MINOR,
  .name = DEVICE_NAME,
  .fops = &my_fops,
};
static int __init init_2420(void)
{
int ret;
  ret = misc_register(&my_dev);
if (ret)
{
printk("my_gpio:[ERROR] Misc device register failed\n");
return ret;
}
}


static void __exit eit_2420(void)
{
    misc_deregister(&my_dev);
}


0 0