> > A: No. > Q: Should I include quotations after my reply? > > http://daringfireball.net/2007/07/on_top > > On Thu, Aug 19, 2010 at 07:07:40PM +0400, ???? wrote: > > Thanks. > > My question (I think) is very easy. But I could not find answer in the Internet, and did not get it on forum. > > My aim is to make UDEV create device node automatically on loading module. > > I tied rule: > > KERNEL=="math", NAME="math" > > meaning my device name (register char dev) is "math" and the node name I want is "math". > > But nothing happens. > > Is there any possibility to make such rule? > > Maybe I do not export some required information to SysFS? > > If you need module code I can send it to you with Makefile. > > Yes, please post your kernel code. You need to hook into the driver > model to properly be able to have udev pick up your device information. > Have you done that? > > thanks, > > greg k-h Yes, here is my code. (Sorry for comments in russian. If you need something to understand - ask me and I'll translate it.) I want kernel to make, for example, /dev/math on loading this module. What should I add to this code or to UDEV rules? #include <linux/module.h> // Необходим для любого модуля ядра #include <linux/kernel.h> // Здесь находится определения KERN_* #include <linux/init.h> // Здесь находятся определения макросов #include <linux/fs.h> // Фаил для работы с фаиловой системой #include <asm/uaccess.h> // Определение функции put_user #include <linux/device.h> static char *DeviceName = "math"; static char *ParamStr = "NULL"; module_param (ParamStr, charp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // Третий аргумент функции - права доступа к соответствующему фаилу в SysFS. // В нашем случае Read User, Write User, Read Group, Read Others. MODULE_PARM_DESC (ParamStr, " String Parameter."); static int DevMajor = -1; #define STR_LEN 256 static char Expression [STR_LEN]; static char FirstOperand [STR_LEN]; static char SecondOperand [STR_LEN]; static char Answer [STR_LEN]; static int Calculate (void) { // Функция выполняющая разбор заданного выражения и вычисление результата. // Принимает Expression, результат вычислений возвращает в Answer. int lStr = 0; int lOperation = -1; int Operand1, Operand2 = 0; // lOperation: -1 - "Error"; 0 - "+"; 1 - "-"; 2 - "*"; 3 - "/"; sprintf (Answer, "No Calculated Result.\n"); Answer[strlen ("No Calculated Result.\n")] = 0; // Ищем знак математической операции: while (lStr < strlen (Expression)) { if (Expression[lStr] == '+') { lOperation = 0; break; } else if (Expression[lStr] == '-') { lOperation = 1; break; } else if (Expression[lStr] == '*') { lOperation = 2; break; } else if (Expression[lStr] == '/') { lOperation = 3; break; }; lStr++; }; if (lOperation == -1) {// Если знак операции не найден - выводим подсказку. sprintf (Answer, "Error in Expression. Please use as \"Operand1 [+, -, *, /] Operand2\".\n"); } else { memcpy (FirstOperand, &Expression[0], lStr); memcpy (SecondOperand, &Expression[lStr + 1], strlen (Expression) - lStr - 1); Operand1 = simple_strtol (FirstOperand, &FirstOperand, 10); Operand2 = simple_strtol (SecondOperand, &SecondOperand, 10); // Сосчитаем что просят: switch (lOperation) { case 0: // + sprintf (Answer, "%s = %d\n", Expression, Operand1 + Operand2); break; case 1: // - sprintf (Answer, "%s = %d\n", Expression, Operand1 - Operand2); break; case 2: // * sprintf (Answer, "%s = %d\n", Expression, Operand1 * Operand2); break; case 3: // / if (Operand2 == 0) { sprintf (Answer, "Sorry, division by Zero is not implemented yet.\n"); } else sprintf (Answer, "%s = %d\n", Expression, Operand1 / Operand2); break; default: sprintf (Answer, "Error in Expression. Please use as \"Operand1 [+, -, *, /] Operand2\".\n"); break; }; }; return lOperation; }; static ssize_t DevWrite (struct file * _File, const char __user * _Buffer, size_t _Count, loff_t *_PPos) { // Функция отвечает за запись в устройство. memcpy (&Expression[0], &_Buffer[0], _Count); Expression[_Count] = 0; // Вычисляем заданное выражение: Calculate (); // Нужно вернуть количество записавшихся (или полученных) байт. // Иначе будет цикл бесконечной записи в устройство. // Видимо кернел продолжает попытки записать в устройство если возвращать 0. // Если устройство вообще не желает принимать данные, нуж но вернуть -EINVAL. // Но это будет обрабатываться именно как ошибка: // echo -n "123" > /dev/coffee // bash: echo: write error: Invalid argument // Мы выбираем самый приемлемый для нас вариант: return _Count; }; static ssize_t DevRead (struct file * _File, char __user * _Buffer, size_t _Count, loff_t *_PPos) { // Функция отвечает за чтение из устройства. size_t BytesWritten = strlen (Answer) - *_PPos; // Проверяем чтобы запрашиваемая позиция в фаиле не выходила за его размер, // чтобы предоставляемый буффер был достаточного размера // и чтобы буффер вообще существовал. Если что-то не так, возвращаем 0 // как признак конца фаила. if ((*_PPos > strlen (Answer)) || (_Count < strlen (Expression)) || (!_Buffer)) return 0; // Т.к. буфер данных находится в пространсвте пользователя, // а мы находимся в пространстве ядра, то для копирования // данных в буфер пользователя мы используем функцию copy_to_user: if (BytesWritten != 0) { if (_Count < BytesWritten) BytesWritten = _Count; copy_to_user (_Buffer, Answer + *_PPos, BytesWritten); *_PPos += BytesWritten; } return BytesWritten; }; static ssize_t DevRelease (struct inode *_INode, struct file *_File) { module_put (THIS_MODULE); return 0; }; static ssize_t DevOpen (struct inode *_INode, struct file *_File) { try_module_get (THIS_MODULE); return 0; }; static const struct file_operations DevOps = { // Здесь мы задаём фнкции, отвечающие за операции с нашим устройством // и указываем адреса процедур отвечающих за эти операции. // В нашем случае - чтение из устройства, запись в него и отработка обращений. .owner = THIS_MODULE, .read = DevRead, .write = DevWrite, .release = DevRelease, .open = DevOpen }; int init (void) { printk (KERN_INFO "Given Parameter String is \"%s\".\n", ParamStr); // Запрашиваем динамический старший номер устройства (драйвера - т.е. нас). // В случае успеха, функция вернет Device Major. DevMajor = register_chrdev (0, DeviceName, &DevOps); if ((DevMajor) < 0) { printk (KERN_ERR "Error! Char Device Could not be Registerd.\n"); // В любом случае, вернем то что получилось. Пусть лубуются. return DevMajor; }; printk ("Use 'mknod /dev/%s c %d 0' to create device file.\n", DeviceName, DevMajor); // Если вернуть ненулевое значение, то это будет воспринято как ошибка, // возникшая в процессе работы init_module; и модуль не будет загружен. sprintf (Expression, "Empty Expression.\n"); sprintf (Answer, "Empty Answer.\n"); return 0; } void cleanup (void) { // Освобождаем номер, выданный нам ранее. // С версии 2.6.23 unregister_chrdev возвращает void т.е. ничего. // Нет возможности проконтролировать работу этой функции. printk (KERN_INFO "unregister_chrdev ();"); unregister_chrdev (DevMajor, DeviceName); } // Начиная с версии 2.3.13, требования к именованию начальной и конечной функций // модуля были сняты. Достигается это с помощью макроопределений module_init() // и module_exit(). Воспользуемся этим, т.к. теперь это считается правильно: module_init (init); module_exit (cleanup); // Описание модуля (может быть прочтено программой modinfo): MODULE_LICENSE("GPL"); // Нужно для функции class_create. MODULE_AUTHOR("Timofey Chernigovskiy"); // Автор. MODULE_DESCRIPTION("Math-Device Module"); // Описание. MODULE_VERSION("0.1"); // Версия. MODULE_SUPPORTED_DEVICE(DeviceName); // Тип устройств поддерживаемых модулем. -- To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html