linux字元裝置驅動程式的編寫

來源:互聯網
上載者:User

在此涉及到兩個重要的結構體cdev和file_operations,前者為描述字元裝置,後者為裝置驅動程式的進入點。

對與file_operations的成員描述請看 file_operations中各項解析

我用圖表示下在核心2.6版本後新版本的字元裝置註冊

這是驅動程式應該做的,但是要真正地使用它,我們還必須還要建立裝置節點(linux的裝置操作都是標準的檔案操作,就是當作檔案來處理,所以必鬚生成相應的裝置檔案,在建立時,制定了主裝置號和次裝置號,就和裝置聯絡了起來)和插入核心,不使用的時候從核心中卸載掉

看這個圖

整個的過程貌似這個,虛線框中的是字元驅動應該做的事。驅動與系統核心緊密聯絡的,所以這個程式都有規定好的格式,這也是必須的。

這兩個函數是系統自動調用的

int init_module(void) 和void cleanup_module(void)

看函數名也知道這兩個函數是在什麼時候被調用,這兩個相當於入口和出口,而對硬體的操作函數都在file_operations中被定義。結構體中除了一個資料成員外,其他的都是函數指標。在init的過程中我們需要填充這些指標,當應用程式使用open等系統調用對裝置檔案進行操作的時候,就會通過file_operations將此系統調用對應到驅動程式中的相應的函數來進行具體的操作。

1.裝置號的分配

裝置號是一個數字,用來標示裝置。裝置檔案的建立要指明主裝置號和次裝置號。主裝置號表明裝置的類型,與一個確定的驅動程式對應,次裝置號通常用來標明不同屬性,例如不同的使用方法,不同的位置等,它標誌某個具體的物理裝置。

在2.6中,用dev_t類型來描述裝置號,它是一個32位類型,高12為主裝置號,後20為次裝置號。使用MAJOR和MINOR取主次裝置號,使用MKDEV來合并得到裝置號。

裝置號的分配分為兩種:靜態和動態。

靜態分配知道主裝置號,通過參數指定第一個裝置號,通常次裝置號為0,所以可以這麼得到MKDEV(主裝置號, 0)。

函數為int register_chrdev_region(dev_t first, unsigned int count, char* name)

count為裝置號數目,name為裝置名稱

動態分配通過參數僅需要第一個次裝置號,通常為0,和分配的數目

int alloc_chrdev_region(dev_t *dev, unsigned int fristminor, char* name)

動態時候需要注意,需要儲存分配到的主裝置號,不卸載裝置的時候有麻煩。

釋放已經分配的裝置號使用unregister_chrdev_region(dev_t first, unsigned int count),不管動態還是靜態。

2.字元裝置的註冊

在關聯cdev和裝置號之前,我們想來填充file_operations結構體,不然關聯了也沒有效果。

在linux中檔案的基本操作是開啟,釋放和讀寫,在file_operations中肯定是有的。我們需要需要根據規定的定義(參數格式)來實現他們,我們實現的為

static int demo_open(struct inode *inodeP, struct file *fileP)static int demo_release(struct inode *inodeP, struct file *fileP)static ssize_t demo_read(struct file *fileP, char *buf, size_t count, loff_t *ppos)static ssize_t demo_write(struct file *fileP, const char *buf, size_t count, loff_t *ppos)

填充結構體

static struct file_operations demo_fops = {owner:THIS_MODULE,read:demo_read,write:demo_write,open:demo_open,release:demo_release,};

對cdev結構體的操作有下面幾個函數

void cdev_init(struct cdev *, const struct file_operations *);//初始化,建立cdev和file_operation 之間的串連struct cdev *cdev_alloc(void); //動態申請一個cdev記憶體void cdev_put(struct cdev *p);   //釋放int cdev_add(struct cdev *, dev_t, unsigned);  //註冊裝置,通常發生在驅動模組的載入函數中void cdev_del(struct cdev *);//登出裝置,通常發生在驅動模組的卸載函數中

下面我貼出整個demo的源碼

