Basic knowledge of Linux device drivers __linux

Source: Internet
Author: User
A device driver is used as a translation between hardware and the application that uses hardware (user code) or the kernel, which hides the details of the hardware's work behind the scenes, thereby simplifying programming. Programmers can use a set of standardized invocation methods (system calls) to write advanced application code without having to care about the specific hardware it will control or the processor on which it is running. With a well-defined internal application programming interface (Kernel API), application code can interface with device drivers in a standard way that is not related to the upper structure of the software or the underlying hardware.

Details of hardware operations handled by the operating system (OS) for a specific processor platform. With kernel (OS) Internal hardware abstraction layer (HAL) and processor-specific peripherals drivers (such as I2C®SPI bus drivers), the usual device drivers can even be stand-alone processor platforms. This approach allows a device driver (such as a touchscreen digitizer AD7879 driver) to be used without modification on any processor platform running Linux, running any graphical user interface (GUI) packages and appropriate applications on top of the Linux kernel. If the hardware designer decides to switch to the touchscreen controller AD7877, he or she will not need to provide information to the software team. Two devices are available drivers, although the device is different, the connection may be different (AD7877 only provide spi,ad7879, there are SPI or I2C), and the register map is not the same, but relative to the touch screen user code kernel API is the same. In this way, control over the hardware is returned to the hardware architect.

Different types of device drivers in the Linux kernel provide different levels of abstraction, traditionally grouped into the following three categories.

Character devices: processing byte streams. The serial port or input device driver (keyboard, mouse, touch screen, game joystick, etc.) typically implements the character device type.

Block data device: a single operation handles 512 bytes or more of two power block data. Storage device drivers typically implement such block devices.

Network interface: Any network transaction is done through the interface, which refers to the device that can exchange data with other hosts.

In the Linux kernel, each particular category may have multiple separate device core layers to help developers implement standard-purpose drivers such as video, audio, networking, input devices, or backlight processing. Typically, each subsystem has its own directory in the Linux kernel source tree. This "device driver core approach" eliminates common code for all device drivers in a particular category and builds a standard interface for the upper layer. Each type of device or bus device core driver typically exports a set of functions to its subclasses. The driver registers with this core driver and uses the API exported by the core driver, rather than registering its own character/block/network driver. This typically includes ways to support and work with multiple instances and to allocate data between tiers. Most systems have no intention of understanding how the device is connected, but need to know what device is available. The Linux device model also includes a mechanism for assigning devices to specific categories, such as input (input), RTC (real time clock), net (network), or GPIO (universal input/output). These class names describe the device at a higher level of functionality so that it can be found in user space.

Specific hardware may have multiple device driver subsystems associated with them. Multifunction chips, such as backlit driver ADP5520 with I/O extender, utilize Linux backlight, LEDs, GPIO, and input subsystems to achieve their keyboard functions.

As mentioned earlier, user applications cannot communicate directly with hardware, because that would require administrator privileges on the processor, such as special instructions or processing interrupts. Applications that use specific hardware devices typically work on kernel drivers exposed through nodes in the/dev directory.

