Rumah  >  Artikel  >  Tutorial sistem  >  Peranti watak pemacu peranti Linux: cara mudah untuk menerangkan dan mengurus peranti capaian berjujukan

Peranti watak pemacu peranti Linux: cara mudah untuk menerangkan dan mengurus peranti capaian berjujukan

王林
王林ke hadapan
2024-02-13 16:54:161041semak imbas

Pernahkah anda terfikir cara menulis pemacu untuk peranti watak anda di Linux? Pernahkah anda berfikir tentang cara untuk membolehkan pemandu anda melaksanakan beberapa fungsi asas dalam sistem Linux, seperti membuka, menutup, membaca, menulis, mengawal, dll.? Pernahkah anda berfikir tentang cara untuk membolehkan pemandu anda melaksanakan beberapa fungsi lanjutan dalam sistem Linux, seperti pemberitahuan tak segerak, pemultipleksan, pemetaan memori, dsb.? Jika anda berminat dengan isu ini, maka artikel ini akan memperkenalkan anda kepada kaedah yang berkesan untuk mencapai matlamat ini-peranti watak pemacu peranti Linux. Peranti aksara ialah struktur data yang digunakan untuk menerangkan peranti capaian berjujukan Ia membolehkan anda menghantar maklumat dan atribut peranti aksara kepada kernel dengan cara yang mudah dan bersatu, dengan itu merealisasikan pengenalan dan pemacu peranti. Peranti aksara juga merupakan mekanisme yang digunakan untuk melaksanakan fungsi asas Ia membolehkan anda mentakrif dan menggunakan pelbagai operasi dan arahan peranti aksara dalam cara yang standard dan universal, dengan itu membolehkan membuka, menutup, membaca, menulis, mengawal, dll. Fungsi. Peranti aksara juga merupakan rangka kerja untuk melaksanakan fungsi lanjutan Ia membolehkan anda mentakrif dan menggunakan pelbagai antara muka dan protokol peranti aksara dalam cara yang fleksibel dan boleh diperluaskan untuk mencapai pemberitahuan tak segerak, pemultipleksan, Pemetaan memori dan fungsi lain. Artikel ini akan memperkenalkan aplikasi dan peranan peranti aksara dalam pemacu peranti Linux secara terperinci daripada konsep asas, peraturan tatabahasa, kaedah penulisan, proses pendaftaran, kaedah pengendalian, dsb. peranti aksara untuk membantu anda menguasai kaedah yang berguna dan berkuasa ini.

Peranti watak pemacu peranti Linux: cara mudah untuk menerangkan dan mengurus peranti capaian berjujukan

Peranti aksara ialah jenis peranti yang lebih ringkas antara tiga jenis peranti utama (peranti aksara, peranti blok dan peranti rangkaian. Kerja utama yang diselesaikan dalam pemacunya ialah untuk memulakan, menambah dan memadam struktur cdev, memohon dan melepaskannya). nombor peranti, Serta mengisi fungsi operasi dalam struktur operasi_fail, melaksanakan fungsi seperti baca(), tulis() dan ioctl() dalam struktur operasi_fail adalah kerja utama reka bentuk pemacu.


Rutin rujukan

Kod sumber

/*
 * 虚拟字符设备globalmem实例:
 *  在globalmem字符设备驱动中会分配一片大小为 GLOBALMEM_SIZE(4KB)
 *  的内存空间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过
 *  Linux系统调用访问这片内存。
 */
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DEV_NAME "globalmem" /* /dev中显示的设备名 */
#define DEV_MAJOR 0 /*
 指定主设备号,为0则动态获取 */

/* ioctl用的控制字 */
#define GLOBALMEM_MAGIC 'M'
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)

/*--------------------------------------------------------------------- local vars */
/*globalmem设备结构体*/
typedef struct {
    struct cdev cdev; /* 字符设备cdev结构体*/

#define MEM_SIZE 0x1000 /*全局内存最大4K字节*/
    unsigned char mem[MEM_SIZE]; /*全局内存*/
    struct semaphore sem; /*并发控制用的信号量*/
} globalmem_dev_t;

static int globalmem_major = DEV_MAJOR;
static globalmem_dev_t *globalmem_devp; /*设备结构体指针*/

