I am developing a kernel module for custom device that, in fact, a 4*8-bit i-o ports attached to ISA bus with addresses 0x0120 - 0x0123. This driver is based on "scull" by Alessandro Rubini and Jonathan Corbet. My OS is Ubuntu 10.04, kernel is 2.6.32-74 generic, I use built-in console-oriented compiler gcc. Now module is loading and unloading successfully, read and write functions are implemented. Reading from device file I implemented this way:
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;
}
and everything works, statusbar displays a string that read from device. But when I tried to write device file - the problem arise. My function that write device file always reports an error.
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;
}
-just like ET3201 == NULL constantly. At this point kernel module is loaded and reading from it is OK. After debugging I found that "Write" function in kernel module is never invoked since it doesn't print corresponding "printk" message in kernel log. In desperation I tried to do it this way:
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;
}
and I get an error message in statusbar: Error: g-file-error-quark: 2, Failed to create file '/dev/ET32010.****8X': Permission denied I think that's because g_file_set_contents writes to a temporary file and then tries to rename it to device file getting "Permission denied". This is maybe a wrong way to write device files, please help me to find the right one...
My questions: Why my function on_BnStop_clicked doesn't work properly? How to make a correct write to char device file from userspace program?
I have checked for device access rights...
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
then changed mode in kernel module init script to 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
and on_BnStart_clicked function returned "Permission denied" as it was before, but the on_BnStop_clicked has invoked the "Write" method of kernel module and then completed successfully with ERR == NULL. After all an error reporting function has crashed with "Segmentation fault" (reporting ERR == NULL) but I already fixed this bug.
Documentation of g_file_set_contents says:
This write is atomic in the sense that it is first written to a temporary file which is then renamed to the final name.
So, file /dev/ET32010.****8X
is actually temporal. And, because creating files under /dev
is usually allowed only for root, creation of temporary file fails with "Permission denied".
Even if writting into /dev
would be allowed, g_file_set_contents
will replace device file with regular one, which is not what you want.
You need to use lower-level way of writting into device file. E.g., using fopen
+ fwrite
.