1. 程式人生 > >程式碼:編寫一個簡單的字元裝置驅動——建立多個同類型裝置

程式碼:編寫一個簡單的字元裝置驅動——建立多個同類型裝置

編寫同類型多個裝置字元驅動應注意一下幾個問題:

1、申請裝置號alloc_chrdev_region時須指定次裝置號範圍;

2、動態分配裝置空間時同時分配NUM個裝置的空間;

3、根據次裝置號和統一的主裝置號生成針對單個裝置的devno,然後完成cdev_add註冊;

4、裝置檔案的device_create中可以指定第五個引數來區別不同裝置的裝置檔案(節點)名稱;

5、解除安裝函式中注意迴圈解除安裝裝置檔案以及登出裝置;

6、登出裝置號;

遺留問題:通過cdev_add中指定count引數應該可以完成多個裝置的同時註冊,但cdev_del中並沒有devno做入參,

                  暫未確認一次呼叫是否可以完成對多個裝置的登出,留待日後解決。

程式碼如下:

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

#include <linux/device.h>

#define MAX_SIZE          0x1000
#define MEM_CLEAR         0x1
#define MEM_PRINT         0x2
#define DEVICE_NUM        4

static struct class *class;
static struct device *classdev;


struct virtdev_dev
{
	struct cdev cdev;
	unsigned char mem[MAX_SIZE];
};

struct virtdev_dev *devp;
dev_t devno;
int major;


static int virtdev_open(struct inode *inode, struct file *filp)
{
	struct virtdev_dev *dev = container_of(inode->i_cdev, 
					struct virtdev_dev, cdev);
	filp->private_data = dev;
	
	return 0;
}


static int virtdev_release(struct inode *inode, struct file *filp)
{
	return 0;
}



static long virtdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct virtdev_dev *dev = filp->private_data;
	
	switch (cmd)
	{
	case MEM_CLEAR:
		memset(dev->mem, 0, MAX_SIZE);
		printk(KERN_INFO "virtdev is set to zero.\n");
		break;
		
	case MEM_PRINT:
		printk(KERN_INFO "copying string OK!\n");
		break;
	
	default:
		return -EINVAL;
	}
	
	return 0;
}

static ssize_t virtdev_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct virtdev_dev *dev = filp->private_data;
	
	if (p >= MAX_SIZE)
		return -EINVAL;
	
	if (count > MAX_SIZE - p)
		count = MAX_SIZE - p;
		
	if (copy_to_user(buf, dev->mem + p, count))
		ret = -EINVAL;
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "read %u byte(s) from %lu\n", count, p);
	}
	
	return ret;
}


static ssize_t virtdev_write(struct file *filp, const char __user *buf, size_t size,
					loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct virtdev_dev *dev = filp->private_data;
	
	if (p >= MAX_SIZE)
		return -EINVAL;
	if (count > MAX_SIZE - p)
		count = MAX_SIZE - p;
		
	if (copy_from_user(dev->mem + p, buf, count))
		ret = -EINVAL;
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "write %u byte(s) from %lu\n", count, p);
	}
	
	return ret;
}


static loff_t virtdev_llseek(struct file *filp, loff_t offset, int orig)
{
	loff_t ret = 0;
	
	switch (orig)
	{
	case 0:
		if (offset < 0)
		{
			ret = -EINVAL;
			break;
		}
		if (offset > MAX_SIZE)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos = offset;
		ret = filp->f_pos;
		break;
		
	case 1:
		if ((filp->f_pos + offset) < 0 )
		{
			ret = -EINVAL;
			break;
		}
		
		if ((filp->f_pos + offset) > MAX_SIZE)
		{
			ret = -EINVAL;
			break;
		}
		filp->f_pos += offset;
		ret = filp->f_pos;
		break;
		
	default:
		ret = -EINVAL;
		break;
	}
	
	return ret;
}


static const struct file_operations virtdev_fops =
{
	.owner  =  THIS_MODULE,
	.llseek =  virtdev_llseek,
	.read   =  virtdev_read,
	.write  =  virtdev_write,
	.unlocked_ioctl = virtdev_ioctl,
	.open   =  virtdev_open,
	.release = virtdev_release,
};

static char *chardev_devnode(struct device *dev, umode_t *mode)
{
    if (mode)
        *mode = 0666;

    return NULL;
}


