GNU/Linux >> Znalost Linux >  >> Linux

Jak vytvořit jednoduchý atribut třídy sysfs v jádře Linuxu v3.2

I když jsou mé znalosti o tématu stále poměrně nízké, odpověď zveřejním jen kvůli stáří této otázky. Pokud má někdo lepší odpověď, napište prosím! :)

Za prvé, budu předpokládat, že jste četli celou tuto kapitolu (konkrétně o kobjects &ksets). Takže téměř každá struktura v modelu ovladače zařízení má tyto roztomile zahrnuty. Pokud chcete manipulovat s kobjectem pro samotnou třídu (nejste si jisti, zda je to moudré nebo ne), je to váš struct class 's dev_kobj člen.

Chcete však manipulovat s atributy této třídy. Věřím uděláte to tak, že definujete (obvykle statické) pole zakončené NULL následovně a poté přiřadíte jeho adresu k struct class 's class_attrs člen (převzato z drivers/uwb/driver.c ):

static struct class_attribute uwb_class_attrs[] = {
    __ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO,
           beacon_timeout_ms_show, beacon_timeout_ms_store),
    __ATTR_NULL,
};

/** Device model classes */
struct class uwb_rc_class = {
    .name        = "uwb_rc",
    .class_attrs = uwb_class_attrs,
};

Když něco neumím používat, obvykle git grep úložiště pro někoho jiného, ​​kdo jej použil, a pokusit se z něj tímto způsobem poučit. Zdá se, že právě proto mají tendenci říkat jádru „hackeři“ a ne „vývojáři“.


V odkazu níže je dobrý návod

http://pete.akeo.ie/2011/08/writing-linux-device-driver-for-kernels.html

parrot_driver.c:

/*
 * Linux 2.6 and 3.0 'parrot' sample device driver
 *
 * Copyright (c) 2011, Pete Batard <[email protected]>
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/kfifo.h>
#include "parrot_driver.h"

/* Module information */
MODULE_AUTHOR(AUTHOR);
MODULE_DESCRIPTION(DESCRIPTION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

/* Device variables */
static struct class* parrot_class = NULL;
static struct device* parrot_device = NULL;
static int parrot_major;
/* Flag used with the one_shot mode */
static bool message_read;
/* A mutex will ensure that only one process accesses our device */
static DEFINE_MUTEX(parrot_device_mutex);
/* Use a Kernel FIFO for read operations */
static DECLARE_KFIFO(parrot_msg_fifo, char, PARROT_MSG_FIFO_SIZE);
/* This table keeps track of each message length in the FIFO */
static unsigned int parrot_msg_len[PARROT_MSG_FIFO_MAX];
/* Read and write index for the table above */
static int parrot_msg_idx_rd, parrot_msg_idx_wr;

/* Module parameters that can be provided on insmod */
static bool debug = false;  /* print extra debug info */
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "enable debug info (default: false)");
static bool one_shot = true;    /* only read a single message after open() */
module_param(one_shot, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "disable the readout of multiple messages at once (default: true)");


static int parrot_device_open(struct inode* inode, struct file* filp)
{
    dbg("");

    /* Our sample device does not allow write access */
    if ( ((filp->f_flags & O_ACCMODE) == O_WRONLY)
      || ((filp->f_flags & O_ACCMODE) == O_RDWR) ) {
        warn("write access is prohibited\n");
        return -EACCES;
    }

    /* Ensure that only one process has access to our device at any one time
    * For more info on concurrent accesses, see http://lwn.net/images/pdf/LDD3/ch05.pdf */
    if (!mutex_trylock(&parrot_device_mutex)) {
        warn("another process is accessing the device\n");
        return -EBUSY;
    }

    message_read = false;
    return 0;
}

static int parrot_device_close(struct inode* inode, struct file* filp)
{
    dbg("");
    mutex_unlock(&parrot_device_mutex);
    return 0;
}

