Viết driver cho bo nho ngoai linux

Bài viết này nằm trong loạt bài viết về device driver trên Linux, trình bày về cách nạp driver vào hệ thống tại thời điểm hệ thống đã thực thi mà không cần biên dịch, nạp lại nhân hệ điều hành (nạp động) và làm quen với xây dựng một driver đầu tiên.

Device Drivers, Phần 1. Cơ bản về driver trên Linux

Device Drivers, Phần 2. Viết driver đầu tiên trên Linux

Device Drivers, Phần 3. Lập trình C ở tầng nhân

Device Drivers, Phần 4. Linux Character Drivers

Device Drivers, Phần 5. Character Device Files

Device Drivers, Phần 6. Các thao tác đối với file thiết bị

Nạp động driver vào hệ thống

Các driver có thể nạp động vào hệ thống thường được xây dựng dựa trên cơ chế modules và được biên dịch tạo thành một file với đuôi mở rộng .ko (kernel object). Mỗi hệ thống Linux qui định một nơi để chứa các modules đã được biên dịch có sẵn, thường được tổ chức trong thư mục /lib/modules//kernel  trong đó, / là phiên bản nhân hệ thống Linux đang sử dụng (có thể lấy bằng lệnh uname –r). Ví dụ xem thông tin về các modules có sẵn như trong hình

Viết driver cho bo nho ngoai linux

Hình 1. Xem các module sẵn có trên Linux

Để nạp động, hoặc gỡ một modules, sử dụng các lệnh sau (nằm trong thư mục /sbin, và cần được thực thi dưới quyền root)

§  lsmod — Liệt kê các modules đã được nạp

§  insmod  — Nạp một module vào hệ thống (yêu cầu modules phụ thuộc nếu có phải được nạp trước)

§  modprobe  — Nạp một module vào hệ thống (Tự động tìm các module phụ thuộc nếu có để nạp)

§  rmmod  — Gỡ một module khỏi hệ thống.

Ví dụ sau minh họa thao tác lệnh để nạp các driver liên quan đến hệ thống quản lý file FAT. Các file modules cần nạp là fat.ko, vfat.ko nằm trong thư mục fat ở đường dẫn /lib/modules/`uname -r`/kernel/fs

Viết driver cho bo nho ngoai linux

Hình 2. Các thao tác (nạp, gỡ) module trên Linux

Module vfat phụ thuộc vào module fat, do vậy fat.ko cần được nạp trước. Việc này có thể thực hiện tự động nếu sử dụng lệnh modprobe. Sử dụng lệnh rmmod để gỡ modules khỏi hệ thống (không cần phải xác định đuôi mở rộng .ko)

Viết driver đầu tiên

Một driver không thể tự nó thực thi mà hoạt động tương tự như một thư viện được nạp và đăng ký các hàm bởi một ứng dụng đang chạy. Driver được viết bằng C, nhưng không có hàm main(). Hơn nữa, bởi vì driver được nạp và liên kết với hệ điều hành, nên nó cần được biên dịch giống cách biên dịch nhân hệ điều hành, và các header files được sử dụng trong mã nguồn driver chỉ là những cái mà nhân hệ điều hành cung cấp, không bao giờ có các hàm của thư viện lập trình C (mà thường để trong thư mục /usr/include).

Một điểm thú vị khác là mã nguồn nhân được lập trình theo kiểu hướng đối tượng trong C, mã nguồn driver cũng tương tự như vậy. Bất kỳ một driver nào trên Linux đều có một hàm tạo (constructor) và một hàm hủy (destructor). Hàm tạo của driver được gọi khi driver được nạp vào nhân hệ thống, và hàm hủy được gọi khi gỡ driver khỏi hệ thống (dùng lệnh rmmod). Hai hàm này cũng giống như các hàm thông thường, ngoại trừ việc chúng cần có các chỉ thị _init_exit tương ứng và phải được đăng ký sử dụng các macros module_init() và module_exit(), (được định nghĩa trong tệp tiêu đề module.h)

Sau đây là mã nguồn cho một driver đơn giản đầu tiên trên linux

/* ofd.c – Our First Driver code */

#include

#include

#include

staticint__init ofd_init(void) /* Constructor */

{

    printk(KERN_INFO "Hello: ofd registered");

    return0;

}

staticvoid__exit ofd_exit(void) /* Destructor */

{

    printk(KERN_INFO "Goodbye: ofd unregistered");

}

module_init(ofd_init);

module_exit(ofd_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Anil Kumar Pugalia");

MODULE_DESCRIPTION("Our First Driver");

Mã nguồn driver được lưu thành file ofd.c. Chú ý rằng trong mã nguồn sẽ không cho phép các thư viện ở tầng ứng dụng như stdio.h, thay vì đó sử dụng các thư viện tầng nhân như kernel.h. Hàm printk() tương đương như printf() tuy nhiên dữ liệu không xuất ra thiết bị ra chuẩn mà xuất ra log file của kernel và phải sử dụng các công cụ đặc biệt để mở. (Ví dụ lệnh dmesg hoặc công cụ Log File Viewer trên Ubuntu). Tệp tiêu đề version.h bao gồm thông tin phiên bản module tương thích với nhân mà module sẽ nạp vào. Các macros MODULE_* cung cấp các thông tin liên quan đến module, đóng vai trò như chữ ký cho module đó.

Biên dịch driver đầu tiên

Sau khi đã có mã nguồn C của driver đơn giản như trên, cần tiến hành biên dịch để tạo ra file module (có đuôi mở rộng .ko, trong trường hợp này là file ofd.ko). Chúng ta sử dụng cách thức biên dịch ở tầng nhân để làm điều này. Cụ thể cần viết một Makefile trong đó thực hiện việc đăng ký thư viện biên dịch ở tầng nhân (kernel build system) từ mã nguồn nhân, và đăng ký driver đang cần biên dịch.

Do vậy, trước khi chạy Makefile để biên dịch driver trên Linux, cần phải có mã nguồn nhân (kernel source) của hệ thống sẽ tiếp nhận driver. Giả sử mã nguồn nhân (kernel source) của hệ thống tương ứng được cài tại /usr/src/linux, (nếu không cần phải chỉ ra vị trí của nó trong biến KERNEL_SOURCE ở trong Makefile)

Sau khi có mã nguồn driver (ofd.c) và Makefile, đặt trong cùng thư mục, sử dụng lệnh make để build driver này. Quá trình biên dịch thành công sẽ tạo ra file ofd.ko để nạp vào hệ thống.

$ make

make -C /usr/src/linux SUBDIRS=... modules

make[1]: Entering directory `/usr/src/linux'

  CC [M]  .../ofd.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      .../ofd.mod.o

  LD [M]  .../ofd.ko

make[1]: Leaving directory `/usr/src/linux'

Nạp module ofd.ko vào hệ thống được thực hiện với quyền root (hoặc sudo)

# su

# insmod ofd.ko

# lsmod | head -10

hungpn, 31/03/2014