static void virtdev_setup_cdev(struct virtdev_dev *dev, int index)
{
	int err;
	int ret;
	int devno = MKDEV(major, index);
	
		
	classdev = device_create(class, NULL, devno, NULL, "virtdev%d", index);
	if (IS_ERR(class))
	{
		ret = PTR_ERR(classdev);
		printk(KERN_ERR "device create error %d\n", ret);
		class_destroy(class);
	}

	
	cdev_init(&dev->cdev, &virtdev_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, devno, 1);
	if (err)
		printk(KERN_NOTICE "Error %d adding virtdev%d", err, index);
}


static int __init virtdev_init(void)
{
	int ret;
	int i;
	
	
	ret = alloc_chrdev_region(&devno, 0, DEVICE_NUM, "virtdev");
	if (ret < 0)
		return ret;
	major = MAJOR(devno);
		
	devp = kzalloc(sizeof(struct virtdev_dev) * DEVICE_NUM, GFP_KERNEL);
	if (!devp)
	{
		ret = -ENOMEM;
		goto fail_malloc;
	}
	
	class = class_create(NULL, "virtdev");
	if (IS_ERR(class))
	{
		ret = PTR_ERR(class);
		printk(KERN_ERR "class create error %d\n", ret);
		goto fail_malloc;
	}
	class->devnode = chardev_devnode;
	
	for (i = 0; i < DEVICE_NUM; i++)
		virtdev_setup_cdev(devp + i, i);
	
	return 0;
	

fail_malloc:
	unregister_chrdev_region(devno, 1);
	return ret;
}


static void __exit virtdev_exit(void)
{
	int i;
	int devno;
	
	for (i = 0; i < DEVICE_NUM; i++)
	{
		devno = MKDEV(major, i);
		device_destroy(class, devno);
		
		cdev_del(&((devp+i)->cdev));
	}
	
	class_destroy(class);
	kfree(devp);
	unregister_chrdev_region(MKDEV(major, 0), DEVICE_NUM);
}

module_init(virtdev_init);
module_exit(virtdev_exit);

MODULE_AUTHOR("lql");
MODULE_LICENSE("GPL");

執行結果顯示:

載入模組是/proc/devices檔案中有virtdev裝置生成,/dev路徑下有virtdev0、virtdev1、virtdev2、virtdev3裝置檔案生成;

載入模組是/proc/devices檔案中刪除virtdev裝置,/dev路徑下刪除virtdev0、virtdev1、virtdev2、virtdev3裝置檔案;

相關推薦

程式碼編寫一個簡單字元裝置驅動——建立同類裝置

編寫同類型多個裝置字元驅動應注意一下幾個問題: 1、申請裝置號alloc_chrdev_region時須指定次裝置號範圍; 2、動態分配裝置空間時同時分配NUM個裝置的空間; 3、根據次裝置號和統一的主裝置號生成針對單個裝置的devno,然後完成cdev_add註冊; 4、

程式碼編寫一個簡單字元裝置驅動(自動建立裝置檔案)

說明: (1)該篇在上篇的基礎上實現了自動建立裝置檔案的功能; (2)自動建立檔案主要用到了class_create()、device_create()兩個函式,宣告在inclue/linux/device.h裡; (3)裝置檔案的建立和銷燬放在模組載入和解除安裝函式中;

如何編寫一個簡單的Linux驅動(三)——完善裝置驅動

前期知識   1.如何編寫一個簡單的Linux驅動(一)——驅動的基本框架   2.如何編寫一個簡單的Linux驅動(二)——裝置操作集file_operations 前言   在上一篇文章中,我們編寫裝置驅動遇到了不少問題:   (1) 註冊裝置時,裝置號需要程式設計師給定,每次編寫驅動時,程式設計師需要知

[Revit]開始編寫一個簡單外部命令

1 建立專案 以Visual Studio作為開發工具,測試平臺為Revit 2017 開啟VS,建立一個C# .NET Framwork類庫專案,選擇。.net框架版本為.NET Framwork 4.5.2,確定。 2 專案設定 引用相關RevitAPI.dll和RevitAPIUI.dll,

python練習編寫一個函數isIn,接受兩字符串作為參數,如果一個字符串是另一個字符串的一部分,返回True,否則返回False。

code pri 兩個 find int 字符 字符串 return 輸出 python練習:編寫一個函數isIn,接受兩個字符串作為參數,如果一個字符串是另一個字符串的一部分,返回True,否則返回False。 重難點:定義函數的方法。使用str類型的find()函數,可

一個簡單的MapReduce示例(MapReduce任務處理)

