1、混杂设备

什么是上拉电阻和下拉电阻?在GPIO端口中,一般一个芯片例如2440,会分为9组的GPIO端口。GPA GPB ...以GPA为例。GPACON用于设置端口的功能。GPADAT用于读写数据。GPAUP用于决定是否使用上拉电阻

主设备号为10的字符设备称为混杂设备。

为什么要引入混杂设备呢?为什么要把它从字符设备中独立出来。

字符驱动的使用方式有很多,①

我们先看一下通过字符设备来控制led的驱动程序:(1)初始化字符操作集,(2)分配devno设备号,(3)把dev添加到字符设备中;

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>struct cdev cdev;
dev_t devno;

int led_open(struct inode *node, struct file *filp)
{}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{}
static struct file_operations led_fops =
{
    .open = led_open,
    .unlocked_ioctl = led_ioctl,
};
static int led_init()
{
    cdev_init(&cdev,&led_fops);
    alloc_chrdev_region(&devno, 0 , 1 , "myled");
    cdev_add(&cdev, devno, 1);
    
    return 0;    
}
static void led_exit()
{
    cdev_del(&cdev);
    unregister_chrdev_region(devno,1);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

我们发现每次这样使用字符设备时比较麻烦的,需要初始化、分配以及添加cdev devno;很多时候都是利用mknod命令手动创建设备节点。
实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static struct class           *firstdrv_class;
static struct class_device    *firstdrv_class_dev;
static int first_drv_open(struct inode *inode, struct file *file)
{}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{}
static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write    =    first_drv_write,       
};
int major;
static int first_drv_init(void)
{
    major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
    firstdrv_class = class_create(THIS_MODULE, "firstdrv");
    firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */return 0;
}

static void first_drv_exit(void)
{
    unregister_chrdev(major, "first_drv"); // 卸载
    class_device_unregister(firstdrv_class_dev);
    class_destroy(firstdrv_class);
    iounmap(gpbcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");

 

2、回归正题--混杂设备驱动

(1)混杂设备结构体描述

 

(2)混杂设备注册

 

 

 (3)代码案例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <match/gpio-bank-k.h>
#include "led.h"

#define LEDCON 0x7f008800
#define LEDDAT 0x7f008808
unsigned int *led_config; 
unsigned int *led_data; 
#if 0
struct cdev cdev;
dev_t devno;
#endif
int led_open(struct inode *node, struct file *filp)
{
    led_config = ioremap(LEDCON,4);
    writel(0x11110000,led_config);
    led_data = ioremap(LEDDAT,4);
    return 0;
}

long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd)
    {
        case LED_ON:
            writel(0x00,led_data);
            return 0;    
        case LED_OFF:
            writel(0xff,led_data);
            return 0;    
        default:
            return -EINVAL;
    }
}
static struct file_operations led_fops =
{
    .open = led_open,
    .unlocked_ioctl = led_ioctl,
};
static struct miscdevice led_miscdev = {
    .minor    =211,
    .name    ="led",
    .fops    =&led_fops,
};
static int led_init()
{
    misc_register(&led_miscdev);
#if 0
    cdev_init(&cdev,&led_fops);
    alloc_chrdev_region(&devno, 0 , 1 , "myled");
    cdev_add(&cdev, devno, 1);
#endif
    return 0;
}

static void led_exit()
{
    misc_deregister(&led_miscdev);
#if 0
    cdev_del(&cdev);
    unregister_chrdev_region(devno,1);
#endif
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

 

 

(4)再来一个选择混杂设备驱动模型的按键驱动案例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>

#if 0
struct miscdevice  {
        int minor;
        const char *name;
        const struct file_operations *fops;
        struct list_head list;
        struct device *parent;
        struct device *this_device;
        const struct attribute_group **groups;
        const char *nodename;
        umode_t mode;
};
#endif 

int key_open(struct inode *inode, struct file *filep)
{
    return 0;
}
struct file_operations key_fops={
    .open=key_fopen,
};
struct miscdevice key_miscdev={
    .minor = 200,
    .name  = "key",
    .fops = &key_fops,
};

static int key_init(void)
{
    misc_register(&key_miscdev);
}
static void key_exit(void)
{
    misc_deregister(&key_miscdev);
}

module_init(key_init);
module_exit(key_exit);