static ssize_t parrot_device_read(struct file* filp, char __user *buffer, size_t length, loff_t* offset)
{
    int retval;
    unsigned int copied;

    /* The default from 'cat' is to issue multiple reads until the FIFO is depleted
     * one_shot avoids that */
    if (one_shot && message_read) return 0;
    dbg("");

    if (kfifo_is_empty(&parrot_msg_fifo)) {
        dbg("no message in fifo\n");
        return 0;
    }

    retval = kfifo_to_user(&parrot_msg_fifo, buffer, parrot_msg_len[parrot_msg_idx_rd], &copied);
    /* Ignore short reads (but warn about them) */
    if (parrot_msg_len[parrot_msg_idx_rd] != copied) {
        warn("short read detected\n");
    }
    /* loop into the message length table */
    parrot_msg_idx_rd = (parrot_msg_idx_rd+1)%PARROT_MSG_FIFO_MAX;
    message_read = true;

    return retval ? retval : copied;
}

/* The file_operation scructure tells the kernel which device operations are handled.
 * For a list of available file operations, see http://lwn.net/images/pdf/LDD3/ch03.pdf */
static struct file_operations fops = {
    .read = parrot_device_read,
    .open = parrot_device_open,
    .release = parrot_device_close
};

/* Placing data into the read FIFO is done through sysfs */
static ssize_t sys_add_to_fifo(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
    unsigned int copied;

    dbg("");
    if (kfifo_avail(&parrot_msg_fifo) < count) {
        warn("not enough space left on fifo\n");
        return -ENOSPC;
    }
    if ((parrot_msg_idx_wr+1)%PARROT_MSG_FIFO_MAX == parrot_msg_idx_rd) {
        /* We've looped into our message length table */
        warn("message length table is full\n");
        return -ENOSPC;
    }

    /* The buffer is already in kernel space, so no need for ..._from_user() */
    copied = kfifo_in(&parrot_msg_fifo, buf, count);
    parrot_msg_len[parrot_msg_idx_wr] = copied;
    if (copied != count) {
        warn("short write detected\n");
    }
    parrot_msg_idx_wr = (parrot_msg_idx_wr+1)%PARROT_MSG_FIFO_MAX;

    return copied;
}

/* This sysfs entry resets the FIFO */
static ssize_t sys_reset(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
    dbg("");

    /* Ideally, we would have a mutex around the FIFO, to ensure that we don't reset while in use.
     * To keep this sample simple, and because this is a sysfs operation, we don't do that */
    kfifo_reset(&parrot_msg_fifo);
    parrot_msg_idx_rd = parrot_msg_idx_wr = 0;

    return count;
}

/* Declare the sysfs entries. The macros create instances of dev_attr_fifo and dev_attr_reset */
static DEVICE_ATTR(fifo, S_IWUSR, NULL, sys_add_to_fifo);
static DEVICE_ATTR(reset, S_IWUSR, NULL, sys_reset);

/* Module initialization and release */
static int __init parrot_module_init(void)
{
    int retval;
    dbg("");

    /* First, see if we can dynamically allocate a major for our device */
    parrot_major = register_chrdev(0, DEVICE_NAME, &fops);
    if (parrot_major < 0) {
        err("failed to register device: error %d\n", parrot_major);
        retval = parrot_major;
        goto failed_chrdevreg;
    }

    /* We can either tie our device to a bus (existing, or one that we create)
     * or use a "virtual" device class. For this example, we choose the latter */
    parrot_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(parrot_class)) {
        err("failed to register device class '%s'\n", CLASS_NAME);
        retval = PTR_ERR(parrot_class);
        goto failed_classreg;
    }

    /* With a class, the easiest way to instantiate a device is to call device_create() */
    parrot_device = device_create(parrot_class, NULL, MKDEV(parrot_major, 0), NULL, CLASS_NAME "_" DEVICE_NAME);
    if (IS_ERR(parrot_device)) {
        err("failed to create device '%s_%s'\n", CLASS_NAME, DEVICE_NAME);
        retval = PTR_ERR(parrot_device);
        goto failed_devreg;
    }

    /* Now we can create the sysfs endpoints (don't care about errors).
     * dev_attr_fifo and dev_attr_reset come from the DEVICE_ATTR(...) earlier */
    retval = device_create_file(parrot_device, &dev_attr_fifo);
    if (retval < 0) {
        warn("failed to create write /sys endpoint - continuing without\n");
    }
    retval = device_create_file(parrot_device, &dev_attr_reset);
    if (retval < 0) {
        warn("failed to create reset /sys endpoint - continuing without\n");
    }

    mutex_init(&parrot_device_mutex);
    /* This device uses a Kernel FIFO for its read operation */
    INIT_KFIFO(parrot_msg_fifo);
    parrot_msg_idx_rd = parrot_msg_idx_wr = 0;

    return 0;

failed_devreg:
    class_unregister(parrot_class);
    class_destroy(parrot_class);
failed_classreg:
    unregister_chrdev(parrot_major, DEVICE_NAME);
failed_chrdevreg:
    return -1;
}

static void __exit parrot_module_exit(void)
{
    dbg("");
    device_remove_file(parrot_device, &dev_attr_fifo);
    device_remove_file(parrot_device, &dev_attr_reset);
    device_destroy(parrot_class, MKDEV(parrot_major, 0));
    class_unregister(parrot_class);
    class_destroy(parrot_class);
    unregister_chrdev(parrot_major, DEVICE_NAME);
}

/* Let the kernel know the calls for module init and exit */
module_init(parrot_module_init);
module_exit(parrot_module_exit);

parrot_driver.h:

/*
 * Linux 2.6 and 3.0 'parrot' sample device driver
 *
 * Copyright (c) 2011, Pete Batard <[email protected]>
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#define DEVICE_NAME "device"
#define CLASS_NAME "parrot"
#define PARROT_MSG_FIFO_SIZE 1024
#define PARROT_MSG_FIFO_MAX  128

#define AUTHOR "Pete Batard <[email protected]>"
#define DESCRIPTION "'parrot' sample device driver"
#define VERSION "0.3"

/* We'll use our own macros for printk */
#define dbg(format, arg...) do { if (debug) pr_info(CLASS_NAME ": %s: " format , __FUNCTION__ , ## arg); } while (0)
#define err(format, arg...) pr_err(CLASS_NAME ": " format, ## arg)
#define info(format, arg...) pr_info(CLASS_NAME ": " format, ## arg)
#define warn(format, arg...) pr_warn(CLASS_NAME ": " format, ## arg)

Minimální spustitelný příklad

Použití:

insmod /sysfs.ko
cd /sys/kernel/lkmc_sysfs
printf 12345 >foo
cat foo
# => 1234
dd if=foo bs=1 count=2 skip=1 status=none
# => 23

sysfs.c

#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <uapi/linux/stat.h> /* S_IRUSR, S_IWUSR  */

enum { FOO_SIZE_MAX = 4 };
static int foo_size;
static char foo_tmp[FOO_SIZE_MAX];

static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
        char *buff)
{
    strncpy(buff, foo_tmp, foo_size);
    return foo_size;
}

static ssize_t foo_store(struct  kobject *kobj, struct kobj_attribute *attr,
        const char *buff, size_t count)
{
    foo_size = min(count, (size_t)FOO_SIZE_MAX);
    strncpy(foo_tmp, buff, foo_size);
    return count;
}

static struct kobj_attribute foo_attribute =
    __ATTR(foo, S_IRUGO | S_IWUSR, foo_show, foo_store);

static struct attribute *attrs[] = {
    &foo_attribute.attr,
    NULL,
};

static struct attribute_group attr_group = {
    .attrs = attrs,
};

static struct kobject *kobj;

static int myinit(void)
{
    int ret;

    kobj = kobject_create_and_add("lkmc_sysfs", kernel_kobj);
    if (!kobj)
        return -ENOMEM;
    ret = sysfs_create_group(kobj, &attr_group);
    if (ret)
        kobject_put(kobj);
    return ret;
}

static void myexit(void)
{
    kobject_put(kobj);
}

module_init(myinit);
module_exit(myexit);
MODULE_LICENSE("GPL");

GitHub upstream.

Testováno s linuxovým jádrem 5.0.


Linux
  1. Jak vytvořit skript příkazu Linux

  2. Jak vytvořit balíček RPM pro Linux

  3. Jak vytvořit odkládací soubor v Linuxu

  1. Jak vytvořit swap v Linuxu

  2. Jak vytvořit službu Systemd v Linuxu

  3. Jak vytvořit, zkompilovat, načíst moduly jádra Linuxu LKM

  1. Jak upgradovat jádro na ploše Linuxu

  2. Jak napsat svůj vlastní modul linuxového jádra s jednoduchým příkladem

  3. Jak kódovat modul jádra Linuxu?