Linux設備驅動之字元設備

来源:http://www.cnblogs.com/archiexie/archive/2016/12/10/6141292.html
-Advertisement-
Play Games

字元設備是3大類設備(字元設備、塊設備和網路設備)中較簡單的一類設備,其驅動程式中完成的主要工作是初始化、添加和刪除cdev結構體,申請和釋放設備號,以及填充 file_operations結構體中的操作函數,實現file_operations結構體中的read()、write()和ioctl()等... ...


字元設備是3大類設備(字元設備、塊設備和網路設備)中較簡單的一類設備,其驅動程式中完成的主要工作是初始化、添加和刪除cdev結構體,申請和釋放設備號,以及填充 file_operations結構體中的操作函數,實現file_operations結構體中的read()、write()和ioctl()等函數是驅動設計的主體工作。


參考常式

源碼

/*
 * 虛擬字元設備globalmem實例:
 *  在globalmem字元設備驅動中會分配一片大小為 GLOBALMEM_SIZE(4KB)
 *  的記憶體空間,併在驅動中提供針對該片記憶體的讀寫、控制和定位函數,以供用戶空間的進程能通過
 *  Linux系統調用訪問這片記憶體。
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#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 < 0 || 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) < 0 || (filep->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 < 0) {
        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 <[email protected]>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A char device module just for demo.");
MODULE_ALIAS("cdev gmem");
MODULE_VERSION("1.0");

用戶空間驗證

  1. 切換到root用戶
  2. 插入模塊

    insmod globalmem.ko
  3. 創建設備節點(後續常式會展示自動創建節點的方法)

    cat /proc/devices 找到主設備號major
    mknod /dev/globalmem0 c major 0 和 /dev/globalmem1 c major 1
  4. 讀寫測試

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

References

1. Linux Device Drivers
2. Linux設備驅動開發詳解(宋寶華第二版)


Copyright (C) 2016 archiexie@cnblogs. All Rights Reserved.



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、數據樣式 寫入之前,需要整理以下數據的格式,之後將數據保存到hdfs中,本例使用的樣式如下(用tab分開): 2、代碼 假設要將以上樣式的數據寫入到hbase中,列族為cf,列名為colb,可以使用下麵的代碼(參考) 這段代碼使用mapreduce程式對數據做了進一步處理,之後調用相關的api將 ...
  • 在通常情況下你在使用消息中間件的時候,都是未經設計的使用,你沒有把應用架構和系統架構邊界搞清楚。消息中間件只是一個純粹的技術工具,當你引入的時候是站在應用架構的角度引入的。這是架構的角度,也是架構的上帝視角,這樣你就不會用到最後發現越來越混亂,而且也無法結合軟體模式、方法論、最佳實踐來綜合提升系統... ...
  • 本文主要利用SQL Server 2008 R2自帶的"維護計劃"創建一個自動備份數據的任務。 首先,啟動Sql Management studio,確保"SQL Server 代理"處於啟動狀態。如果沒有,可以右擊選擇"啟動". 第二步,依次展開"管理"---"維護計劃",並右擊"維護計劃"選擇"... ...
  • redis雖說是用C語言開發的,但是redis考慮了性能、安全性、效率性、功能等要,redis底層存儲字元串實現,自己實現了名為簡單動態字元串(Simple dynamic string)簡稱SDS的結構來存儲字元串,這個結構有int len(當前字元串長度), int free(未使用的字元串長度 ...
  • 一.jdk的安裝與配置 3.創建軟鏈接方便原目錄/usr/local/src/jdk1.8.0_60 配置到/etc/profile,先創建兩個軟鏈接 : (1)原目錄/usr/local/src/jdk1.8.0_60 ln -s /usr/local/src/jdk1.8.0_60 java ( ...
  • 今天想用一下顯示器自帶的喇叭,忽然發現聲音輸出選項里HDMI的聲音設備沒了。之前開始使用這台顯示器的時是用過一段時間的。 百度了一番,沒發現什麼線索。後來去谷歌找到這麼一段文字: I'm not sure where to report this, but after I upgraded a Le ...
  • 問題描述:當安裝好Ubuntu系統的時候,root用戶沒有密碼,需要設置。 解決方法: ...
  • 本文由ilanniweb提供友情贊助,首發於爛泥行天下 想要獲得更多的文章,可以關註我的微信ilanniweb 最近沒有時間好久沒有寫文章了,今天由於需要安裝docker學習虛擬容器的知識,需要升級OS的內核。目前我這邊使用的OS是centos6.5,內核是2.6版本的,如下: cat /etc/i... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...