#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/cdev.h>#include <linux/uaccess.h>#define DEV_NAME "demo"#define MAX_BUF_SIZE 1024static struct cdev demo_cdev;unsigned int major = 0;static char *data = NULL;/** * [demo_open description] * @param  inodeP * @param  fileP * @return */static int demo_open(struct inode *inodeP, struct file *fileP){printk("device open success!\n");    data = (char*)kmalloc(sizeof(char) * MAX_BUF_SIZE, GFP_KERNEL);    if (!data) {    return -ENOMEM;    }    memset(data, 0, MAX_BUF_SIZE);return 0;}static int demo_release(struct inode *inodeP, struct file *fileP){    printk("release device\n");    if (data) {    kfree(data);    data = NULL;    }return 0;}static ssize_t demo_read(struct file *fileP, char *buf, size_t count, loff_t *ppos){if (count < 0 ) {/* code */return -EINVAL;}if (count > MAX_BUF_SIZE) {count = MAX_BUF_SIZE;}if (copy_to_user(buf, data, count) == EFAULT) {/* code */return -EFAULT;}    printk("user read data from device!\n");return count;}static ssize_t demo_write(struct file *fileP, const char *buf, size_t count, loff_t *ppos){if (count < 0 ) {/* code */return -EINVAL;}if (count > MAX_BUF_SIZE) {count = MAX_BUF_SIZE;}    memset(data, 0, MAX_BUF_SIZE);if (copy_from_user(data, buf, count) == EFAULT) {return -EFAULT;}printk("user write data to device\n");return count;}static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops){int err;int devno;devno = MKDEV(major, minor);cdev_init(dev, fops);dev->owner = THIS_MODULE;dev->ops = fops;err = cdev_add(dev, devno, 1);if (err) {printk(KERN_NOTICE" Error %d adding dev %d", err, minor);}}static struct file_operations demo_fops = {owner:THIS_MODULE,read:demo_read,write:demo_write,open:demo_open,release:demo_release,};/** * [init_module description] 載入驅動模組時調用 * @return  [description] */int init_module(void) {int re;dev_t dev = MKDEV(major, 0);if (major) {re = register_chrdev_region(dev, 1, DEV_NAME);} else {re = alloc_chrdev_region(&dev, 0, 1, DEV_NAME);}if (re < 0) {printk(KERN_WARNING" Demo dev-->unable to get major %d\n", major);return re;}/** * 如果是動態分配的則要改變原來的major不然在調用setup_cdev時會不能關聯裝置和裝置號, *  * 在cleanup_module時也不能從/proc/devices中刪除相應的裝置號項目 */major = MAJOR(dev);setup_cdev(&demo_cdev, 0, &demo_fops);printk("The major of the demo device is %d\n", major);return 0;}void cleanup_module(void){cdev_del(&demo_cdev);unregister_chrdev_region(MKDEV(major, 0), 1);printk("Demo device uninstalled\n");}

Makefile的編寫

在編譯這些核心模組時,建議使用你kbuild

詳細情況見

http://www.mjmwired.net/kernel/Documentation/kbuild/modules.txt

obj-m := demo.oall:$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

上面是Makefile檔案


寫了載入和卸載的shell指令碼

#!/bin/bashmodule="demo"device="demo"rm -f /dev/${device}/sbin/insmod -f ./$module.ko $* || exit 1#尋找/proc/devices檔案中demo對應的裝置號major=`cat /proc/devices | awk -v var=${module} '{if($2==var)print $1}'`echo $majormknod /dev/${device} c $major 0

#!/bin/bashmodule="demo"device="demo"/sbin/rmmod $module $* || exit 1rm -f /dev/${device}exit 0

測試的代碼

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h>#include <fcntl.h>#define DEV_NAME "/dev/demo"#define BUF_SIZE 1024int main(int argc, char const *argv[]){int fd;char buffer[BUF_SIZE];fd = open(DEV_NAME, O_RDWR);if (fd < 0) {perror("open dev fail!\n");return -1;}    do {    printf("Input some worlds to kernel(enter 'quit' to exit)\n");    memset(buffer, 0 ,BUF_SIZE);    if (fgets(buffer, BUF_SIZE, stdin) == NULL) {    perror("fgets error!\n");    break;    }    buffer[strlen(buffer) - 1] = '\0';    if (write(fd, buffer, strlen(buffer)) < 0) {    perror("write error\n");    break;    }    if (read(fd, buffer, BUF_SIZE) < 0) {    perror("read error\n");    break;    } else {    printf("The read string is from kernel : %s\n", buffer);    }    } while(strncmp(buffer, "quit" , 4));    close(fd);return 0;}

無圖無真相

載入和卸載可以用lsmod命令察看,執行了load.sh後,也可以去看看/proc/devives檔案

補充一下,當major,也就是主裝置號為0時,說明是動態註冊。



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.