In this user mode drivers, access of registers through mmap and interrupts through proc interface is discussed. This is a from Peter Chubb's User Mode Driver mechanism for linux kernel 2.6. A casual one to handle the scenarios from user space is discussed below. Whereas, it has limitations like shared interrupts cannot be used with this mechanism and a lot more. Infact at certain cases it would result in a deadlock and further discussions on this mechanism could be viewed in this discussion thread.

Further References

Handling Interrupts

The interrupts generated in kernel space is transmitted to user space through the proc interface. Here the main key is that the user mode process is allowed to block on a particular file descriptor and when an interrupt arrives, the block is revoked and the process is put to schedule. The process in detail is explained below.

/proc/irq - directory structure

/proc/irq directory structure The /proc filesystem consists of a directory entry named irq which has the list of all interrupts available in the system, as shown in the figure. The mechanism adds a new file called irq inside each interrupt folders and the user process is expected to block/poll this file to be notified about the interrupt.

With the addition of this file, irq, suitably privileged processes can open this file.

  • Reading the file returns the number of interrupts (if occurred) since the last read.
  • In blocking mode, reading it blocks until an interrupt occurs and then returns the value as said above.

The user mode process can implement a poll(2) or select(2) on this file and as one would expect, this would allow interrupts to be one of the events that revokes the block. Interrupts are usually masked; while a thread is in poll(2) or read(2) on the file, they are unmasked.

Interrupt handling mechanism

A brief description of the mechanism of interrupt handling from user space is described here.

  1. The interrupts are registered through the function init_irq_proc where the irq’s inturn gets registered and the file /proc/irq/<nnn>/irq is created and the proc file operations of the file are linked with the irq_proc operations which would be expained shortly.
  2. The drivers that wish to handle the interrupts from the kernel side can independently handle them from the kernel side. The interrupt handlers are _NOT_ linked during the register_irq event. So, only user mode drivers have the need to use the irq file under proc to access interrupts.
  3. Proc file operations: open, read, poll, release are mapped to the irq_proc functions defined in irq.c for this purpose.
  4. open on the irq file makes a request_irq to the irq module of the kernel. A handler function, proc_irq_handler is defined in the irq.c and is provided as a handler function for requested interrupt.
  5. select/poll puts the thread in poll_wait. Irq is enabled if its disabled during the call of the function and the polling returns the result of the irq being active.
  6. read operation on the irq performs two things. Everytime an interrupt is raised, the count for that interrupt increases. The irq is enabled if its not enabled before.
    • If already few interrupts are queued up and are not caught, they are returned and the count is decreased.
    • If no pending interrupt exist, the irq_read puts the task in wait state and the process blocks if the file is opened in the blocking mode or no data is returned if its in non-blocking mode.
  7. The read operation is interrupted by the scheduler by waking up the sleeping task through schedule call. After which the read returns the count of the interrupts occured thus breaking the _block_ on the user mode.
  8. The irq_proc_handler function takes care of waking up the sleeping task and disables the irq and decreases the count.

IRQ Mechanism

mmapping device

mmap Mechanism For the completion of user mode driver, what we require is to make an assign/read operation upon the registers. The registers can be accessed from the user space by mmaping the portion of the memory where the device registers are. The memory area is accessed as a pointer variable on the user mode side.

mmap syntax

The mmap and its allied functions are declared in sys/mman.h. The syntax of mmap/munmap is given below.

The detailed discussion of the parameters could be found in mmap(2). The system memory /dev/mem should be opened prior to this mmap call and the file descriptor returned should be passed as fd to mmap. After mmaping the device, the pointer to the device area in the memory is returned. Now the pointer can be used as an array and values could be read/written depending on the mode in which the /dev/mem is opened.

 void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
 int munmap(void *start, size_t length);

[CAUTION]

Since the device is directly mapped, the registers should be written with caution and since the code is going to be on the user space, it doesnot mean that its safe from crashing in case of a wrong write to the device register.

Examples

mmapping device

The following function defines the mapping/unmapping of device given its address and size.

void map_addr(unsigned int addr, unsigned int size, void **map)
{
	int fd;
	
	fd = open("/dev/mem", O_RDWR);
	if (fd == -1) { // handle error 
        }
	*map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr);
	if (*map == MAP_FAILED) { // handle error 
        }
}
 
void unmap_addr(void *map, unsigned int size)
{
	int ret;
	
	ret = munmap(map, size);
	if (ret == -1) { // handle error
        }
}

mmapping and IRQ handling

The following piece of code shows how to use the above method to access the device registers as well as handle the IRQ’s of the device.

Main body of the code

        ....
	// select FD's
	fd_set poll_fds, read_fds;
	int proc_irq, poll;
	struct timeval tv;
	char buf[BUFSIZE];
 
	proc_irq = open(irqfile, O_RDONLY);
	if (proc_irq == -1) { // handle error
        }
 
	// Setup for select
	FD_ZERO(&poll_fds);
	FD_SET(proc_irq, &poll_fds);
	tv.tv_sec=0; tv.tv_usec=10000;
	led_flag = 0;
	...
	// Set device regs (Init device)
	device_init();
 
	// Ready to poll/block
	while (1)
	{
		read_fds=poll_fds;
		poll=select(FD_SETSIZE, &read_fds, NULL, NULL, &tv);
 
		if (poll==-1) // SYSCALL: handle error
		else if (poll) {
 
			// Data available? Meaning, interrupt available :-)
			if (FD_ISSET(proc_irq, &read_fds)) { 
				int len;
				len = read(proc_irq, buf, sizeof (int));
				if (len == -1) // handle error
 
				... // Some op's
 
				// Read data
			}
		}
		// Reset polling time.
		tv.tv_sec=0; tv.tv_usec=10000;
	}
	
	// Shutdown device op's
	device_shutdown();
}

Device init/shutdown

unsigned char *devmem_map; 
void device_init ()
{
        ...
	// Map device area (All values for illustration purpose only)
	map_addr(DEVICE_START_ADDRESS, DEVICE_SIZE, &devmem_map);
	// Set registers here
	...
}
 
void device_shutdown ()
{
	// Set registers here
	...
	unmap_addr(devmem_map, DEVICE_SIZE);
}
 
user_mode_driver.txt · Last modified: 2007/04/07 08:34 by ramasamy
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki