Hierarchical Design of Linux Device Drivers)

Source: Internet
Author: User
1.1 In object-oriented programming, the device driver core layer and examples can define a base class for a class of similar things, and specific things can inherit the functions in this base class. If the implementation of a function of the inherited object is the same as that of the base class, it can directly inherit the functions of the base class. On the contrary, it can be overloaded. This object-oriented design method greatly improves the code reusable ability and is a good display of the relationship between real-world things. The Linux kernel is fully written in C and assembly languages, but is frequently used in object-oriented design. In terms of device drivers, a framework is often designed for similar devices, while the core layer in the framework implements some common functions of the device. Similarly, if a specific device does not want to use functions at the core layer, it can be reloaded. For example, return_type core_funca (xxx_device * bottom_dev, param1_type param1, param1_type param2) {If (bottom_dev-> funca) return bottom_dev-> funca (param1, param2 ); /* Common funca code at the core layer */...} in the preceding Implementation of core_funca, the system checks whether the underlying device has overloaded funca (). If the function is reloaded, the underlying code is called. Otherwise, the generic layer is directly used. The advantage of doing so is that the Code at the core layer can handle the funca () function of the vast majority of such devices, and only a few special devices need to implement funca () again (). Let's look at another example: return_type core_funca (xxx_device * bottom_dev, param1_type param1, param1_type param2) {/* Common step code */... bottom_dev-> funca_ops1 ();/* General step code B */... bottom_dev-> funca_ops2 ();/* General step code C */... bottom_dev-> funca_ops3 ();} the above Code assumes that the operation process is consistent for similar devices to implement funca, the steps of "General Code A, underlying ops1, general code B, underlying OPS2, general code C, and underlying OPS3" are required. The advantages of hierarchical design are, for general code A, B, and C, the specific underlying driver does not need to be implemented, but only concerned with its underlying operations ops1, OPS2, and OPS3. Figure 1 clearly shows the relationship between the core layer of the device driver and the specific device driver. In fact, this layer may only have two layers (A in Figure 1 ), it may also be Multilayer (B in Figure 1 ). Figure 1 hierarchical design of Linux device drivers is not uncommon among input, RTC, MTD, I2 C, SPI, tty, USB, and many other device driver types in Linux. The following two sections describe input and RTC first. Of course, the subsequent sections will analyze the driver layers corresponding to several large device types in more detail. 1.2 A typical character input device (such as buttons, keyboards, touch screens, and mouse, the general working mechanism is that the underlying layer generates an interruption when the buttons, touch, and other actions are sent (or the driver regularly queries through timer ), then, the CPU reads key-value, coordinate, and other data through SPI, I2 C, or external memory bus, and puts the data into a buffer zone. The character device driver manages the buffer zone, while the drive read () allows you to read data such as key values and coordinates. Obviously, in these operations, the interrupt and read values are device-related, while the buffer management of input events and the file_operations interface driven by character devices are common to the input devices. Based on this, the kernel designs the input subsystem, which processes public work by the core layer. The framework 2 of the Linux Kernel Input subsystem is shown in. Figure 2 hierarchical input core of the Linux Input Device Driver provides the APIS required by the underlying input device driver, such as allocating/releasing an input device: struct input_dev * input_allocate_device (void ); void input_free_device (struct input_dev * Dev); input_allocate_device () returns a struct of input_dev, which is used to characterize one input device. The following interfaces are used to register or cancel an input device: int _ must_check input_register_device (struct input_dev *); void input_unregister_device (struct input_dev *); The following interfaces are used to report input events: /* Report input events of the specified type and code */void input_event (struct input_dev * Dev, unsigned int type, unsigned int code, int value ); /* report key value */void input_report_key (struct input_dev * Dev, unsigned int code, int value);/* report relative coordinates */void input_report_rel (struct input_dev * Dev, unsigned Int code, int value);/* report absolute coordinates */void input_report_abs (struct input_dev * Dev, unsigned int code, int value ); /* Report the synchronization event */void input_sync (struct input_dev * Dev). The kernel describes all input events in a unified data structure. The data structure is input_event, such as code listing 7. Code List 7 input_event struct 1 struct input_event {2 struct timeval time; 3 _ b2type; 4 _ b2code; 5 _ s32 value; 6 }; drivers/input/keyboard/gpio_keys.c implements a general gpio key driver based on the input architecture. The driver is based on the platform_driver architecture and is named "gpio-keys ". It shields hardware-related information (such as the gpio number used and the electronic equality) in the platform_data of the platform_device board file. Therefore, this driver can be applied to various processors and has good cross-platform performance. Listing 8 lists the probe () Functions of the driver. Code List 8 gpio key-driven probe () function 1 static int _ devinit gpio_keys_probe (struct platform_device * pdev) 2 {3 struct gpio_keys_platform_data * pdata = pdev-> Dev. platform_data; 4 struct gpio_keys_drvdata * dData; 5 struct input_dev * input; 6 int I, error; 7 int wakeup = 0; 89 dData = kzarloc (sizeof (struct gpio_keys_drvdata) + 10 pdata-> nbuttons * sizeof (struct gpio_button_data), 11 gfp_kernel ); 12 input = input_allocate_device ();13 if (! DData |! Input) {14 Error =-enomem; 15 goto fail1; 16} 17 18 platform_set_drvdata (pdev, dData); 19 20 input-> name = pdev-> name; 21 Input-> phys = "gpio-keys/input0"; 22 input-> Dev. parent = & pdev-> dev; 23 24 input-> ID. bustype = bus_host; 25 input-> ID. vendor = 0x0001; 26 Input-> ID. product = 0x0001; 27 input-> ID. version = 0x0100; 28 29 dData-> input = input; 30 31 for (I = 0; I <pdata-> nbuttons; I ++) {32 struct gpio_keys _ Button * button = & pdata-> buttons [I]; 33 struct gpio_button_data * bdata = & dData-> data [I]; 34 int IRQ; 35 unsigned int type = button-> type?: Ev_key; 36 37 bdata-> input = input; 38 bdata-> button = button; 39 setup_timer (& bdata-> timer, 40 gpio_check_button, (unsigned long) bdata ); 41 42... 43 error = request_irq (IRQ, gpio_keys_isr, 44 ir1__sample_random | ir1__trigger_rising | 45 ir1__trigger_falling, 46 button-> DESC? Button-> Desc: "gpio_keys", 47 bdata); 48 if (error) {49... 50} 51 52 If (button-> wakeup) 53 wakeup = 1; 54 55 input_set_capability (input, type, button-> Code );56} 57 58 error = input_register_device (input );59 If (error) {60 pr_err ("gpio-keys: unable to register input device," 61 "error: % d \ n", error); 62 goto fail2; 63} 64 65 device_init_wakeup (& pdev-> Dev, wakeup); 66 67 return 0; 68... 69} the first line of the above Code is allocated with one input device, 12th ~ Row 27 initializes some attributes of the input_dev, and row 58th registers the input device. 31st ~ In line 56, the interrupt number required by the gpio key device is applied and timer is initialized. Row 3 sets the information that the input device can tell. After registering the input device, the core work of the underlying input device driver is only reported when human actions such as buttons and touch occur. Code List 9 lists the event reporting code when the gpio button is interrupted. Code List 9: Event Report 1 static void gpio_keys_report_event (struct gpio_button_data * bdata) 2 {3 struct gpio_keys_button * button = bdata-> button; 4 struct input_dev * input = bdata-> input; 5 unsigned int type = button-> type? : Ev_key; 6 int state = (gpio_get_value (button-> gpio )? 1: 0) ^ button-> active_low; 7 8 input_event (input, type, button-> code ,!! State ); 9 input_sync (input );10} 11 12 static irqreturn_t gpio_keys_isr (int irq, void * dev_id) 13 {14 struct gpio_button_data * bdata = dev_id; 15 struct gpio_keys_button * button = bdata-> button; 16 17 bug_on (IRQ! = Gpio_to_irq (button-> gpio); 18 19 if (button-> debounce_interval) 20 mod_timer (& bdata-> timer, 21 jiffies + trim (button-> debounce_interval )); 22 else23 gpio_keys_report_event (bdata); 24 25 return irq_handled; 26} row 8th is the report key value, while row 9th is a synchronization event, it indicates that the previously reported message belongs to one message group. For example, after the X coordinate is reported, the user reports the Y coordinate, and then reports one synchronization event, the application can know that the X and Y events previously reported belong to one group, it combines two to form the coordinates of one (x, y. In code list 8, Row 3 obtains platform_data, while platform_data is actually an array that defines the hardware information of gpio buttons. Row 3's for loop tool applies for gpio and initializes the interrupt. For the ldd6140 circuit board, this information is shown in code 10. Code List 10 platform_data1 static struct gpio_keys_button ldd6410_buttons [] = {2 {3. gpio = maid (0), 4. code = key_down, 5. desc = "down", 6. active_low = 1,7}, 8 {9. gpio = maid (1), 10. code = key_enter, 11. desc = "enter", 12. active_low = 1, 13. wakeup = 1,14}, 15 {16. gpio = maid (2), 17. code = key_home, 18. desc = "home", 19. active_low = 1,20}, 21 {22. gpio = 89c64xx_gp N (3), 23. code = key_power, 24. desc = "power", 25. active_low = 1, 26. wakeup = 1,27}, 28 {29. gpio = maid (4), 30. code = key_tab, 31. desc = "tab", 32. active_low = 1,33}, 34 {35. gpio = maid (5), 36. code = key_menu, 37. desc = "menu", 38. active_low = 1,39}, 40}; 41 42 static struct gpio_keys_platform_data ldd6410_button_data = {43. buttons = ldd6410_buttons, 44. nbuttons = array_size (ldd641 0_buttons), 45}; 46 47 static struct platform_device ldd6410_device_button = {48. name = "gpio-keys", 49. id =-1, 50. dev = {51. platform_data = & ldd6410_button_data, 52} 53}; 1.3 RTC device drives RTC (Real-time clock) powered by battery, and can still walk when the system powers down. It also has the ability to generate periodic interruptions and generate an alarm clock (Alarm) interrupt. It is a typical character device. As a character device driver, RTC needs to implement interface functions in file_operations, such as open (), release (), read (), Poll (), IOCTL (), etc, typical IOCTL includes rtc_set_time, rtc_alm_read, rtc_alm_set, rtc_irqp_set, rtc_irqp_read, and so on. These are common for all RTC, and only the underlying implementation is device-related. Therefore, drivers/RTC/rtc-dev.c realizes the RTC driver generic character device driver layer, which implements the file_opearations member function and some general control code about RTC, export rtc_device_register () and rtc_device_unregister () to the underlying layer for registration and cancellation of RTC. Export the rtc_class_ops struct to describe the underlying RTC hardware operations. The result of this RTC universal layer implementation is that the underlying RTC driver no longer needs to care about the specific implementation of RTC as the character device driver, and does not need to care about some general RTC control logic, figure 3 shows the relationship. Figure 3 the hierarchical Drivers/RTC/rtc-s3c.c of Linux RTC Device Driver realizes the RTC driver of cloud6410. It registers RTC and binds the rtc_class_ops Code such as the code list 11. Code List 11. register the rtc_class_ops instance with rtc 1 static const struct rtc_class_ops initi_rtcops = {2. open = maid, 3. release = maid, 4. IOCTL = maid, 5. read_time = maid, 6. set_time = maid, 7. read_alarm = maid, 8. set_alarm = maid, 9. irq_set_freq = maid, 10. irq_set_state = maid, 11. proc = maid, 12}; 13 14 static int maid (struct platform_device * pdev) 15 {16... 17 RTC = rtc_device_register ("s3c", & pdev-> Dev, & initi_rtcops,18 this_module); 19... 20} http://21cnbao.blog.51cto.com/109393/336263

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.