Device nodes are called pseudo files because they look like files, and applications can turn them on or off (open () or close (), but when they are read or written, the data comes from or is passed to the driver associated with the device node. This abstraction level is handled by the virtual file system (VFS) in the Linux kernel. In addition to read (), write (), or poll (), user applications can interact with the device using IOCTL () (Input/output control).

In addition to device nodes, applications can take advantage of file entries in the/sys directory; This is a SYSFS virtual file system that can export information about devices and drivers, including parent-child relationships or associations to specific classes, buses, and from the kernel device model to user space. /sys is also frequently used for device configuration, especially when the driver is registered with a device driver core, which only exports its standard set of features to the user.

The device driver can register/sys "hooks" or "entries", and when reading or writing hooks or entries, a callback function specifically registered by the device driver is executed. These callback functions (run in admin mode) can accept parameters, initiate bus transfers, invoke some sort of processing, modify specific device variables, and return an integer value or string to the user. This creates conditions for implementing other functions, for example, user space can use a touch-screen digitizer AD7877 temperature sensor or auxiliary ADC.

Device drivers can be either statically built into the kernel or later dynamically installed as loadable modules. The Linux kernel module (LKM) is a dynamic component that can be inserted and removed at run time. This is especially useful for driver developers because faster compilation speeds can save time and test modules do not have to reboot the system. Having a hardware driver reside in a module that can be loaded into the kernel at any time can save RAM when a particular piece of hardware is not in use.

When you load a module, you can also give it configuration parameters. For modules built in the kernel, parameters are routed to the module when the kernel starts. For example:

Root:~> insmod./sample_module.ko argument=1

Root:~> Lsmod

Module Size Used by

Sample_module 1396 0-live 0x00653000

Root:~> Rmmod Sample_module

The driver can also be instantiated multiple times, each time using a different setting, the target device can have different I2C from the ID, connect to the different SPI from the selection, or map to a different physical memory address. All instances share the same code in order to save the memory, but have their own data segments.

Linux is a first-account multitasking, multi-user operating system, so almost all device drivers and kernel subsystems allow multiple processes (which may be owned by different users) to take advantage of the device at the same time. Common examples are network (network), audio (audio), or input (input) interfaces. The key press or release event of the QWERTY keyboard controller ADP5588 is time-stamped, queued, and sent to all processes that have open input vent device (input event devices). These event codes are the same on all schemas and have nothing to do with hardware. Reading a USB keyboard is no different from reading ADP5588 from user space. Event types are distinguished by code. The keyboard sends a key event (Ev_key), a key identifier, and a state value that represents the press or release of an action. The touch screen sends an absolute coordinate event (Ev_abs) and a triple group of X, Y, and touch pressure, and the mouse sends a relative motion event (Ev_rel). The accelerometer ADXL346 can send key events about Tanzhen or double vibrations while sending an absolute coordinate event on the acceleration.

In some applications, it is also significant that the accelerometer ADXL346 generate relative events or send specific key codes (specific application settings). In general, there are two ways to customize a driver: at runtime or at compile time.

Device features that may be customized at run time use module parameters and or/sys entries.

Achieve a specific goal

Using open source Linux drivers-by customizing for specific goals, it is a common practice for Linux to exclude specific boards and specific application configurations from the main driver files for compile-time configuration, typically into board Supportfile (Board support files).

For devices on a custom board (which is typical of embedded and SOC-based system hardware), Linux uses Platform_data to describe the device and how it connects to the particular board structure of the Soc. This can include available ports, different chip versions, preferred mode, default initialization, other roles for pins, and so on. This will reduce the Board Support Pack (BSP) and minimize driver board and apply specific #ifdef. The driver's author determines which adjustable variables enter the Platform_data and which should have access at run time.

The characteristic of the digital speedometer has a very close relationship with the application, and different plates and models may have different characteristics. The following example shows a set of configuration options. These variables are described in detail in the header file Adxl34x.h (include/linux/input/adxl34x.h).

Analog dialogue 44-03, March (2010)

#include

static const struct ADXL34X_PLATFORM_DATA

adxl34x_info={

. x_axis_offset=0,

. y_axis_offset=0,

. z_axis_offset=0,

. tap_threshold=0x31,

. tap_duration=0x10,

. tap_latency=0x60,

. tap_window=0xf0,

. tap_axis_control=adxl_tap_x_en | Adxl_tap_

Y_en | Adxl_tap_z_en,

. Act_axis_control=0xff,

. activity_threshold=5,

. inactivity_threshold=3,

. inactivity_time=4,

. free_fall_threshold=0x7,

. free_fall_time=0x20,

. data_rate=0x8,

. Data_range=adxl_full_res,

. Ev_type=ev_abs,

. ev_code_x=abs_x,/*ev_rel*/

. ev_code_y=abs_y,/*ev_rel*/

. ev_code_z=abs_z,/*ev_rel*/

. Ev_code_tap={btn_touch,btn_touch,btn_touch},/*ev_key X,y,z * *

. ev_code_ff=key_f,/* Ev_key * *

. ev_code_act_inactivity=key_a,/*ev_key*/

. power_mode=adxl_auto_sleep| Adxl_link,

. Fifo_mode=adxl_fifo_stream,

};

To associate a device with a driver, the platform and bus model does not require a device driver to contain the hard-coded physical address or bus ID of the device that it controls. The platform and bus models also prevent resource conflicts, greatly improve portability, and cleanly interface with the power management features of the kernel.

Using platform and bus models, device drivers know how to control devices once they have the physical location of the device and the interrupt line. This information is passed to the driver as a data structure during probing.

Unlike PCI or USB devices, I2C or SPI devices are not enumerated at the hardware level. Instead, the software must know which devices are connected to each I2C/SPI bus segment, and what addresses these devices use. Therefore, the kernel code must explicitly instantiate the I2C/SPI device. This can be done in a number of different ways, depending on the context and requirements. However, the most common method is to declare the I2C/SPI device through the bus number.

This approach is appropriate when the I2C/SPI bus is a system bus; many embedded systems are in this situation, where each I2C/SPI bus has a previously known number. Therefore, I2C/SPI devices connected to the bus can be declared in advance. This can be done using a struct-i2c_board_info/spi_board_info array that registers I2c_register_board_info ()/spi_register_board_info () by calling the following content

static struct I2c_board_info __initdata bfin_ i2c_board_info[] = {#if defined (CONFIG_TOUCHSCREEN_AD7879_I2C) | | Defined (config_touchscreen_ad7879_i2c_module)

{

I2c_board_info ("ad7879", 0x2f),

. Irq=irq_pg5,

. Platform_data= (void*) &bfin_ad7879_ts_info,

},

#endif

#ifdefined (config_keyboard_adp5588) | | Defined (config_keyboard_adp5588_module)

{

I2c_board_info ("Adp5588-keys", 0x34),

. Irq=irq_pg0,

. Platform_data= (void*) &adp5588_kpad_data,

},

#endif

#ifdefined (config_pmic_adp5520) | | Defined (config_pmic_adp5520_module)

{

I2c_board_info ("pmic-adp5520", 0x32),

. Irq=irq_pg0,

. Platform_data= (void*) &adp5520_pdev_data,

},

#endif

#ifefined (CONFIG_INPUT_ADXL34X_I2C) | | Defined (config_input_adxl34x_i2c_module)

{

I2c_board_info ("adxl34x", 0x53),

. IRQ = Irq_pg0,

. Platform_data = (void *) &adxl34x_info,

},

#endif

};

static void __init blackfin_init (void)

{

(...)

I2c_register_board_info (0,bfin_i2c_board_info, Array_size (Bfin_i2c_board_info));

Spi_register_board_info (Bfin_spi_board_info, Array_size (Bfin_spi_board_info));

(...)

}

Therefore, to enable such a driver, you only need to edit the Board support file and add the appropriate entries to I2c_board_info (spi_board_info).

You should also note that you need to select a driver during kernel configuration. The driver is categorized according to the subsystem to which it belongs. You can find the adxl34x driver in the following locations:

Device Drivers--->

Input Device Support--->

[*] Miscellaneous Devices--->

Analog Devices ad714x capacitance touch Sensor

Support I2C Bus Connection

Support SPI Bus Connection

<*>analog Devices adxl34x Three-axis

Digital Accelerometer

<*>support I2C Bus Connection

<*>support SPI Bus Connection

Once the user starts the kernel build process, the selected driver is automatically compiled. The above code declares that there are four devices on I2C bus 0, including their respective addresses, IRQ, and the custom platform_data required by their drivers. When the associated I2C bus is registered, the I2c-core kernel subsystem automatically instantiates these I2C devices.

static struct I2c_driver Adxl34x_driver = {

. driver={

. Name= "Adxl34x",

. Owner=this_module,

},

. Probe=adxl34x_i2c_probe,

. Remove=__devexit_p (Adxl34x_i2c_remove),

. Suspend=adxl34x_suspend,

. Resume=adxl34x_resume,

. id_table=adxl34x_id,

};

static int __init adxl34x_i2c_init (void)

{

Return I2c_add_driver (&adxl34x_driver);

}

Module_init (Adxl34x_i2c_init);

A device driver named adxl34x can register itself by invoking I2c_add_driver () at a point in time at which the kernel is booting, or at any subsequent time, by using struct i2c_driver. Members of the struct I2c_driver connect the driver with its bus master kernel, using pointers to adxl34x driver functions. (The Macro Module_init () defines which function (Adxl34x_ i2c_init ()) is called when the module is inserted. )

If the archived driver name matches the name provided by the macro I2c_board_info, the I2c-core bus model implementation will invoke the driver's probe () function to pass the associated Platform_data and IRQ from the Board support file to the driver. This occurs only if there is no recourse conflict, such as when the previous instantiation device uses the same I2C from the address.

The Adxl34x_i2c_probe () function then begins to perform the function that its name represents. It checks to see if the ADXL345 or ADXL346 exists and is working correctly by reading the manufacturer and device IDs. If the check succeeds, the driver's probe function assigns a specific device data structure, requests an interrupt, and initializes the accelerometer.

The Input_allocate_device () is then used to allocate the new input device structure and set the input bit field. In this way, the device driver tells the other parts of the input system what kind of device this is and what kind of event this new input device can produce. Finally, the adxl34x driver registers the input device by calling Input_register_device ().

This adds the new input device structure to the list of links to the input driver and invokes the connection function of the device handler module, informing it that a new input device has occurred. From this point on, the device can produce interrupts. Once the interrupt service routine is executed, the State Register and the event FIFO are read from the accelerometer and the appropriate event is sent back to the input subsystem using nput_event ().

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.