/*--------------------------------------------------------------------- file operations */
/*文件打开函数*/
static int globalmem_open(struct inode *inodep, struct file *filep)
{
    /* 获取dev指针 */
    globalmem_dev_t *dev = container_of(inodep->i_cdev, globalmem_dev_t, cdev);
    filep->private_data = dev;
    return 0;
}
/*文件释放函数*/
static int globalmem_release(struct inode *inodep, struct file *filep)
{
    return 0;
}
/*读函数*/
static ssize_t globalmem_read(struct file *filep, char __user *buf, size_t len, loff_t *ppos)
{
    globalmem_dev_t *dev = filep->private_data;

    unsigned long p = *ppos; 
    int ret = 0;

    /*分析和获取有效的长度*/
    if (p > MEM_SIZE) {
        printk(KERN_EMERG "%s: overflow!\n", __func__);
        return - ENOMEM;
    }
    if (len > MEM_SIZE - p) {
        len = MEM_SIZE - p;
    }

    if (down_interruptible(&dev->sem)) /* 获得信号量*/
        return  - ERESTARTSYS;

    /*内核空间->用户空间*/
    if (copy_to_user(buf, (void*)(dev->mem + p), len)) {
        ret = - EFAULT;
    }else{
        *ppos += len;

        printk(KERN_INFO "%s: read %d bytes from %d\n", DEV_NAME, (int)len, (int)p);
        ret = len;
    }

    up(&dev->sem); /* 释放信号量*/

    return ret;
}
/*写函数*/
static ssize_t globalmem_write(struct file *filep, const char __user *buf, size_t len, 
loff_t *ppos)
{
    globalmem_dev_t *dev = filep->private_data;
    int ret = 0;

    unsigned long p = *ppos; 
    if (p > MEM_SIZE) {
        printk(KERN_EMERG "%s: overflow!\n", __func__);
        return - ENOMEM;
    }

    if (len > MEM_SIZE - p) {
        len = MEM_SIZE - p;
    }

    if (down_interruptible(&dev->sem)) /* 获得信号量*/
        return  - ERESTARTSYS;

    /*用户空间->内核空间*/
    if (copy_from_user(dev->mem + p, buf, len)) {
        ret =  - EFAULT;
    }else{
        *ppos += len;

        printk(KERN_INFO "%s: written %d bytes from %d\n", DEV_NAME, (int)len, (int)p);
        ret = len;
    }

    up(&dev->sem); /* 释放信号量*/

    return ret;
}
/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filep, loff_t offset, int start)
{
    globalmem_dev_t *dev = filep->private_data;
    int ret = 0;

    if (down_interruptible(&dev->sem)) /* 获得信号量*/
        return  - ERESTARTSYS;

    switch (start) {
        case SEEK_SET:
            if (offset  MEM_SIZE) {
                printk(KERN_EMERG "%s: overflow!\n", __func__);
                return - ENOMEM;
            }

            ret = filep->f_pos = offset;
            break;
        case SEEK_CUR:
            if ((filep->f_pos + offset) f_pos + offset) > MEM_SIZE) {
                printk(KERN_EMERG "%s: overflow!\n", __func__);
                return - ENOMEM;
            }

            ret = filep->f_pos += offset;
            break;
        default:
            return - EINVAL;
            break;
    }

    up(&dev->sem); /* 释放信号量*/

    printk(KERN_INFO "%s: set cur to %d.\n", DEV_NAME, ret);

    return ret;
}
/* ioctl设备控制函数 */
static long globalmem_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
    globalmem_dev_t *dev = filep->private_data;

    switch (cmd) {
        case MEM_CLEAR:
            if (down_interruptible(&dev->sem)) /* 获得信号量*/
                return  - ERESTARTSYS;

            memset(dev->mem, 0, MEM_SIZE);
            up(&dev->sem); /* 释放信号量*/

            printk(KERN_INFO "%s: clear.\n", DEV_NAME);
            break;
        default:
            return - EINVAL;
    }

    return 0;
}
/*文件操作结构体*/
static const struct file_operations globalmem_fops = {
    .owner = THIS_MODULE,
    .open         = globalmem_open,
    .release      = globalmem_release,
    .read         = globalmem_read,
    .write        = globalmem_write,
    .llseek       = globalmem_llseek,
    .compat_ioctl = globalmem_ioctl
};

