linux struct resource

I/O ports and I/O memory are two conceptual ways of supporting communication between device drivers and devices.

So that the various drivers do not interfere with each other, it is essential to reserve ports and I/O memory ranges prior to use by a driver.

This ensures that several device drivers do not attempt to access the same resources.

Linux provides a generalized framework to help build data structures in memory.

These structures describe the resources available in the system and enable the kernel code to manage and allocate the resources.

Significantly, the name of the key structure is resource; it is defined as follows:

<ioport.h>
struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
};

name stores a string so that the resource can be given a meaningful name.

It has no relevance for the kernel but is useful when a resource list (in the proc filesystem) is output in readable form.

The resource itself is characterized by the three parameters below. 

start and end specify a general area marked by unsigned long numbers; even though theoretically the contents of the two numbers can be interpreted freely, they usually represent an area in an address space. 

flags enables the resource and its current state to be described more precisely.

Of particular interest are the three pointers to other resource structures.

These enable a tree-like hierarchy to be established in which the pointers are often arranged as you will see below.

Resource management in a tree structure.

The rules for linking the parent, child, and sibling elements are simple.

  • Each child has just one parent.

  • A parent can have any number of children.

  • All children with the same parent are linked on the list of siblings.

The following must be noted when the data structure is represented in memory.

  • Even though there may be a pointer from each child to the parent, there is always only a single pointer from the parent to the first child. All other children can be reconstructed from the sibling list.

  • The pointer to the parent may also be NULL, in which case there is no higher-level element

How can this hierarchical structure be used for device drivers? Let us look at the example of a system bus to which a network card is attached.

The card supports two outputs, each of which is assigned a specific memory area for data input and output.

The bus itself also has a I/O memory area, sections of which are used by the network card.

This scheme fits perfectly into the tree hierarchy.

The bus memory area that theoretically occupies the (fictitious) range between 0 and 1,000 acts as the root element (the uppermost parent element).

The network card lays claim to the memory area between 100 and 199 and is a child of the root element (the bus itself).

The child elements of the network card represent the individual network outputs to which the I/O ranges from 100 to 149 and from 150 to 199 are assigned.

The originally large resource area is repeatedly subdivided into smaller sections, each of which represents a layer of an abstraction model.

Consequently, child elements can be used to partition the area into ever smaller and ever more specific sections.

REQUESTING AND RELEASING RESOURCES

To ensure that resources — regardless of kind — are deployed reliably, the kernel must feature a mechanism to allocate and subsequently release them.

Once a resource has been allocated, it may not be used by any other driver.

Requesting and releasing a resource equates to nothing more than adding and removing entries from a resource tree.

Requesting Resources

The kernel provides the __request_resource function to request a resource area. The function expects a series of parameters including a pointer to the parent element, the start and end address of the resource area, and a string to hold the name of the area.

kernel/resource.c

static struct resource * request_resource(struct resource *root,
                                          struct resource *new);

The purpose of the function is to allocate a request instance and fill it with the data passed.

Checks are also performed to detect obvious errors (start address bigger than the end address, for example) that would render the request useless and would abort the action.

request_resource is responsible only for the requisite locking. The heavy work is delegated to __request_resource. It scans the existing resources consecutively to add the new resource at the correct position or to reveal conflicts with areas already allocated. It does this by running through the list of siblings.

The new resource instance is inserted if the required resource area is free, thus reserving the resource. Reservation fails if the area is not free.

The children of a specific parent are scanned on one sibling level only. The kernel does not scan the list of children downward.

If a resource cannot be reserved, the driver automatically knows that it is already in use and is therefore not available at the moment.

Releasing Resources

The release_resource function is invoked to release a resource that is in use.

kernel/resource.c

void release_resource(struct resource *old)

 

I/O Memory

One of the most important aspects of the resource concept deals with how I/O memory is distributed because this is the main way on all platforms (with the exception of IA-32, where great importance is attached to I/O ports) of communicating with peripherals.

I/O memory includes not only the memory regions used directly to communicate with expansion devices but also the regular RAM and ROM memory available to the system and included in the resource list (which can be displayed using the iomem file in the proc filesystem).

All allocated I/O memory addresses are managed in a resource tree that uses the global kernel variable iomem_resource as its root.

Each text indentation represents a child level. All entries with the same indentation level are siblings and are linked as such. 

However, reservation of a memory region is not the only action needed when using I/O memory.

Depending on bus system and processor type, it may be necessary to map the address space of an expansion device into kernel address space before it can be accessed (this is known as software I/O mapping).

This is achieved by setting up the system page tables appropriately using the ioremap kernel function, which is available at various points in the kernel sources and whose definition is architecture-specific.

The likewise architecture-specific iounmap function is provided to unmap mappings.

Implementation requires, in part, long and complex manipulation of the process tables.

I won't therefore discuss it in detail, particularly as it varies greatly from system to system and is not important for an understanding of device drivers.

What's more important is that — in general terms — a physical address is mapped into the virtual address space of the processor so that it can be used by the kernel.

As applied to device drivers, this means that the address space of an expansion bus is mapped into the address space of the CPU,

where it can then be manipulated using normal memory access functions.

 

NOTE

Even after I/O areas have been mapped, it is necessary on some platforms to use special methods rather than direct pointer de-referencing to access the individual memory areas. the table shows the functions declared to do this on all platforms (generally in <asm-arch/io.h>). They should always be used by portable drivers even if they boil down to simple pointer de-referencing on some architectures because no other steps are needed to communicate with the I/O areas (as on IA-32 systems, for example).

Function

Meaning

readb(addr)

 

readw(addr)

 

readl(addr)

Reads a byte, word, or long from the specified I/O address addr.

writeb(val, addr)

 

writew(val, addr)

 

writel(val, addr)

Writes a byte, word, or long value specified by val to the I/O address addr.

memcpy_fromio(dest, src, num)

Moves num bytes from the I/O addresss src to dest in normal address space.

memcpy_toio(dst, src, nun)

Copies num bytes from dst in normal address space to src in the I/O area.

memset_io(addr, value, count)

Fills count bytes with value starting at position addr.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值