我如何学习为Linux编写char设备驱动程序?

时间:2009-08-25 18:58:44

标签: linux device-driver linux-device-driver

如何在Linux中编写char设备驱动程序?

6 个答案:

答案 0 :(得分:4)

一个很好的例子是Linux“软件狗”或软件看门狗定时器。加载后,它将监视一个特殊的写入设备,并根据这些写入的频率采取措施。

它还向您展示了如何实现一个非常有用的rudamentary ioctl接口。

要查看的文件是drivers / watchdog / softdog.c

如果你通过实例学习,那么这是一个非常好的开始。其他人建议的基本字符设备(null,random等)也很好,但是没有充分展示你需要如何实现ioctl()接口。

旁注,我相信司机是由艾伦考克斯写的。如果您要从示例中学习,那么研究顶级维护者的工作绝不是一个坏主意。您可以非常肯定该驱动程序还说明了遵守正确的Linux标准。

就驱动程序而言(在Linux中),字符驱动程序是最容易编写的,也是最有价值的,因为您可以看到代码运行得非常快。祝你好运,快乐的黑客。

答案 1 :(得分:3)

阅读本书:O'Reilly出版的Linux Device Drivers

帮了我很多。

答案 2 :(得分:1)

我最喜欢的学习内核如何工作的书,BY FAR(我读过大部分内容)是:

Linux Kernel Development (2nd Edition)

这本书相当简短,首先阅读,然后阅读关于驱动程序的O'Reilly书。

答案 3 :(得分:1)

阅读linux设备驱动程序第3版。好的是开始编码。我只是粘贴一个简单的字符驱动程序,以便您可以开始编码。

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h> /*this is the file structure, file open read close */
#include<linux/cdev.h> /* this is for character device, makes cdev avilable*/
#include<linux/semaphore.h> /* this is for the semaphore*/
#include<linux/uaccess.h> /*this is for copy_user vice vers*/