.lib exceptio apr private util sum length reat lin 一、需求   有一個列表,只有兩列:id、pro,記錄了id與pro的對應關系,但是在同一個id下,pro有可能是重復的。   現在需要寫一個程序,統計一下每個id下有

caffe finetuning時從源模型到目的模型一個layer的引數賦予到layer

    for(int shot_ = 0; shot_ < total_target_shot.size();++shot_){              int target_id = total_target_shot[shot_];       layer_init_flag[target_id

linux 驅動建立裝置

linux 驅動中建立一個裝置的例子很多,以下例子是建立多個字元裝置的例子,包括驅動部分和測試部分 驅動部分包括 globalmem.c  Makefile兩個檔案: globalmem.c file: #include <linux/module.h>

linux裝置驅動第三篇一個簡單字元裝置驅動

在linux裝置驅動第一篇:裝置驅動程式簡介中簡單介紹了字元驅動,本篇簡單介紹如何寫一個簡單的字元裝置驅動。本篇借鑑LDD中的原始碼,實現一個與硬體裝置無關的字元裝置驅動,僅僅操作從核心中分配的一些記憶體。 下面就開始學習如何寫一個簡單的字元裝置驅動。首先我們來分解一下字元

linux設備驅動第三篇一個簡單的字符設備驅動

提示 copy flags 驅動程序 相關 clas open ugo param 在linux設備驅動第一篇:設備驅動程序簡介中簡單介紹了字符驅動,本篇簡單介紹如何寫一個簡單的字符設備驅動。本篇借鑒LDD中的源碼,實現一個與硬件設備無關的字符設備驅動,僅僅操

C語言編寫一個程式統計輸入字串中,各個數字、空白字元、以及其他所有字元出現的次數。

#include<stdio.h> int main() {      int c = 0;      int num_count = 0;      int emp_count = 0;      int els_count = 0;            w

華為OJ開發一個簡單錯誤記錄功能小模組,能夠記錄出錯的程式碼所在的檔名稱和行號。

用到了類string的length(), size(),find_first_of(),find_last_of(),substr(),push_back()函式  #include <iostream> #include <algorithm> #

程式設計題編寫一個程式碼,將“i am from shanghai”倒置為“shanghai from am i”

題目要求:編寫一個程式碼,將“i am from shanghai”倒置為“shanghai from am i”,即將句子中單詞的位置調換,                    但不改變單詞內部的順序 #include <stdio.h> #include

【原】shell編寫一個簡單的jmeter自動化壓測腳本

image tac vbo 用戶數 osx dot png das uvc 在公司做壓力測試也挺長時間了,每次測試前環境數據準備都需要話費較長時間,所以一直在考慮能不能將整個過程實現自動化進行,於是就抽空寫了一個自動化腳本,當然這個腳本目前功能十分簡陋,代碼也不完善,很有很

Golang中使用heap編寫一個簡單高效的定時器模塊

true pop 邏輯 .com light 初始化 callback before cell 定時器模塊在服務端開發中非常重要,一個高性能的定時器模塊能夠大幅度提升引擎的運行效率。使用Golang和heap實現一個通用的定時器模塊,代碼來自:https://github.

手把手教你編寫一個簡單的PHP模塊形態的後門

cpp rest xtu job ring 事先 們的 original call 看到Freebuf 小編發表的用這個隱藏於PHP模塊中的rootkit,就能持久接管服務器文章,很感興趣,苦無作者沒留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一個非常流行

如何用Java編寫一個簡單的服務器和客戶機

exce 解決 對賬 location exceptio acc 明顯 隊列 客戶 今天我要向大家介紹的是自己編寫的一個比較簡單的服務器和客戶機程序,註意一下哦,比較簡單。好了,閑話休提,砸門直入主題。 小編先從客戶機和服務器的模型開始講解。

編寫一個簡單的TCP服務端和客戶端

不同的 大連 終端 服務器端 com 讀寫 所有 字數 資料 下面的實驗環境是linux系統。 效果如下: 1.啟動服務端程序,監聽在6666端口上 2.啟動客戶端,與服務端建立TCP連接 3.建立完TCP連接,在客戶端上向服務端發送消息 4.斷開

編寫一個簡單的單元測試用例

ide bsp span log 加減乘除 self teardown __main__ str 開發一個簡單的計算器,用於計算兩個數的加減乘除,示例: 1 class Calculator(): 2 ‘‘‘實現簡單的加減乘除‘‘‘ 3 def _