【问题标题】:Data cannot be written to device file from userspace program数据无法从用户空间程序写入设备文件
【发布时间】:2016-02-25 14:26:14
【问题描述】:

我正在为自定义设备开发一个内核模块,实际上,这是一个连接到 ISA 总线的 4*8 位 i-o 端口,地址为 0x0120 - 0x0123。该驱动程序基于 Alessandro Rubini 和 Jonathan Corbet 的“scull”。我的操作系统是 Ubuntu 10.04,内核是 2.6.32-74 通用的,我使用内置的面向控制台的编译器 gcc。 现在模块加载和卸载成功,实现了读写功能。从设备文件中读取我是这样实现的:

gboolean DataRead (GtkWidget *widget, gpointer data)
{
GtkWidget *statusbar = GTK_WIDGET(data);
g_assert(statusbar != NULL);
gchar *StatusText = "Reading data from ADC F-4226";
gtk_statusbar_remove (GTK_STATUSBAR(statusbar), StatusBarContext, MessageId);
StatusBarContext = gtk_statusbar_get_context_id (GTK_STATUSBAR(statusbar), "ET3201-read");
MessageId = gtk_statusbar_push (GTK_STATUSBAR(statusbar), StatusBarContext, StatusText);
ERR = NULL; 
gchar *contents;
gsize *length = NULL;
gboolean ReadOK = FALSE;

ReadOK = g_file_get_contents (DevFile, &contents, length, &ERR);
if (ReadOK) StatusText = contents;
    else 
    {
        g_warning ("Can't open ET3201: %s", ERR->message);
        StatusText = g_strdup_printf("Error!!! %s: %d, %s", g_quark_to_string (ERR->domain), ERR->code, ERR->message);
        g_error_free(ERR);
    }
    gtk_statusbar_remove (GTK_STATUSBAR(statusbar), StatusBarContext, MessageId);
    StatusBarContext = gtk_statusbar_get_context_id (GTK_STATUSBAR(statusbar), "ET3201-read");
    MessageId = gtk_statusbar_push (GTK_STATUSBAR(statusbar), StatusBarContext, StatusText);
    g_free(contents);
}
return ReadOK;

}

  • 一切正常,状态栏显示从设备读取的字符串。 但是当我尝试写入设备文件时 - 问题出现了。我写设备文件的函数总是报错。

    GError * on_BnStop_clicked (GtkButton *button, gpointer data)
    {
    char Data = 255;
    FILE * ET3201 = NULL;
    char buf;
    ERR = NULL;
    GtkWidget *label = GTK_WIDGET(data);
    g_assert(label != NULL);
    if ( Started ) Started = FALSE; 
    if ( ! Started ) Data = 0;              /* FOR TESTING ONLY!!!*/
    gchar *LabelText = "STOP";
    gtk_label_set_text(GTK_LABEL(label), LabelText);
    errno = 0;
    ET3201=fopen(DevFile,"w");/* Opening the device ET3201 */
    ErrorCode = errno;
    if (ET3201 == NULL)
    {
        ERR = g_error_new ( G_FILE_ERROR, G_FILE_ERROR_NXIO, "Can't
    open port ET3201!" );
        goto fail;
    }   
    setvbuf(ET3201,&buf,_IONBF,1); /* We remove the buffer from the
    file i/o */
    fwrite(&Data,1,1,ET3201);       /* Writing to device*/
    sleep(1);
    fclose(ET3201);    
    /* g_free(&buf);  - leading to "Program aborted" */
    fail: 
    return ERR; 
    }
    

-就像 ET3201 == NULL 一样。此时内核模块已加载并从中读取就可以了。调试后我发现内核模块中的“Write”函数从未被调用,因为它不会在内核日志中打印相应的“printk”消息。无奈之下,我试图这样做:

GError * on_BnStart_clicked (GtkButton *button, gpointer data)
{
    gchar *contents;
    gssize length = 0;
    ERR = NULL;
    GtkWidget *label = GTK_WIDGET(data);
    g_assert(label != NULL);
    if (! Started ) Started = TRUE; 
    gchar *LabelText = "RUN";
    gtk_label_set_text(GTK_LABEL(label), LabelText);

    contents = g_strdup_printf("/255");   /*FOR TESTING PURPOSES!!!*/

    length = sizeof(contents);

    g_file_set_contents (DevFile, contents, length, &ERR);

    return ERR; 
}

我在状态栏中收到一条错误消息: 错误:g-file-error-quark:2,无法创建文件'/dev/ET32010.****8X':权限被拒绝 我认为这是因为 g_file_set_contents 写入一个临时文件,然后尝试将其重命名为设备文件,得到“权限被拒绝”。这可能是编写设备文件的错误方式,请帮助我找到正确的方式...

我的问题:为什么我的函数 on_BnStop_clicked 不能正常工作?如何从用户空间程序正确写入 char 设备文件?

我已检查设备访问权限...

master@master-desktop:~$ ls -l /dev/ET3201*
lrwxrwxrwx 1 root root       7 2015-11-23 14:34 /dev/ET3201 -> ET32010
crw-rw-r-- 1 root staff 250, 0 2015-11-23 14:34 /dev/ET32010
crw-rw-r-- 1 root staff 250, 1 2015-11-23 14:34 /dev/ET32011
crw-rw-r-- 1 root staff 250, 2 2015-11-23 14:34 /dev/ET32012
crw-rw-r-- 1 root staff 250, 3 2015-11-23 14:34 /dev/ET32013

然后将内核模块初始化脚本中的模式更改为 666

lrwxrwxrwx 1 root root       7 2015-11-23 22:27 /dev/ET3201 -> ET32010
crw-rw-rw- 1 root staff 250, 0 2015-11-23 22:27 /dev/ET32010
crw-rw-rw- 1 root staff 250, 1 2015-11-23 22:27 /dev/ET32011
crw-rw-rw- 1 root staff 250, 2 2015-11-23 22:27 /dev/ET32012
crw-rw-rw- 1 root staff 250, 3 2015-11-23 22:27 /dev/ET32013

on_BnStart_clicked 函数返回"Permission denied" 和以前一样,但是on_BnStop_clicked 调用了内核模块的"Write" 方法,然后以ERR == NULL 成功完成。毕竟,错误报告功能因“分段错误”而崩溃(报告 ERR == NULL),但我已经修复了这个错误。

【问题讨论】:

  • 您是否检查过您的用户是否可以写入文件? fopen 失败的时候检查errno 值了吗?

标签: char gtk linux-device-driver fwrite userspace


【解决方案1】:

g_file_set_contents 的文档说:

这种写入是原子性的,因为它首先写入一个临时文件,然后将其重命名为最终名称。

所以,文件/dev/ET32010.****8X 实际上是临时。而且,因为在/dev创建文件通常只允许root 用户,临时文件的创建失败并显示“权限被拒绝”。

即使允许写入/devg_file_set_contents 也会替换 device 文件为regular 文件,这不是你想要的。

您需要使用较低级别的方式写入设备文件。例如,使用fopen + fwrite

【讨论】:

  • 感谢您的帮助!我测试了我的函数 on_BnStop_clicked - 现在它实际上使用 fopen() 和 fwrite() 将数据发送到设备。我的帖子现在包含此功能的工作代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多