字符设备驱动实现

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

#define MAJOR_NUM   100                     //主设备号
#define MINOR_NUM   0                       //次设备号
#define GLOBALMEM_SIZE  0x400              //内存空间大小

#define CMD_1       0x01
#define CMD_2       0x02
#define CMD_3       0x03

static dev_t    dev;                         //dev_t类型是unsigned int 类型,32位,用于在驱动程序中定义设备编号
static struct   cdev     mychardev;          //字符设备
static struct   class    *myclass;            //class
//static char     mem[GLOBALMEM_SIZE] = {0};  //全局内存空间

static int mychardev_open(struct inode *inode, struct file *file);
static int mychardev_release(struct inode *inode, struct file *file);
static ssize_t mychardev_read(struct file *filp, char *buf, size_t len, loff_t *ppos);
static ssize_t mychardev_write(struct file *filp, const char *buf, size_t len, loff_t *ppos);
static long mychardev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

struct file_operations mychardev_fops =
{
    .owner = THIS_MODULE,
	.open  = mychardev_open,
	.release = mychardev_release,
    .read = mychardev_read,
    .write = mychardev_write,
	.unlocked_ioctl = mychardev_ioctl,
};

static int mychardev_open(struct inode *inode, struct file *file)
{
    try_module_get(THIS_MODULE);            //模块计数加一
    printk("test:This chrdev is open\n");
    return 0;
}

static int mychardev_release(struct inode *inode, struct file *file)
{
    module_put(THIS_MODULE);                //模块计数减一
    printk("test:This chrdev is release\n");
    return 0;
}

static ssize_t mychardev_read(struct file *filp, char *buf, size_t len, loff_t *ppos)
{
    int ret;
    unsigned long p = *ppos;
    char mem[GLOBALMEM_SIZE] = "mychardev_read";

    printk("test:This chrdev is reading\n");
    if(p >= GLOBALMEM_SIZE)
        return 0;
    if(len > GLOBALMEM_SIZE - p)
        len = GLOBALMEM_SIZE - p;
    
    ret = copy_to_user(buf, mem + p, len);        //copy_to_user(to, from, n)

    if(ret)
        return -EFAULT;

    *ppos += len;
    ret = len;

    printk("test:Read %u bytes from %lu, Read:%s\n", len, p, mem);
    return ret;
}

static ssize_t mychardev_write(struct file *filp, const char *buf, size_t len, loff_t *ppos)
{
    int ret;
    unsigned long p = *ppos;
    char mem[GLOBALMEM_SIZE] = {0};

    printk("test:This chrdev is writting\n");
    if(p >= GLOBALMEM_SIZE)
        return 0;
    if(len > GLOBALMEM_SIZE - p)
        len = GLOBALMEM_SIZE - p;
    
    ret = copy_from_user(mem + p, buf, len);        //copy_from_user(to, from, n)

    if(ret)
        return -EFAULT;

    *ppos += len;
    ret = len;

    printk("test:Write %u bytes from %lu, write:%s\n", len, p, mem);
    return ret;
}

static long mychardev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int ret;
    char mem[GLOBALMEM_SIZE] = {0};

    printk("test:mychardev_ioctl start\n");
    switch(cmd){
    case CMD_1:
        //memset(mem, 0, GLOBALMEM_SIZE);
        printk("test:CMD1: Mem has been reset\n");
        break;
    case CMD_2:
        ret = copy_from_user(mem, (char *)arg, GLOBALMEM_SIZE);        //copy_from_user(to, from, n)
        printk("test:CMD2: Mem has been write = %s\n", mem);
        break;
    case CMD_3:
        strcpy(mem, "hello");
        ret = copy_to_user((char *)arg, mem, GLOBALMEM_SIZE);
        printk("test:CMD3: Mem has been read = %s\n", mem);
        break;
    default:
        return -EFAULT;
    }
    return 0;
}

static int __init test_init(void)
{
    int major = MAJOR_NUM;
    int minor = MINOR_NUM;
    int ret;

    dev = MKDEV(MAJOR_NUM, MINOR_NUM);                      //MKDEV宏可以通过主设备号和次设备号生成dev_t

    printk("----------------[test_init]----------------");
    printk("[test_init] start");
    printk("----------------[test_init]----------------");

    /* 先申请设备号 */
    if(major)
    {
        //如果主设备号存在,则静态申请设备号
        ret = register_chrdev_region(dev, 1, "sunlh");      //dev_t 次设备号个数 编号对应设备名称(cat /proc/devices可以查看设备号及名称)
    }
    else
    {
        //如果主设备号不存在,则动态申请
        ret = alloc_chrdev_region(&dev, 0, 1, "sunlh");     //dev_t 次设备号起始 次设备号个数 编号对应设备名称(cat /proc/devices可以查看设备号及名称)

        major = MAJOR(dev);                                 //获取分配的主设备号
        minor = MINOR(dev);                                 //获取分配的次设备号
    }

    if(ret < 0)
    {
        printk("register chrdev region failed");
        return -EFAULT;
    }
    
    /* 其次注册字符设备驱动,将设备与file_operation绑定 */
    cdev_init(&mychardev, &mychardev_fops);
    mychardev.owner = THIS_MODULE;

    /* 最后向系统添加设备,将申请的设备号与字符设备绑定 */
    ret = cdev_add(&mychardev, dev, 1);                     //字符设备 dev_t 次设备号个数

    if(ret)
    {
        printk("fail to add cdev");
        return -EFAULT;
    }

/**
 * device_create - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @fmt: string for the device's name
 **/
    /* 创建class并将class注册到内核中,返回值为class结构指针
    在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用device_create在/dev/下创建对应的设备。 */
    myclass = class_create(THIS_MODULE, "sunlh_class");     // ls /sys/class 可以查看设备类型名称
    device_create(myclass, NULL, dev, NULL, "sunlh_dev");   // ls /dev/ 可以查看设备名称

    printk("----------------[test_init]----------------");
    printk("[test_init] end");
    printk("----------------[test_init]----------------");

    return 0;
}

static void __exit test_exit(void)
{
    printk("----------------[test_exit]----------------");
    printk("[test_exit] start");
    printk("----------------[test_exit]----------------");

    device_destroy(myclass, dev);
    class_destroy(myclass);
    cdev_del(&mychardev);

    unregister_chrdev_region(dev, 1);

    printk("----------------[test_exit]----------------");
    printk("[test_exit] end");
    printk("----------------[test_exit]----------------");
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL v2");

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IAYL.BL

创作不易,投个硬币😆

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值