Linux 字符設備驅動模板

在Linux內核裏面,設備(device)主要分爲字符設備,塊設備,網絡設備,字符設備驅動是Linux驅動基礎,在看《Linux 設備驅動開發詳解》這本書的過程中,把字符設備相知識記錄整理如下。

字符設備驅動的組成

字符設備驅動模塊加載和卸載函數

//設備結構體
struct xxx_dev_t{
	struct cdev cdev;
	//......
}xxx_dev;

static struct xxx_dev *xxx_dev;
const struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.read = xxx_read,
	.write = xxx_write,
	//......
}
stattic int __init xxx_init(void)
{
	//......
	cdev_init(&xxx_dev->cdev, &xxx_fops);
	xxx_dev->cdev.owner = THIS_MODULE;
	//獲取字符設備號
	alloc_chrdev_region(&xxx_dev_no, 0, DEVICE_COUNT, DEVICE_NAME);
	//註冊設備
	ret = cdev_add(&xxx_dev->cdev, xxx_dev_no, 1);
    //......
}

static void __exit xxx_exit(void)
{	
	//註銷設備
	cdev_del(&xxx_dev->cdev);
	//註銷設備號
	unregister_chrdev_region(xxx_dev_no,1);
	//......
}

字符驅動操作成員函數

static int xxx_open(struct inode *inode, struct file *filp) 
{
	//......
	return 0;
}

static int xxx_release(struct inode *inode, struct file *filp) 
{
	//......
    return 0;
}

static long xxx_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{
	//...
	switch (cmd) {
    case xxx_cmd1:
       	//......
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static ssize_t xxx_read(struct file *filp, char __user *buf,
                              size_t size, loff_t *ppos)
{
	//...
	copy_to_user(buf, xxx, count);
	//...
	return count;
}

static ssize_t xxx_write(struct file *filp, const char __user *buf,
                               size_t size, loff_t *ppos) 
{
	//...
	copy_from_user(xxx, buf, count);
	//...
	return count;
}

static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
	//......
	switch (orig) {
    case 0:/*SEEK_SET  Seek from beginning of file*/
        //......
        filp->f_pos = (unsigned int)offset;
        ret = filp->f_pos;
        break;
    case 1: /*SEEK_CUR Seek from current position*/
       	//......
        filp->f_pos += offset;
        ret = filp->f_pos;
        break;
    case 2:/* SEEK_END Seek from end of file */
    	//......
    	filp->f_pos -= offset;
        ret = filp->f_pos;
        break;
    default:
        ret = -EFAULT;
        break;
    }
    return ret;
}

DEMO測試

驅動Demo完整代碼

通過以下命令加載和查看驅動

	$: make
    $: sudo demsg -c
    $: sudo insmod multi_globalmem.ko
    $: lsmod
    $: sudo mknod /dev/globalmem c 230 0   //創建設備節點
    $: sudo dmesg

用戶空間測試代碼如下:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DEVICE_NAME "/dev/globalmem"
#define MAX_SIZE 1020
#define MEM_CLEAR 0x01

static void usage(char *name) {
    fprintf(stderr, "Usage:%s [message]\n", name);
}

int main(int argc, char **argv) {
    if (argc != 2) {
        usage(argv[0]);
        return -1;
    }
    char *msg = argv[1];
    char buf[MAX_SIZE];

    //open the file
    int fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        fprintf(stderr, "open %s failed\n", DEVICE_NAME);
        return -1;
    }

    //clear the file
    if (ioctl(fd, 1, 0) < 0) {
        fprintf(stderr, "clear %s filed\n", DEVICE_NAME);
        close(fd);
        return -1;
    }

    //write message to file
    if (write(fd, msg, strlen(msg)) <= 0) {
        fprintf(stderr, "write %s error\n", DEVICE_NAME);
    }

    if (lseek(fd, -strlen(msg), SEEK_CUR) < 0) {
        fprintf(stderr, "seek %s error\n", DEVICE_NAME);
    }
    
    //read from file
    if (read(fd, buf, MAX_SIZE) <= 0) {
        fprintf(stderr, "read %s failed!\n", DEVICE_NAME);
    } else {
        fprintf(stderr, "read suceess: buf=%s\n", buf);
    }

    close(fd);
    return 0;
}

使用以下命令測試:

	$: gcc test_globalmem.c -o test_globalmem
    $: sudo ./test_globalmem message

Debug message 如下:
d

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章