int chardev_init(void);
void chardev_exit(void);
static int device_open(struct inode *, struct file *);
static int device_close(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static loff_t device_lseek(struct file *file, loff_t offset, int orig);

/*new code*/
#define BUFFER_SIZE 1024
static char device_buffer[BUFFER_SIZE];
struct semaphore sem;
struct cdev *mcdev; /*this is the name of my char driver that i will be registering*/
int major_number; /* will store the major number extracted by dev_t*/
int ret; /*used to return values*/
dev_t dev_num; /*will hold the major number that the kernel gives*/

#define DEVICENAME "megharajchard"

/*inode reffers to the actual file on disk*/
static int device_open(struct inode *inode, struct file *filp) {
    if(down_interruptible(&sem) != 0) {
        printk(KERN_ALERT "megharajchard : the device has been opened by some other device, unable to open lock\n");
        return -1;
    }
    //buff_rptr = buff_wptr = device_buffer;
    printk(KERN_INFO "megharajchard : device opened succesfully\n");
    return 0;
}

static ssize_t device_read(struct file *fp, char *buff, size_t length, loff_t *ppos) {
    int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
    int bytes_to_read; /* gives the number of bytes to read*/
    int bytes_read;/*number of bytes actually read*/
    maxbytes = BUFFER_SIZE - *ppos;
    if(maxbytes > length) 
        bytes_to_read = length;
    else
        bytes_to_read = maxbytes;
    if(bytes_to_read == 0)
        printk(KERN_INFO "megharajchard : Reached the end of the device\n");
    bytes_read = bytes_to_read - copy_to_user(buff, device_buffer + *ppos, bytes_to_read);
    printk(KERN_INFO "megharajchard : device has been read %d\n",bytes_read);
    *ppos += bytes_read;
    printk(KERN_INFO "megharajchard : device has been read\n");
    return bytes_read;
}

static ssize_t device_write(struct file *fp, const char *buff, size_t length, loff_t *ppos) {
    int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
    int bytes_to_write; /* gives the number of bytes to write*/
    int bytes_writen;/*number of bytes actually writen*/
    maxbytes = BUFFER_SIZE - *ppos;
    if(maxbytes > length) 
        bytes_to_write = length;
    else
        bytes_to_write = maxbytes;
    bytes_writen = bytes_to_write - copy_from_user(device_buffer + *ppos, buff, bytes_to_write);
    printk(KERN_INFO "megharajchard : device has been written %d\n",bytes_writen);
    *ppos += bytes_writen;
    printk(KERN_INFO "megharajchard : device has been written\n");
    return bytes_writen;
}

static loff_t device_lseek(struct file *file, loff_t offset, int orig) {
    loff_t new_pos = 0;
    printk(KERN_INFO "megharajchard : lseek function in work\n");
    switch(orig) {
        case 0 : /*seek set*/
            new_pos = offset;
            break;
        case 1 : /*seek cur*/
            new_pos = file->f_pos + offset;
            break;
        case 2 : /*seek end*/
            new_pos = BUFFER_SIZE - offset;
            break;
    }
    if(new_pos > BUFFER_SIZE)
        new_pos = BUFFER_SIZE;
    if(new_pos < 0)
        new_pos = 0;
    file->f_pos = new_pos;
    return new_pos;
}

static int device_close(struct inode *inode, struct file *filp) {
    up(&sem);
    printk(KERN_INFO "megharajchard : device has been closed\n");
    return ret;
}

struct file_operations fops = { /* these are the file operations provided by our driver */
    .owner = THIS_MODULE, /*prevents unloading when operations are in use*/
    .open = device_open,  /*to open the device*/
    .write = device_write, /*to write to the device*/
    .read = device_read, /*to read the device*/
    .release = device_close, /*to close the device*/
    .llseek = device_lseek
};


int chardev_init(void) 
{
    /* we will get the major number dynamically this is recommended please read ldd3*/
    ret = alloc_chrdev_region(&dev_num,0,1,DEVICENAME);
    if(ret < 0) {
        printk(KERN_ALERT " megharajchard : failed to allocate major number\n");
        return ret;
    }
    else
        printk(KERN_INFO " megharajchard : mjor number allocated succesful\n");
    major_number = MAJOR(dev_num);
    printk(KERN_INFO "megharajchard : major number of our device is %d\n",major_number);
    printk(KERN_INFO "megharajchard : to use mknod /dev/%s c %d 0\n",DEVICENAME,major_number);

    mcdev = cdev_alloc(); /*create, allocate and initialize our cdev structure*/
    mcdev->ops = &fops;   /*fops stand for our file operations*/
    mcdev->owner = THIS_MODULE;

    /*we have created and initialized our cdev structure now we need to add it to the kernel*/
    ret = cdev_add(mcdev,dev_num,1);
    if(ret < 0) {
        printk(KERN_ALERT "megharajchard : device adding to the kerknel failed\n");
        return ret;
    }
    else
        printk(KERN_INFO "megharajchard : device additin to the kernel succesful\n");
    sema_init(&sem,1);  /* initial value to one*/

    return 0;
}

void chardev_exit(void)
{
    cdev_del(mcdev); /*removing the structure that we added previously*/
    printk(KERN_INFO " megharajchard : removed the mcdev from kernel\n");

    unregister_chrdev_region(dev_num,1);
    printk(KERN_INFO "megharajchard : unregistered the device numbers\n");
    printk(KERN_ALERT " megharajchard : character driver is exiting\n");
}

MODULE_AUTHOR("A G MEGHARAJ(agmegharaj@gmail.com)");
MODULE_DESCRIPTION("A BASIC CHAR DRIVER");
//MODULE_LICENCE("GPL");    

module_init(chardev_init);
module_exit(chardev_exit);

这是make文件。

obj-m   := megharajchard.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

加载脚本。确保主要号码是251或者相应地改变它。

#!/bin/sh

sudo insmod megharajchard.ko
sudo mknod /dev/megharajchard c 251 0
sudo chmod 777 /dev/megharajchard

卸载脚本,

#!/bin/sh

sudo rmmod megharajchard
sudo rm /dev/megharajchard

也是测试设备操作的程序

#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<malloc.h>

#define DEVICE "/dev/megharajchard"
//#define DEVICE "megharaj.txt"
int debug = 1, fd = 0;

int write_device() {
    int write_length = 0;
    ssize_t ret;
        char *data = (char *)malloc(1024 * sizeof(char));
    printf("please enter the data to write into device\n");
    scanf(" %[^\n]",data); /* a space added after"so that it reads white space, %[^\n] is addeed so that it takes input until new line*/
    write_length = strlen(data);
    if(debug)printf("the length of dat written = %d\n",write_length);
    ret = write(fd, data, write_length);
    if(ret == -1)
        printf("writting failed\n");
    else
        printf("writting success\n");
    if(debug)fflush(stdout);/*not to miss any log*/
    free(data);
return 0;
}

int read_device() {
    int read_length = 0;
    ssize_t ret;
        char *data = (char *)malloc(1024 * sizeof(char));
    printf("enter the length of the buffer to read\n");
    scanf("%d",&read_length);
    if(debug)printf("the read length selected is %d\n",read_length);
    memset(data,0,sizeof(data));
    data[0] = '0\';
    ret = read(fd,data,read_length);
    printf("DEVICE_READ : %s\n",data);
    if(ret == -1)
        printf("reading failed\n");
    else
        printf("reading success\n");
    if(debug)fflush(stdout);/*not to miss any log*/
    free(data);
return 0;
}

int lseek_device() {
    int lseek_offset = 0,seek_value = 0;
    int counter = 0; /* to check if function called multiple times or loop*/
    counter++;
    printf("counter value = %d\n",counter);
    printf("enter the seek offset\n");
    scanf("%d",&lseek_offset);
    if(debug) printf("seek_offset selected is %d\n",lseek_offset);
    printf("1 for SEEK_SET, 2 for SEEK_CUR and 3 for SEEK_END\n");
    scanf("%d", &seek_value);
    printf("seek value = %d\n", seek_value);
    switch(seek_value) {
        case 1: lseek(fd,lseek_offset,SEEK_SET);
            return 0;
            break;  
        case 2: lseek(fd,lseek_offset,SEEK_CUR);
            return 0;
            break;          
        case 3: lseek(fd,lseek_offset,SEEK_END);
            return 0;
            break;  
        default : printf("unknown  option selected, please enter right one\n");
            break;  
    }
    /*if(seek_value == 1) {
        printf("seek value = %d\n", seek_value);
        lseek(fd,lseek_offset,SEEK_SET);
        return 0;
    }
    if(seek_value == 2) {
        lseek(fd,lseek_offset,SEEK_CUR);
        return 0;
    }
    if(seek_value == 3) {
        lseek(fd,lseek_offset,SEEK_END);
        return 0;
    }*/
    if(debug)fflush(stdout);/*not to miss any log*/
return 0;
}

int lseek_write() {
    lseek_device();
    write_device();
return 0;
}

int lseek_read() {
    lseek_device();
    read_device();
return 0;
}

int main()
{
    int value = 0;
    if(access(DEVICE, F_OK) == -1) {
        printf("module %s not loaded\n",DEVICE);
        return 0;
    }
    else
        printf("module %s loaded, will be used\n",DEVICE);

    while(1) {
    printf("\t\tplease enter 1 to write\n \
                     2 to read\n \
                     3 to lseek and write\
                 4 to lseek and read\n");
        scanf("%d",&value);
        switch(value) {
            case 1 :printf("write option selected\n");
            fd = open(DEVICE, O_RDWR);
            write_device();
            close(fd); /*closing the device*/
            break;
            case 2 :printf("read option selected\n"); 
            /* dont know why but i am suppoesed to open it for writing and close it, i cant keep  open and read.
            its not working, need to sort out why is that so */
            fd = open(DEVICE, O_RDWR);
            read_device();
            close(fd); /*closing the device*/
            break;
            case 3 :printf("lseek  option selected\n");
            fd = open(DEVICE, O_RDWR);
            lseek_write();
            close(fd); /*closing the device*/
            break;  
            case 4 :printf("lseek  option selected\n");
            fd = open(DEVICE, O_RDWR);
            lseek_read();   
            close(fd); /*closing the device*/
            break;
            default : printf("unknown  option selected, please enter right one\n");
            break;
        }
    }
    return 0;
}

答案 4 :(得分:0)

在标准内核中查看一些非常简单的标准 - “null”,“zero”,“mem”,“random”等。它们显示了简单的实现。

显然,如果您正在驾驶真正的硬件,那就更复杂了 - 您需要了解如何与您的设备的硬件以及子系统API(PCI,USB等)进行交互。您可能还需要了解如何使用中断,内核定时器等。

答案 5 :(得分:0)

  • 只需从此处http://www.linuxforu.com/2011/02/linux-character-drivers/....Go查看角色驱动程序骨架,然后阅读此处的所有主题,彻底了解。(这是一个教程 - 所以如上所述)。

  • 了解“copy_to_user”和“copy_from_user”函​​数的工作原理,可以在驱动函数回调的读/写部分中使用。

完成此操作后,请开始阅读基本的“tty”驱动程序。

首先关注驱动程序注册架构,这意味着: -

  1. 查看要填充的结构 - 例如: - struct file_operations f_ops = ....
  2. 负责向核心注册特定结构的功能。例如: - _register_driver。 完成上述操作后,查看驱动程序所需的功能(策略),然后考虑实现该策略的方式(称为机制) - 策略和机制允许您区分驱动程序的各个方面。
  3. 编写编译makefile(如果你有多个文件则很难 - 但不是很难)。
  4. 尝试解决错误和警告, 你将度过难关。
  5. 编写机制时,永远不要忘记它为用户空间中的应用程序提供的内容。