Документ взят из кэша поисковой машины. Адрес
оригинального документа
: http://www.arcetri.astro.it/irlab/doc/library/linux/khg/HyperNews/get/devices/char.html
Дата изменения: Thu Mar 23 12:37:16 2000 Дата индексирования: Wed Sep 15 01:05:33 2010 Кодировка: Поисковые слова: spring |
Besides functions defined by the file_operations structure, there is at least one other function that you will have to write, the foo_init() function. You will have to change chr_dev_init() in drivers/char/mem.c to call your foo_init() function.
foo_init() should first call register_chrdev() to register itself and avoid device number contention. register_chrdev() takes three arguments:
Generally, the foo_init() routine will then attempt to detect the hardware that it is supposed to be driving. It should make sure that all necessary data structures are filled out for all present hardware, and have some way of ensuring that non-present hardware does not get accessed. [Detail different ways of doing this. In particular, document the request_* and related functions.]
In a polling driver, the foo_read() and foo_write() functions are pretty easy to write. Here is an example of foo_write():
static int foo_write(struct inode * inode, struct file * file, char * buf, int count) { unsigned int minor = MINOR(inode->i_rdev); char ret; while (count > 0) { ret = foo_write_byte(minor); if (ret < 0) { foo_handle_error(WRITE, ret, minor); continue; } buf++ = ret; count-- } return count; }foo_write_byte() and foo_handle_error() are either functions defined elsewhere in foo.c or pseudocode. WRITE would be a constant or #define.
It should be clear from this example how to code the foo_read() function as well.
Interrupt-driven drivers are a little more difficult. Here is an example of a foo_write() that is interrupt-driven:
static int foo_write(struct inode * inode, struct file * file, char * buf, int count) { unsigned int minor = MINOR(inode->i_rdev); unsigned long copy_size; unsigned long total_bytes_written = 0; unsigned long bytes_written; struct foo_struct *foo = &foo_table[minor]; do { copy_size = (count <= FOO_BUFFER_SIZE ? count : FOO_BUFFER_SIZE); memcpy_fromfs(foo->foo_buffer, buf, copy_size); while (copy_size) { /* initiate interrupts */ if (some_error_has_occured) { /* handle error condition */ } current->timeout = jiffies + FOO_INTERRUPT_TIMEOUT; /* set timeout in case an interrupt has been missed */ interruptible_sleep_on(&foo->foo_wait_queue); bytes_written = foo->bytes_xfered; foo->bytes_written = 0; if (current->signal & ~current->blocked) { if (total_bytes_written + bytes_written) return total_bytes_written + bytes_written; else return -EINTR; /* nothing was written, system call was interrupted, try again */ } } total_bytes_written += bytes_written; buf += bytes_written; count -= bytes_written; } while (count > 0); return total_bytes_written; } static void foo_interrupt(int irq) { struct foo_struct *foo = &foo_table[foo_irq[irq]]; /* Here, do whatever actions ought to be taken on an interrupt. Look at a flag in foo_table to know whether you ought to be reading or writing. */ /* Increment foo->bytes_xfered by however many characters were read or written */ if (buffer too full/empty) wake_up_interruptible(&foo->foo_wait_queue); }
Again, a foo_read() function is written analagously. foo_table[] is an array of structures, each of which has several members, some of which are foo_wait_queue and bytes_xfered, which can be used for both reading and writing. foo_irq[] is an array of 16 integers, and is used for looking up which entry in foo_table[] is associated with the irq generated and reported to the foo_interrupt() function.
To tell the interrupt-handling code to call foo_interrupt(), you need to use either request_irq() or irqaction(). This is either done when foo_open() is called, or if you want to keep things simple, when foo_init() is called. request_irq() is the simpler of the two, and works rather like an old-style signal handler. It takes two arguments: the first is the number of the irq you are requesting, and the second is a pointer to your interrupt handler, which must take an integer argument (the irq that was generated) and have a return type of void. request_irq() returns -EINVAL if irq > 15 or if the pointer to the interrupt handler is NULL, -EBUSY if that interrupt has already been taken, or 0 on success.
irqaction() works rather like the user-level sigaction(), and in fact reuses the sigaction structure. The sa_restorer() field of the sigaction structure is not used, but everything else is the same. See the entry for irqaction() in Supporting Functions, for further information about irqaction().
Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson,
johnsonm@redhat.com.