
Bài viết này mở đầu trong chuỗi bài viết về driver cho thiết bị USB trên Linux (usb device driver). Để tiếp cận một cách nhanh nhất đến nội dung này, chúng ta sẽ sử dụng một thiết bị ổ nhớ usb (usb flash drive) để tìm hiểu quá trình hệ thống Linux làm việc với thiết bị này và thử nghiệm viết một driver đơn giản cho nó, cái mà có thể áp dụng tương tự cho các thiết bị usb với chức năng khác. Thiết bị ổ nhớ usb được sử dụng là Flash Voyager của nhà sản xuất Feiya Technology Corp, có vendor ID 0x090c và product ID 0x1000
Quá trình nhận dạng thiết bị usb trên Linux
Khi có một thiết bị usb hợp lệ được cắm vào hệ thống Linux, cho dù nó có driver hay không thì nó cũng vẫn được nhận diện (detect) bởi phần cứng ở tầng nhân (kernel space) của hệ thống Linux mà đã được hỗ trợ giao thức usb. Hệ thống có thể làm điều này là bởi vì khả năng của chính bản thân giao thức usb đã được thiết kế trong đặc tả của nó. Cụ thể, việc phát hiện ra thiết bị usb cắm vào được thực hiện bởi chip usb host controller (là thiết bị chủ động đường bus của giao thức usb). USB host controller này sẽ thu thập và diễn giải các thông tin ở tầng vật lý (low-level) đến các thông tin đặc tả giao thức USB ở tầng trên (high-level). Các thông tin về thiết bị theo khuôn dạng qui định của giao thức USB lại tiếp tục được đưa vào tầng usb core tổng quát (generic usb core) trong tầng nhân (được điều khiển bởi usbcore driver). Chính điều này giúp cho các thiết bị usb được hệ thống nhận diện ở tầng nhân, mặc dù nó có thể chưa có một driver cụ thể nào cho chức năng của nó.
Sau quá trình diễn ra ở tầng nhân này, sẽ đến nhiệm vụ của các drivers hoặc interfaces hoặc applications (cái mà phụ thuộc vào các bản Linux khác nhau) để tiếp tục nhận dạng ra thiết bị ở tầng người dùng (user space). Hình dưới minh họa cho kiến trúc phân tầng từ trên xuống của hệ thống USB trên Linux
Hình 1. Hệ thống USB trên Linux
Trên Linux, có thể sử dụng lệnh lsusb để liệt kê danh sách thông tin cơ bản của tất cả các thiết bị usb được hệ thống nhận diện. Để xem thông tin chi tiết về 1 thiết bị nào đó, cần sử dụng thêm tham số -vd (<vendor ID>:<product ID>). Minh họa như hình dưới
Hình 2. Xem thông tin các thiết bị USB sử dụng lệnh lsusb
Trong nhiều bản Linux như Mandriva, Fedora, … driver usbfs sẽ được nạp như một thành phần của cấu hình mặc định. Điều này cho phép thông tin chi tiết về thiết bị usb đã nhận diện được có thể quan sát dưới góc độ kỹ thuật thông qua file thông tin thiết bị trong /proc (sử dụng lệnh cat để mở cat /proc/bus/usb/devices). Cũng cần lưu ý luôn rằng, nhiều hệ thống Linux mới không còn chứa thông tin ở đường dẫn này nữa mà đặt trong /sys/kernel/debug/usb/devices ). Hình dưới chỉ ra các thông tin về thiết bị ổ nhớ usb đang sử dụng được cắm vào linux 2.6.35.7 trên KIT FriendlyARM Tiny6410 (hoặc mở file cat /proc/bus/usb/devices trên Ubuntu 10.04). Chú ý là kết quả xuất ra màn hình chứa thông tin cơ bản về tất cả các thiết bị usb được nhận diện trên hệ thống trong đó có vùng thông tin cho thiết bị ổ nhớ usb Flash Voyager đang sử dụng.
Hình 3. Xem thông tin đặc tả thiết bị USB trong /proc/bus/usb/devices (hoặc /sys/kernel/debug/usb/devices)
Giải mã thông tin về thiết bị USB
Để tiếp tục giải mã vùng thông tin để cập trên về thiết bị USB, hệ thống cần hiểu về một thiết bị USB hợp lệ được nhận dạng. Đầu tiên, tất cả các thiết bị USB hợp lệ đều chứa thông tin về một hoặc một vài cấu hình (configurations). Một cấu hình của thiết bị usb giống như một bản hồ sơ về thiết bị đó. Linux chỉ hỗ trợ một cấu hình cho mỗi một thiết bị, do vậy nếu thiết bị có nhiều cấu hình nó sẽ chỉ được hệ thống sử dụng một cái mặc định là cái được sử dụng phổ biến nhất. Với mỗi cấu hình, thiết bị lại có một hoặc một vài giao diện (interfaces). Mỗi một giao diện tương ứng với một chức năng (function) của thiết bị đó. Tức là, số giao diện là bằng với số chức năng của thiết bị usb đó. Ví dụ, một thiết bị máy in usb đa chức năng (MFD – multi-function device) có thể thực hiện in ấn, quét tài liệu, gửi fax, do đó nó cần ít nhất 3 giao diện, mỗi cái ứng với một chức năng. Vì vậy, không giống như các driver thiết bị đơn chức năng khác, driver cho thiết bị usb thông thường sẽ gắn với một giao diện (chức năng) nào đó của thiết bị usb đấy chứ không thường cho toàn bộ chức năng của thiết bị. Điều đó cũng có nghĩa là thiết bị usb đó có thể có nhiều driver và nhiều giao diện khác nhau có thể cùng chung một driver, nhưng ngược lại mỗi giao diện chỉ có tối đa 1 driver. Tuy nhiên, cũng hoàn toàn là phù hợp nếu thiết bị sử dụng một driver cho tất cả các giao diện của nó.
Mỗi giao diện (interface) lại có một hoặc một vài end-points. Mỗi end-point là một bộ đệm dữ liệu trên thiết bị gắn kết với một đường ống (pipe) logic dùng để truyền thông tin từ hoặc đến thiết bị (giống như “hút” hoặc “bơm” dữ liệu giữa thiết bị và máy host), việc này phụ thuộc vào các chức năng của thiết bị đó. Dựa trên các kiểu thông tin cần trao đổi, có 4 loại end-points (tương ứng với 4 kiểu truyền):
- Control (Kiểu điều khiển)
- Interrrupt (Kiểu ngắt)
- Bulk (Kiểu khối dữ liệu đảm bảo tính chính xác khi truyền)
- Isochronous (Kiểu khối dữ liệu đảm bảo về thời gian truyền)
Theo đặc tả của giao thức USB, tất cả các thiết bị USB hợp lệ đều ngầm định sử dụng end-point 0 để truyền điều khiển (control), đây là end-point duy nhất có 2 chiều (bi-directional). Hình dưới minh họa biểu diễn của một thiết bị usb theo các phân tích ở trên.
Hình 4. Mô hình biểu diễn tổng quan thiết bị USB
Trở lại phân tích vùng thông tin thiết bị USB quan sát được trong hình 3, các chữ cái đầu của mỗi dòng sẽ giải thích các thành phần khác nhau trong đặc tả của thiết bị USB. Ví dụ chữ D (Description) cho dòng mô tả thiết bị, chữ C (Configuration) cho cấu hình, chữ I (interface) chữ E (endpoint), v.v… Chi tiết về các thông tin này và các thông tin khác về thiết bị được mô tả trong tài liệu Documentation/usb/proc_usb_info.txt có trong mã nguồn nhân (kernel source) của hệ thống Linux
T = Topology (etc.)
B = Bandwidth (applies only to USB host controllers, which are virtualized as root hubs)
D = Device descriptor info.
P = Product ID info. (from Device descriptor, but they won't fit together on one line)
S = String descriptors.
C = Configuration descriptor info. (* = active configuration)
I = Interface descriptor info.
E = Endpoint descriptor info.
Đăng ký driver thiết bị USB Flash Drive
Như đề cập ở trên, có thể thấy rằng để viết driver cho thiết bị USB, có rất nhiều điều chi tiết cần tìm hiểu về giao thức USB: cấu hình thiết bị (configuration), các giao diện (interfaces) của thiết bị, các kiểu truyền, endpoints, … nằm trong đặc tả của thiết bị USB. Tuy nhiên, các vấn đề này sẽ tiếp tục được đề cập sau. Trước hết, chúng ta sẽ làm quen với một driver đơn giản đầu tiên cho thiết bị ổ nhớ usb đã đề cập, cho một interface của nó để thực hiện đăng ký thiết bị với hệ thống (gọi là pen_register driver).
Việc xây dựng driver này cũng giống như bất kỳ một driver thiết bị nào khác trên Linux, cần sử dụng một khuôn mẫu chung (có hàm tạo, hàm hủy). Tuy nhiên, nội dung chi tiết sẽ rất khác vì đây là một driver cho lớp giao thức phần cứng không giống như một character driver thông thường. Thay vì thực hiện đăng ký và hủy đăng ký với hệ thống file ảo (VFS – virtual file system) như của character driver thì ở đây, USB device driver cần thực hiện công việc tương ứng với lớp giao thức USB core; và thay vì cung cấp giao diện lên tầng người dùng (user space) giống như file thiết bị thì ở đây, usb driver cần thực hiện kết nối đến thiết bị thật trong tầng phần cứng (hardware-space).
Các hàm API của tầng USB core để thực hiện đăng ký và hủy đăng ký thiết bị như sau (nằm trong tệp tiêu đề <linux/usb.h>):
int usb_register(struct usb_driver *driver); void usb_deregister(struct usb_driver *); |
Cấu trúc usb_driver chứa các trường cung cấp tên driver, bảng ID để dò thiết bị tự động, và 2 hàm callback được gọi bởi USB core trong quá trình kết nối thiết bị (hot plugging) và gới thiết bị (hot removal). Mã nguồn cụ thể như sau (file pen_register.c):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
static int pen_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
printk(KERN_INFO "Pen drive (%04X:%04X) plugged\n", id->idVendor, id->idProduct);
return 0;
}
static void pen_disconnect(struct usb_interface *interface)
{
printk(KERN_INFO "Pen drive removed\n");
}
static struct usb_device_id pen_table[] =
{
{ USB_DEVICE(0x058F, 0x6387) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);
static struct usb_driver pen_driver =
{
.name = "pen_driver",
.id_table = pen_table,
.probe = pen_probe,
.disconnect = pen_disconnect,
};
static int __init pen_init(void)
{
return usb_register(&pen_driver);
}
static void __exit pen_exit(void)
{
usb_deregister(&pen_driver);
}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia");
MODULE_DESCRIPTION("USB Pen Registration Driver");
Thực hiện biên dịch driver trên (sử dụng Makefile) cho hệ thống Linux sử dụng:
- Biên dịch bằng lệnh make (tạo ra file pen_register.ko)
- Nạp driver sử dụng insmod
- Xem các module đã nạp dùng lệnh lsmod
- Gỡ driver sử dụng lệnh rmmod
Tuy nhiên, đến đây khi chúng ta sử dụng lệnh dmesg hoặc mở file bằng cat /proc/bus/usb/devices) để xem kết quả thì driver được nạp vào chưa được sử dụng. Lý do là bởi vì thiết bị ổ nhớ usb này đã có một interface (số 0) đã được liên kết với usb-storage driver (driver thường dùng có sẵn có các thiết bị ổ nhớ USB). Vì vậy, để thiết bị liên kết với driver đã viết ở trên, cần gỡ bỏ driver usb-storage có sẵn này (bằng cách rmmod usb-storage, thực hiện khi thiết bị đã ngắt kết nối), sau đó cắm thiết bị trở lại vào hệ thống để thiết bị nhận driver đã viết ở trên. Hình dưới minh quá quá trình thử nghiệm driver này.
Hình 5. Thử nghiệm với usb driver pen_register
Trong phần tiếp theo chúng ta sẽ tiếp tục phát triển mã nguồn driver cho thiết bị ở nhớ usb này một cách chi tiết hơn liên quan đến các đặc tả của giao thức USB.
Nguồn: hungpn
Mong Muốn Có Thêm Cơ Hội Trong Công Việc
Và Trở Thành Một Người Có Giá Trị Hơn
Bạn Chưa Biết Phương Thức Nào Nhanh Chóng Để Đạt Được Chúng
Hãy Để Chúng Tôi Hỗ Trợ Cho Bạn. SEMICON
Hotline: 0972.800.931 - 0938.838.404 (Mr Long)