Как я могу научиться писать драйверы char для Linux? - PullRequest
2 голосов
/ 25 августа 2009

Как написать драйверы устройства char в Linux?

Ответы [ 6 ]

4 голосов
/ 26 августа 2009

Очень хороший пример - Linux "softdog" или программный сторожевой таймер. После загрузки он будет наблюдать за специальным устройством для записи и предпринимать действия в зависимости от частоты этих записей.

Также показано, как реализовать рудиментарный интерфейс ioctl, что очень полезно.

Файл для просмотра: drivers / watchdog / softdog.c

Если вы учитесь на собственном примере, это очень хороший вариант для начала. Базовые символьные устройства (null, random и т. Д.), Как предлагают другие, также хороши, но не показывают, как правильно реализовать интерфейс ioctl ().

Примечание: я думаю, что драйвер был написан Аланом Коксом. Если вы собираетесь учиться на примере, то никогда не бывает плохой идеей изучать работу сопровождающего высшего уровня. Вы можете быть совершенно уверены, что драйвер также иллюстрирует соблюдение соответствующих стандартов Linux.

Что касается драйверов (в Linux), то символьные драйверы легче всего написать, а также они наиболее полезны, так как вы можете видеть, как ваш код работает очень быстро. Удачи и счастливого взлома.

3 голосов
/ 25 августа 2009

Прочитайте эту книгу: Драйверы устройств Linux , опубликованные O'Reilly.

Мне очень помогли.

1 голос
/ 19 сентября 2013

Прочитайте драйвер устройства Linux 3-е издание. И хорошо, начать кодирование. Я просто вставляю простой драйвер char, чтобы вы могли начать кодирование.

#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

также c программой для проверки работы вашего устройства

#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;
}
1 голос
/ 25 августа 2009

Моя любимая книга о том, как работает ядро, ПО ДАЛЬНЕЙШЕМУ (и я прочитал большинство из них):

Разработка ядра Linux (2-е издание)

Эта книга довольно короткая, сначала прочтите ее, а затем прочитайте книгу О'Рейли о драйверах.

0 голосов
/ 30 октября 2012
  • Просто посмотрите здесь скелет драйвера персонажа 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. Как только вы закончите с вышеуказанным, посмотрите, какую функциональность вы хотите использовать с драйвером (policy), а затем подумайте, как реализовать эту политику (так называемый механизм) - политика и механизм позволяют вам различать различные аспекты драйвера. *
  3. написать сборочные файлы сборки (сложно, если у вас есть несколько файлов, но не сложно).
  4. Попробуйте устранить ошибки и предупреждения, и ты пройдешь.

При написании механизма никогда не забывайте, что он предлагает приложениям в пространстве пользователя.

0 голосов
/ 26 августа 2009

Посмотрите на некоторые из действительно простых стандартных - "ноль", "ноль", "mem", "random" и т. Д. В стандартном ядре. Они показывают простую реализацию.

Очевидно, что если вы работаете с реальным оборудованием, это более сложно - вам нужно понять, как взаимодействовать с оборудованием, а также с API подсистемы (PCI, USB и т. Д.) Для вашего устройства. Вам также может понадобиться понять, как использовать прерывания, таймеры ядра и т. Д.

...