/*---------------------------------------------------------------------*/
/*初始化并注册cdev*/
static int globalmem_setup(globalmem_dev_t *dev, int minor)
{
    int ret = 0;
    dev_t devno = MKDEV(globalmem_major, minor);

    cdev_init(&dev->cdev, &globalmem_fops);
    dev->cdev.owner = THIS_MODULE;

    ret = cdev_add(&dev->cdev, devno, 1);
    if (ret) {
        printk(KERN_NOTICE "%s: Error %d dev %d.\n", DEV_NAME, ret, minor);
    }

    return ret;
}
/*设备驱动模块加载函数*/
static int __init globalmem_init(void)
{
    int ret = 0;
    dev_t devno; 

    /* 申请设备号*/
    if(globalmem_major){
        devno = MKDEV(globalmem_major, 0);
        ret = register_chrdev_region(devno, 2, DEV_NAME);
    }else{ /* 动态申请设备号 */
        ret = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
        globalmem_major = MAJOR(devno);
    }

    if (ret return ret;
    }

    /* 动态申请设备结构体的内存,创建两个设备 */
    globalmem_devp = kmalloc(2*sizeof(globalmem_dev_t), GFP_KERNEL);
    if (!globalmem_devp) {
        unregister_chrdev_region(devno, 2);
        return - ENOMEM;
    }

    ret |= globalmem_setup(&globalmem_devp[0], 0); /* globalmem0 */
    ret |= globalmem_setup(&globalmem_devp[1], 1); /* globalmem1 */
    if(ret)
        return ret;

    init_MUTEX(&globalmem_devp[0].sem); /*初始化信号量*/
    init_MUTEX(&globalmem_devp[1].sem);

    printk(KERN_INFO "globalmem: up %d,%d.\n", MAJOR(devno), MINOR(devno));
    return 0;
}
/*模块卸载函数*/
static void __exit globalmem_exit(void)
{
    cdev_del(&globalmem_devp[0].cdev);
    cdev_del(&globalmem_devp[1].cdev);
    kfree(globalmem_devp);
    unregister_chrdev_region(MKDEV(globalmem_major, 0), 2);
    printk(KERN_INFO "globalmem: down.\n");
}

/* 定义参数 */
module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);

/* 模块描述及声明 */
MODULE_AUTHOR("Archie Xie ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A char device module just for demo.");
MODULE_ALIAS("cdev gmem");
MODULE_VERSION("1.0");

Pengesahan Ruang Pengguna

  1. Tukar kepada pengguna root

  2. Masukkan modul

    insmod globalmem.ko
    
  3. Buat nod peranti (rutin seterusnya akan menunjukkan cara membuat nod secara automatik)

    cat /proc/devices 找到主设备号major
    mknod /dev/globalmem0 c major 0 和 /dev/globalmem1 c major 1
    
  4. Ujian Membaca dan Menulis

    echo "hello world" > /dev/globalmem
    cat /dev/globalmem
    

Melalui artikel ini, kami memahami aplikasi dan peranan peranti aksara dalam pemacu peranti Linux, dan belajar cara menulis, mendaftar, mengendalikan, mengubah suai dan nyahpepijat peranti aksara. Kami mendapati bahawa peranti aksara adalah kaedah yang sangat sesuai untuk pembangunan sistem terbenam Ia membolehkan kami menerangkan dan mengurus peranti capaian berjujukan dan melaksanakan fungsi asas dan lanjutan. Sudah tentu, peranti watak juga mempunyai beberapa langkah berjaga-jaga dan had, seperti keperluan untuk mengikuti spesifikasi sintaks, memberi perhatian kepada isu kebenaran, memberi perhatian kepada kesan prestasi, dsb. Oleh itu, apabila menggunakan peranti watak, kita perlu mempunyai pengetahuan dan pengalaman perkakasan tertentu, serta tabiat pengaturcaraan yang baik dan kemahiran penyahpepijatan. Saya harap artikel ini dapat memberi anda panduan peringkat permulaan dan memberi anda pemahaman awal tentang peranti watak. Jika anda ingin mengetahui lebih lanjut tentang peranti watak, disyorkan agar anda merujuk kepada lebih banyak bahan dan contoh, serta berlatih dan meneroka sendiri.

Atas ialah kandungan terperinci Peranti watak pemacu peranti Linux: cara mudah untuk menerangkan dan mengurus peranti capaian berjujukan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam