2013-04-26 17 views
15

Tôi đang học cách sử dụng sysfs trong các mô-đun Linux của mình, nhưng tôi đang gặp khó khăn khi tìm tài liệu hiện tại về các chủ đề này. Cuốn sách Linux Device Drivers 3rd Edition tôi đã sử dụng dường như khá đáng tiếc trong khu vực này (ví dụ: cấu trúc class_device dường như đã biến mất hoàn toàn trong các phiên bản Linux hiện tại).Cách tạo thuộc tính lớp sysfs đơn giản trong Linux kernel v3.2

Tôi chỉ đơn giản là cố gắng để có thuộc tính xuất hiện, dưới lớp tương ứng sysfs cho mô-đun của tôi, điều này sẽ cho phép tôi đọc giá trị của biến mô-đun từ không gian hạt nhân.

Trong mã của tôi, tôi có một lớp được tạo ra cho phép udev để tạo ra một nút thiết bị tại /dev/foo cho mô-đun của tôi:

dev_t foo_dev; 
alloc_chrdev_region(&foo_dev, 0, 1, "bar"); 

struct class *bar = class_create(THIS_MODULE, "bar"); 

device_create(bar, NULL, foo_dev, NULL, "foo"); 

struct cdev foo_dev_file; 
cdev_init(&foo_dev_file, &fops); /* fops defined earlier */ 
cdev_add(&foo_dev_file, foo_dev, 1); 

Khi tôi chèn module tôi nhận được một sysfs thư mục lớp được tạo và được điền với một số thuộc tính mặc định tại /sys/class/bar/foo/. Làm thế nào tôi có thể tạo các thuộc tính hiển thị trong thư mục mới này?

Tôi có khái niệm khá tốt Tôi tin - tạo cấu trúc thuộc tính, xác định các hàm sysfs_ops, v.v. - vấn đề của tôi là tôi không biết cấu trúc hạt nhân cụ thể nào sử dụng (class_attribute?), Cũng như cách tạo các thuộc tính này xuất hiện bên dưới thư mục sysfs đúng.

Có ai chỉ cho tôi một bài hướng dẫn hoặc bài viết nêu chi tiết quy trình cho hạt nhân Linux hiện tại không?

Trả lời

13

Mặc dù kiến ​​thức của tôi vẫn còn khá thấp về chủ đề này, tôi sẽ đăng câu trả lời chỉ vì tuổi của câu hỏi này. Nếu ai đó có câu trả lời hay hơn, hãy vui lòng đăng bài! :)

Trước hết, tôi sẽ giả định rằng bạn đã đọc toàn bộ chương đó (cụ thể là về kobjects & ksets). Vì vậy, chỉ cần về mỗi cấu trúc trong mô hình trình điều khiển thiết bị có những dễ thương bao gồm trong đó. Nếu bạn muốn thao tác các kobject cho lớp tự của nó (không chắc chắn nếu đó là khôn ngoan hay không), đó là dev_kobj thành viên của struct class của bạn.

Tuy nhiên, bạn muốn thao tác các thuộc tính của lớp đó. Tôi tin bạn làm điều này bằng cách định nghĩa một (thường là tĩnh), NULL-chấm dứt mảng trong số họ như sau và sau đó gán địa chỉ của nó để class_attrs thành viên của s struct class (lấy từ 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, 
}; 

Khi tôi không biết làm thế nào để sử dụng một cái gì đó, tôi thường git grep kho lưu trữ cho người khác đã sử dụng nó và cố gắng học hỏi từ nó theo cách đó. Có vẻ như đây là lý do tại sao họ có xu hướng nói "hacker" hạt nhân chứ không phải "nhà phát triển".

+0

Tôi nhận thấy trong hàm 'uwb_subsys_init',' uwb_rc_class' được chuyển tới hàm 'class_register'. Điều gây nhầm lẫn cho tôi là liệu tôi có thể gọi hàm này sau 'class_create' hay không. –

+0

Có vẻ như từ [mã nguồn Linux] (http://lxr.free-electrons.com/source/drivers/base/class.c?v=3.2#L249) rằng 'class_create' chỉ thực hiện cuộc gọi đến' class_register ', do đó, câu hỏi thực sự sôi xuống cho dù' class_register' có thể được gọi nhiều lần. –

+0

Tôi vừa mới nhận ra: Tôi có thể sử dụng 'class_create_file' để làm cho thuộc tính lớp của tôi xuất hiện thay vì một cuộc gọi thứ hai tới' class_register'. Cảm ơn bạn đã giúp đỡ @DanielSantos, và ví dụ đẹp về "hack" hạt nhân Linux cho một giải pháp! :) –

2

Có một hướng dẫn tốt trong link bên dưới

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) 
1

Minimal dụ Runnable

Cách sử dụng:

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 

Source:

#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"); 
Các vấn đề liên quan