Bài giảng Lập trình hệ thống

Bài 1: TỔNG QUAN VỀ LẬP TRÌNH HỆ

THỐNG

Khái niệm về lập trình hệ thống

Lập trình hệ thống (hoặc chương trình hệ thống) là hoạt động của các phần mềm hệ

thống. Đầu tiên chỉ ra sự khác biệt tiêu biểu của các chương trình hệ thống khi đã so

sánh tới lập trình ứng dụng là ở đó nhắm vào lập trình ứng dụng để sản sinh phần mềm

mà cung cấp những dịch vụ tới người dùng (ví du: bộ xử lý văn bản), trong khi những

nhà lập trình hệ thống nhắm vào việc sản xuất phần mềm mà cung cấp những dịch vụ

tới phần cứng máy tính (ví dụ: phần mềm chống phân mảnh đĩa). Nó cũng yêu cầu một

độ lớn hơn của sự ý thức phần cứng.

pdf371 trang | Chia sẻ: phuongt97 | Lượt xem: 585 | Lượt tải: 0download
Bạn đang xem trước 20 trang nội dung tài liệu Bài giảng Lập trình hệ thống, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
hất có thể. Hàm SetupDevice là khá đơn giản : VOID SetupDevice(PDEVICE_EXTENSION pdx) { WRITE_PORT_ULONG((PULONG) (pdx->portbase + INTCSR), INTCSR_IMBI_ENABLE │ (INTCSR_MB1 << INTCSR_IMBI_REG_SELECT_SHIFT) │ (INTCSR_BYTE0 << INTCSR_IMBI_BYTE_SELECT_SHIFT) ); } Hàm này lập trình lại INTCSR để chỉ định điều chúng ta muốn một ngắt được xảy ra khi có sự thay đổi tới byte 0 của thanh ghi hộp thư về 1. chúng ta có thể chỉ định các điều kiện ngắt khác cho chip này, bao gồm sự “trống rỗng” của một byte riêng biệt của một thanh ghi hộp thư đến được chỉ định, việc hoàn thành một tiến trình chuyển DMA đọc, và việc hoàn thành của một tiến trình chuyển DMA ghi Bắt đầu một thao tác đọc -Starting a Read Operation PCI42’s StartIo routine cho phép “mô hình” mà chúng ta đã được học rồi. VOID StartIo(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { 251/369 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); if (!stack->Parameters.Read.Length) { StartNextPacket(&pdx->dqReadWrite, fdo); CompleteRequest(Irp, STATUS_SUCCESS, 0); return; } pdx->buffer = (PUCHAR) Irp->AssociatedIrp.SystemBuffer; pdx->nbytes = stack->Parameters.Read.Length; pdx->numxfer = 0; KeSynchronizeExecution(pdx->InterruptObject, (PKSYNCHRONIZE_ROUTINE) TransferFirst, pdx); } 1. Ở đây chúng ta ghi lại các thông số trong phần mở rộng thiết bị để mô tả tiến trình vào của một thao tác đầu vào. Chúng ta bảo đảm PCI42 sử dụng phương thức DO_BUFFERED_IO, là không điển hình nhưng nó giúp chúng ta tạo ra trình điều khiển đủ đơn giản để được sử dựng như một ví dụ. 2. Bởi vì ngắt của chúng ta đã được kết nối, thiết bị của chúng ta có thể ngắt bất kỳ lúc nào. ISR sẽ muốn truyền các bytes dữ liệu khi các ngắt xảy ra, nhưng chúng ta muốn được đảm bảo rằng ISR không bao giờ bị lộn xộn, rắc rối về bộ đệm dữ liệu để sử dụng hoặc về số các bytes chúng ta đang cố gắng đọc. Để kiềm chế “tính hám” của ISR, chúng ta đặt một cờ trong phần mở rộng thiết bị định bận rộn mà thông thường là FALSE. 252/369 Bây giờ là lúc thiết lập cờ về giá trị TRUE. Giống như thông thường khi chúng ta giải quyết với một tài nguyên chia sẻ , chúng ta cần phải đồng bộ việc thiết lập cờ với đoạn mã trong ISR mà kiểm tra nó, và theo đó cần phải viện dẫn một thủ tục SynchCritSection giống như tôi đã thảo luận từ trước. Ngòai ra, có thể xảy ra một byte dữ liệu đã có sẵn rồi. trong trường hợp đó thì ngắt đầu tiên sẽ không bao giờ xảy ra . TransferFirst là một thủ tục trợ giúp mà kiểm tra việc đọc và những kết quả có thể xảy ra cho byte đầu tiên này. Hàm thêm vào “add-on function” có nhiều cách để nhận biết việc xóa sạch hộp mail (hộp mail rỗng), vì thế nó có thể đoán chừng để gửi byte tiếp theo vào thời điểm thích hợp. . Đây là TransferFirst: 1. VOID TransferFirst(PDEVICE_EXTENSION pdx) 2. { 3. pdx->busy = TRUE; 4. ULONG mbef = READ_PORT_ULONG((PULONG) (pdx- >portbase + MBEF)); 5. if (!(mbef & MBEF_IN1_0)) 6. return; 7. 8. *pdx->buffer = READ_PORT_UCHAR(pdx->portbase + IMB1); 9. ++pdx->buffer; 10. ++pdx->numxfer; 11. if (—pdx->nbytes == 0) 12. { 13. pdx->busy = FALSE; 14. PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite); 15. Irp->IoStatus.Status = STATUS_SUCCESS; 16. Irp->IoStatus.Information = pdx->numxfer; 17. IoRequestDpc(pdx->DeviceObject, NULL, pdx); 18. } } S5933 có một thanh ghi rỗng/đầy cho hộp thư (empty/full register) (MBEF) các bit của chúng chỉ ra trạng thái hiện hành của mỗi byte của mỗi thanh ghi hộp mail. ở đây chúng ta kiểm tra liệu rằng byte thanh ghi chúng ta sử dụng cho đầu (giá trị thanh ghi hộp mail từ 1, về 0) hiện giờ không được đọc. Nếu như vậy, chúng ta sẽ đọc nó, điều này quả thật làm rỗng bộ đếm truyền. chúng ta đã có một thủ tục con rồi (DpcForIsr) thủ tục này biết phải làm gì với một yêu cầu đầy đủ, vì thế chúng ta yêu cầu một DPC nếu như byte đầu tiên thỏa mãn được yêu cầu. (Gọi lại rằng chúng ta đang thực thi tại DIRQL dưới sự bảo vệ của một khóa quay ngắt bởi vì chúng ta đã viện dẫn như môt thủ tục SynchCritSection vì thế chúng ta ko thể chỉ hoàn thành IRP ngay bây giờ ) 253/369 Sử dụng ngắt (Handling the Interrupt ) Trong thao tác thông thường với PCI42, các ngắt S5933 khi một byte dữ liệu mới đến một hộp thư 1. ISR sau đây sẽ giành được điều khiển: BOOLEAN OnInterrupt(PKINTERRUPT InterruptObject, PDEVICE_EXTENSION pdx) { ULONG intcsr = READ_PORT_ULONG((PULONG) (pdx->portbase + INTCSR)); if (!(intcsr & INTCSR_INTERRUPT_PENDING)) return FALSE; BOOLEAN dpc = FALSE; PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite); if (pdx->busy) { if (Irp->Cancel) status = STATUS_CANCELLED; else status = AreRequestsBeingAborted(&pdx->dqReadWrite); if (!NT_SUCCESS(status)) dpc = TRUE, pdx->nbytes = 0; 254/369 }while (intcsr & INTCSR_INTERRUPT_PENDING) { if (intcsr & INTCSR_IMBI) { if (pdx->nbytes && pdx->busy) { *pdx->buffer = READ_PORT_UCHAR(pdx->portbase + IMB1); ++pdx->buffer; ++pdx->numxfer; if (!--pdx->nbytes) { Irp->IoStatus.Information = pdx->numxfer; dpc = TRUE; status = STATUS_SUCCESS; } } } WRITE_PORT_ULONG((PULONG) (pdx->portbase + INTCSR), intcsr); 255/369 intcsr = READ_PORT_ULONG((PULONG) (pdx->portbase + INTCSR)); } if (dpc) { pdx->busy = FALSE; Irp->IoStatus.Status = status; IoRequestDpc(pdx->DeviceObject, NULL, NULL); } return TRUE; } 1. Nhiệm vụ đầu tiên của chúng ta là khám phá xem liệu rằng thiết bị riêng của chúng ta bây giờ đang cố gắng ngắt. chúng ta đọc S5933’s INTCSR và kiểm tra một bit (INTCSR_INTERRUPT_PENDING) mà tóm tắt tất cả các lý do, nguyên nhân treo của các ngắt. Nếu như bit này bị xóa, chúng ta sẽ trả lại ngay lập tức. Lý do chúng ta lựa chọn sử dụng con trỏ mở rộng thiết bị như một đối số ngữ cảnh routine—back này khi tôi gọi IoConnectInterrupt—bây giờ nên bị xóa : chúng ta cần truy cập ngay lập tức tới cấu trúc này để có được địa chỉ cổng cơ sở. 2. Khi chúng ta sử dụng một DEVQUEUE, chúng ta dựa vào, tin tưởng vào đối tượng hàng đợi để giữ dấu vết của IRP hiện hành. Ngắt này có thể chúng ta không mong đợi bởi vì ở thời điểm hiện tại đó chúng ta không đang phục vụ bất kỳ IRp nào. Trong trường hợp đó, chúng ta vẫn phải xóa ngắt nhưng không nên làm làm bất kỳ điều gì khác. 3. Ngoài ra vẫn có thể với một sự kiện Plug and Play hoặc sự kiện nguồn đã xảy ra mà sẽ tạo ra bất kỳ IRP mới nào bị loại bỏ bởi thủ tục gửi thông điệp. Hàm AreRequestsBeingAborted của DEVQUEUE’s nói cho chúng ta rằng chúng ta có thể abort (kết thúc sớm )yêu cầu hiện hành ngay bây giờ. Việc kết thúc sớm 256/369 một yêu cầu đang hoạt động là một điều hợp lý để làm với một thiết bị chẳng hạn như “số thu thập” byte từng byte này (proceeds byte by byte) . Tương tự như vậy, một ý tưởng hay để kiểm tra liệu rằng IRP có bị dừng lại hay không nếu như nó chiếm quá nhiều thời gian để kết thúc IRP. Nếu các thiết bị ngắt của bạn chỉ khi được thực hiện với một việc truyền tải dài, bạn có thể bỏ đi bước kiểm tra này ra khỏi ISR của mình. 4. Bây giờ chúng ta bắt tay vào một vòng lặp mà sẽ kết thúc khi tất cả các ngắt thiết bị hiện hành đã được xóa. Ở cuối vòng lặp, chúng ta sẽ đọc lại INTCSR để quyết định xem liệu rằng bất kỳ các điều kiện ngắt nào có thể phát sinh. Nếu như vậy, chúng ta sẽ lặp lại vòng lặp . Chúng ta không bàn tới thời gian CPU ở đây- chúng ta muốn tránh việc để các ngắt “chảy như thác nước” vào hệ thống bởi vì việc phục vụ một ngắt là tương đối “đắt đỏ” 5. Nếu S5933 đã bị ngắt bởi vì một sự kiện mailbox , chúng ta sẽ đọc một byte dữ liệu mới từ mailbox vào trong một bộ đệm I/O cho IRP hiện hành. Nếu bạn tìm kiếm một thanh ghi MBEF ngay sau khi đọc, bạn sẽ thấy rằng việc đọc xóa bit tương ứng của thanh ghi mailbox từ 1 về 0 (inbound mailbox register 1, byte 0). Chú ý rằng chúng ta không cần thiết kiểm tra MBEF để quyết định xem liệu rằng byte của chúng ta thực thế có thay đổi hay không bởi vì chúng ta đã lập trình cho thiết bị để chỉ ngắt vào lúc có một thay đổi tới byte đơn. 6. Việc ghi INTCSR với nội dung trước đó của nó có ảnh hưởng tới việc xóa bit ngắt thứ 6 R/WC, không thay đổi một vài bit chỉ đọc (read-only bits), và bảo toàn cài đặt gốc của tất cả các bit điều khiển chỉ đọc 7. Ở đây chúng ta đọc INTCSR để quyết định xem liệu rằng các điều kiện ngắt thêm vào đó có xuất hiện hay không. nếu có chúng ta sẽ lặp lại vòng lặp để phục vụ cho chúng. 8. Giống như chúng ta đã tiến hành trên các đoạn code trước, chúng ta thiết lập biến BOOLEANdpc trở thành TRUE nếu một DPC bây giờ thích hợp để hoàn thành IRP hiện hành. DPC thường lệ cho PCI42 như sau:: VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo, PIRP junk, PDEVICE_EXTENSION pdx) { PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite); StartNextPacket(&pdx->dqReadWrite, fdo); IoCompleteRequest(Irp, IO_NO_INCREMENT); } 257/369 Testing PCI42 Nếu như bạn muốn kiểm tra PCI42 trong thao tác, bạn cần phải làm một vài việc. Đầu tiên là tìm kiếm và cài đặt một “board” phát triển S5933DK1 bao gồm card giao diện thêm vào ISA (ISA add-in interface card). Sử dụng Add Hardware wizard để cài đặt trình điều khiển S5933DK1.SYS và trình điều khiển PCI42.SYS. ( Tôi phát hiện rằngI Windows 98 đồng nhất thiết lập board phát triển như việc sẽ không chạy sound cord và tôi đã phải gỡ bỏ nó đi trong Device Manager trước khi tô có thể tiến hành cài đặt PCI42 như trình điều khiển cho nó. Nhưng Windows XP thì làm việc bình thường) Sau đó chạy cả hai chương trình ADDONSIM và TEST chúng thuộc cây thư mục PCI42 trong nội dung sách hướng dẫn. ADDONSIM ghi một giá trị dữ liệu tới một mailbox thông qua giao diện ISA. TEST đọc một byte dữ liệu từ PCI42. Việc quyết dịnh giá trị của byte dữ liệu là bài tập dành cho các bạn. 258/369 Truy nhập bộ nhớ trực tiếp (Direct Memory Access ) Windows XP hỗ trợ việc truy cập trực tiếp bộ nhớ thong qua một mô hình máy tính trừu tượng được mô tả ở hình 7.6. Trong hình này, máy tính được coi như là có một tập hợp sơ đồ các thanh ghi cái mà được chuyển đổi giữa địa chỉ vật lý của CPU và địa chỉ bus. Mỗi sơ đồ địa chỉ thanh ghi này lưu giữ địa chỉ của một trang vật lý nào đó. Phần cứng truy cập bộ nhớ để đọc hay ghi bằng cách chỉ ra địa chỉ bus hay địa chỉ logic. Sơ đồ các thanh ghi này thực hiện cùng một vai trò khi tiếp nhận bảng trang cho phần mềm bằng cách cho phép phần cứng sử dụng các giá trị số khác nhau cho các địa chỉ của nó hơn là để cho CPU hiểu. Hình 7-6. Mô hình máy tính trừu tượng của bộ chuyển MDA. Một số CUP như Alpha chẳng hạn thì có sơ đồ thanh ghi phần cứng thực tế. Một trong các bước khởi đầu cho bộ chuyển DMA dự trữ một số thanh ghi cho quá trình sử dụng của bạn- tôi sẽ thảo luận vấn đề này trong phần sơ đồ chuyển. Một số loại CPU khác, như x86 chẳng hạn thì không có sơ đồ thanh ghi, bạn viết trình điều khiển của bạn tương tự như họ làm. Sơ đồ chuyển thực hiện từng bước có thể dảo ngược từ dưới lên của các vùng đệm bộ nhớ vật lý, cái mà phụ thuộc vào hệ thống. Trong một số trường hợp thì hoạt động của DMA sẽ được xử lý sử dụng vùng đệm đảo ngược. Rõ rang là một số người đã copy dữ liệu đến hoặc từ vùng đệm DMA trước hoặc sau khi dịch. Trong trường hợp cụ thể, ví dụ như là khi chúng ta lien hệ một đường bus của thiết bị chủ, cái mà là noi tụ họp của các cáp- các giai đoạn của sơ đồ chuyển có thể không làm gì trong cấu trúc mà không có sơ đồ thanh ghi. 259/369 Nhân của Windows XP sử dụng một cấu trúc dữ liệu được biết đến như là đối tượng điều hợp để mô tả các đặc tính DMA của thiết bị và để điều khiển truy cập đến các nguồn được chia sẻ, như là hệ thống các kênh DMA và sơ đồ các thanh ghi. Bạn sẽ lấy một con trỏ trỏ tới đối tượng điều hợp bởi lời gọi IOGetDmAAdapter trong suốt quá trình StartDevice của bạn. Đối tượng điều hợp sẽ có một con trỏ để trỏ tới một cấu trúc được gọi là DmaOperations cái mà khi nó bật thì chứa các con trỏ để trỏ tới các hàm mà bạn muốn gọi. Hãy xem bảng 7.4. Các hàm này chỉ ra vị trí đích của hàm (ví dụ như ..) cái mà bạn phải sử dụng phiên bản trước của Windown NT. Thực tế, tên đích này có trong các Macro các mà được khai báo trong hàm DmaOperations. Table 7-4. DmaOperations Function Pointers for DMA Helper Routines DmaOperations Function Pointer Description PutDmaAdapter Destroys adapter object AllocateCommonBuffer Allocates a common buffer FreeCommonBuffer Releases a common buffer AllocateAdapterChannel Reserves adapter and mapregisters FlushAdapterBuffers Flushes intermediate data buffersafter transfer FreeAdapterChannel Releases adapter object and mapregisters FreeMapRegisters Releases map registers only MapTransfer Programs one stage of a transfer GetDmaAlignment Gets address alignment requiredfor adapter ReadDmaCounter Determines residual count GetScatterGatherList Reserves adapter and constructsscatter/gather list PutScatterGatherList Releases scatter/gather list Chiến lược chuyển đổi (Transfer Strategies): Cách bạn thựchiện chuyển đổi DMa phụ thuộc vào nmột số nhân tố sau: 260/369 1. Nếu thiết bị của bạn có Bus-Mastering capability, tất nhiên là nó cần có điện để truy cập vào bộ nhớ chính nếu như bạn yêu cầu nó một số chức năng có bản. như là nơi bắt đầu, bao nhiêu đơn vị dữ liệu được chuyển, bạn đang thực hiện việc vào hay ra dữ liệu và nhiều điều khác. Bạn sẽ phải hội ý với những người thiết kế ra phần cứng của bạn để lọc ra được các chi tiết này hoặc là bạn sẽ phải làm việc với bảng hướng dẫn để biết được bạn cần phải làm gì với các mức phần cứng này. 2. Một thiết bị với khả năng tập hợp/trải ra có thể chuyển các khối lớn dữ liệu đến hoặc đi các vùng không cấu hình của bộ nhớ vật lý. Sử dụng scatter/gather là một lợi thể của phần mềmbởi vì nó giới hạn yêu cầu về với các khối dữ liệu lớn của các trang Frame cấu hình. CÁc trang này có thể đơn giản là bị khoá khi mà chúng được tìm thấy trong bộ nhớ vật lý và thiết bị có thể bị mô tả bởi chúng. 3. Nếu thiết bị của bạn không có Bus chủ, bạn sẽ sử dụng hệ thống điều khiển DMa trên bo mạch chủ của máy tính. Kiểu của DMA đó đôi khi được gọi là DMA nô lệ (Slave). Hệ thống điều khiển DMA lien kết với các bus ISA có một số giới hạn về bộ nhớ nào nó được truy cập và độ rộng của một bộ chuyển nó có thể thực hiện mà không có chương trình định trước. Trình điều khiển này cí dụ như là IESA thiếu các giới hạn này. Ít nhất là trong Windows XP, bạn sẽ không cần phải biết kiểu bus phần cứng cảu bạn cắm vào bởi vì hệ thống có thể lấy ra các hạn chế này một cách tự động. 4. Thông thường, hệ thống DMA bao gồm chương trình sơ đồ các thanh ghi phần cứng hoặc bản copy dữ liệu trước hay sau của hệ thống. nếu thiết bịcủa bạn cần đọc hay ghi dữ liệu lien tục, bạn không cần phải thực hiện các bước này với mỗi yêu cầu vào.ra, nó có thể làm chậm đi quá trình được chấp nhận trong trường hợp cụ thể rất nhiều. Vì vậy bạn có thể chỉ định cái nào được biết như là vùng đệm chung, nơi mà các thiết bị và các trình điều khiển của bạn có thể đông thời truy cập tại nhiều thời điểm. Tuy nhiên trong thực tế nhiều chi tiết này sẽ bị phụ thuộc khác nhau vào cách mà 4 tác nhân này ảnh hưởng lẫn nhau, các bước mà bạn thực hiện sẽ có những đặc tính chung. Hình 7.7 đã minh hoạc qua tổ chức của một chuyển đổi. Bạn bắt đầu chuyển đổi từ công việc StartIo bằng cách yêu cầu quyền sở hữu của chính đối tượng điều hợp.Quyền sở hữu này chỉ có giá trị khi bạn chia sẻ một kênh DMA hệ thống với một thiết bị khác, nhưng mà mô hình DMA của Windows XP yêu cầu bạn cần phải thực hiện các bước này. Khi trình quản lý vào/ra có thể cung cấp cho bạn quyền này, nó sẽ chỉ định cho bạn một số sơ đồ các thanh ghi cho quá trình sử dụng tạm thời cảu bạnvà gọi lại hành động điều khiển bộ điều hợp bạn cung cấp. TRong hành động điều khiển bộ điều hợp của bạn, bạn thực hiện một sơ đồ chuyển đổi từng bước đế sắp xếp phạm vi chuyển đổi đầu tiên (cũng có thể là chỉ có một). Một số phạm vi có thể cần thiết nếu khả năng sơ đồ các thanh ghi là không thể. Thiết bị của bạn phải có thể cản trở và điều khiển những điều có thể xáy ra giữa các phạm vi. 261/369 Figure 7-7. Flow of ownership during DMA. Một thủ tục điều khiển bộ điều hợp của bạn có thể khởi tạo sơ đồ các thanh ghi cho phạm vi đầu tiên, bạn báo hiệu cho thiết bị của mình băt đầu hoạt động. Thiết bị của bạn sẽ thúc đẩy một ngắt khi mà quá trình chuyển đổi ban đầu được hoàn tất. VÀ sau đó thì bạn sẽ liệt kê được một DPC. Thủ tục của DPC sẽ khởi đầu một phạm vi chuyển đổi khácnếu cần thiết hoặc nếu không thì nó sẽ hoàn thành yêu cầu. Đôi khi theo cách này, bạn sẽ giải phóng sơ đồ các thanh ghi và đối tượng điều hợp. Sự tính toán thời gian của hai sự kiện này là một trong số các chi tiết khác cái mà phục thuộc vào các tác nhân tôi đã nêu ra ở đầu của phần này. Thực hiện chuyển đổi DM (Performing DMA Transfers): Bây giờ tôi sẽ đi vào chi tiết về những cái máy cơ học cái mà vẫn được gọi là một gói cơ sở DMA chuyển đổi, ở khía cạnh nào đó bạn chuyển đổi một số dữ liệu riêng biệt bằng cách sử dụng vùng đệm dữ liệu cái mà đi cùng với gói yêu cầu vào/ra. Hãy bắt đầu một cách đơn giản và giả sử rằng bạn đối diện với một trường hợp rất chung ngày nay: Thiết bị của bạn là một bus PCI master nhưng không có khả năng phân giải/ tụ tập Khi bạn tạo ra đối tượng thiết bị của mình, để bắt đầu bạn thong thường sẽ biểu thị điều bạn muốn để sử dụng phương thức truy cập trực tiếp vùng đệm dữ liệu bằng việc thiết lập cờ DO_DIRECT_IO. Bạn chọn phương thức trực tiếp bởi vì bạn thậm chí sẽ phải thong qua địa chỉ của một kí hiệu bộ nhớ lập danh sách khi một trong số các tham số hàm MapTransfẻ bạn sẽ gọi. Sự lựa chọn này sẽ đưa ra một số vấn đề đáng quan tâm về sự sắp hang của vùng đệm. Trừ phi mà ứng dụng sử dụng cờ FILE_FLAG_NO_BUFFERING trong lời gọi của nó tới hàm CreatFile, trình quản lý 262/369 vào ra sẽ không bắt buộc yêu cầu xếp hang của đối tượng thiết bị trên các vùng đệm dữ liệu ở chế độ người dùng (nó không bắt buộc các yêu cầu cho nhân ở chế độ lời gọi trong ). Nếu Hal hoặc thiết bị của bạn yêu cầu các vùng đệm DMA để bắt đầu trong một ranh giới cụ thể, vì thế bạn có thể copy từ duới lên một phần nhỏ của dữ liệu người dùng tới hang chính xác bên trong vùng đệm để có được một hang đợi yêu cầu- hoặc là điều đó hoặc nguyên nhân để làm sai và yêu cầu điều đó có một vùng đệm không sắp hàng. TRong hàm StartDevice, bạn tạo một đối tượng điềuhợp bằng cách sử dụng đoạn code như sau: DEVICE_DESCRIPTION dd; RtlZeroMemory(&dd, sizeof(dd)); dd.Version = DEVICE_DESCRIPTION_VERSION; dd.Master = TRUE; dd.InterfaceType = InterfaceTypeUndefined; dd.MaximumLength = MAXTRANSFER; dd.Dma32BitAddresses = TRUE; pdx->AdapterObject = IoGetDmaAdapter(pdx->Pdo, &dd, &pdx->nMapRegisters); Câu lệnh cuối cùng trong đoạn code này là quan trọng bậc nhất. IoGetDmaAdapter sẽ giao tiếp với bus điều khiển hoặc Hal để tạo ra một đối tượng điều hợp, cái mà địa chỉ của nó được trả về cho bạn. Tham số đầu tiên (pdx→Pdo) định nghĩa đối tượng thiết bị vật lý cho đối tượng của bạn. Tham số thứ hai chỉ tới cấu trúc DEVICE_DESCRIPTION cái mà bạn khởi tạo để mô tả một DMA tiêu biếu cho thiết bị của bạn. Tham số cuối cùng chỉ ra nơi mà hệ thống nên lưu giữ số lượng lớn nhất các số của sơ đồ các thanh ghi mà bạn sẽ được phép cố gắng lưu trữ để dự trữ trong suốt quá trình chuyển đơn. Bạn sẽ cần phải lưu ý rằng tôi đã lưu trữ hai trường trong thiết bị mở rộng để nhận lấy hai dữ liệu ra từ hàm này. Để khởi đầu cho hoạt động vào/ra, thủ tục StartIo đầu tiên cần phải dự trữ một đối tượng bằng lời gọi thủ tục AllocateAdapterChannel của đối tượng. Một trong các tham số truyền tới hàm AllocateAdapterChannel là địa chỉ của một thủ tục điều khiển điều hợp, cái mà trình quản lý vào ra sẽ gọi khi sự lưu trữ xong xuôi. Đây là đoạn code mẫu mà bạn sử dụng để chuẩn bị và thực hiện lời gọi tới AllocateAdapterChannel: 263/369 typedef struct _DEVICE_EXTENSION { PADAPTER_OBJECT AdapterObject; // device's adapter object ULONG nMapRegisters; // max # map registers ULONG nMapRegistersAllocated; // # allocated for this xfer ULONG numxfer; // # bytes transferred so far ULONG xfer; // # bytes to transfer during this stage ULONG nbytes; // # bytes remaining to transfer PVOID vaddr; // virtual address for current stage PVOID regbase; // map register base for this stage } DEVICE_EXTENSION, *PDEVICE_EXTENSION; VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PMDL mdl = Irp->MdlAddress; pdx->numxfer = 0; pdx->xfer = pdx->nbytes = MmGetMdlByteCount(mdl); 264/369 pdx->vaddr = MmGetMdlVirtualAddress(mdl); ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr, pdx->nbytes); if (nregs > pdx->nMapRegisters) { nregs = pdx->nMapRegisters; pdx->xfer = nregs * PAGE_SIZE - MmGetMdlByteOffset(mdl); } pdx->nMapRegistersAllocated = nregs; NTSTATUS status = (*pdx->AdapterObject->DmaOperations ->AllocateAdapterChannel)(pdx->AdapterObject, fdo, nregs, (PDRIVER_CONTROL) AdapterControl, pdx); if (!NT_SUCCESS(status)) { CompleteRequest(Irp, status, 0); StartNextPacket(&pdx->dqReadWrite, fdo); } } 1. thiết bị mở rộng của bạn cần phải lưu trữ một số trường có lien quan đến các chuyển đổi DMA. Phần chú thích chỉ ra các trường hợp cho các trường này. 2. Đây là một số câu lệnh khởi tạo cho các trường trong thiết bị mở rộng cho phạm vi đầu tiên của sự chuyển đổi. 265/369 3. Ở đây chúng ta tính toán số lượng các sơ đồ các thanh ghi chúng ta yêu cầu hệ thống lưu trữ cho chúng ta trong suốt quá trình chuyển đổi này. Chúng ta bắt đầu bằng việc tính toán số lượng được yêu cầu cho toàn bộ sự chuyển đổi này. Macro ADDRESS_AND_SIZE_TO_SPAN_PAGES có thể đưa vào một bản kê khai vùng đệm có thể kéo dài qua ranh giới của một trang. Tuy nhiên con số mà chúng ta đi ngược từ dưới lên với khả năng có thể vượt qua số lớn nhất mà chúng ta được cho phépbởi lời gọi thong thường tới hàm IoGetDmAAdapter. TRong trường hợp này chúng ta cần thực hiện một chuyển đổi trong nhiều phạm vi. Do vậy chúng ta đảo ngược tỉlệ của trang đầu tiên để chỉ sử dụng con số chấp nhận được của sơ đồ các thanh ghi. Chúng ta cũng cấn phải nhớ có bao nhiêu sơ đồ các thanh ghi mà chúng ta đang cho phép để chúng ta có thể huỷ chính xác con số này về sau. 4. TRong lời gọi tới hàm AllocateAdapterChannel, chúng ta chỉ ra địa chỉ của đ ối tượng Adapter, địa chỉ của đối tượng thiết bị của chúng ta, số lượng đã được tính toán của sơ đồ các thanh ghi, và địa chỉ của thủ tục điều khiển điều hợ của chúng ta. Tham số cuối cùng pdx là tham số ngữ cảnh cho thủ tục điều khiển điều hợp của chúng ta. Thông thường thì một vài thiết bị có thể chia sẻ chung một đối tượng điều hợp đơn, đối tượng điều hợp đang chia sẻ trong cuộc sống thực tế chỉ khi bạn tin tưởng vào hệ thống điều khiển DMA. Các thiết bị Bus-Master tận dụng các đối tượng điều hợp thuộc quyền sở hữu.Nhưng bởi vì bạn không cần phải biết về hệ thống các thiết bị khi bạn tạo ra các đối tượng điều hợp, nên bạn cũng không cần phải đưa ra bất cứ chứng minhnào vềnó. Khi đó thong thường đối tượng điều hợp của chúng ta sẽ rất bận rộn khi bạn gọi tới AllocateAdapterChannel, và yêu cầu của bạn vì thế có thể bị đặt vào trong một hang đợi cho tới khi đối tượng điềuhợp trở nên có thể xử lý (rảnh rỗi). Tất cả khoảng trễ này xảy ra bên trong của AllocateAdapterChannel cái mà sẽ gọi thủ tục điều khiển điều hợp của bạn khi đối tượng điều hợp và tất cả sơ đồ các thanh ghi mà bạn yêu cầu rảng rỗi. Nhiệm vụ tiếp theo của bạn là thực hiện những gì mà thiết bị phụ thuộc của bạn yêu cầu để nói cho thiết bị của bạn biết về địa chỉ vật lý để bắt đầu hoạt động trong phần cứng của bạn. IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo, PIRP junk, PVOID regbase, PDEVICE_EXTENSION pdx) { PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite); 266/369 PMDL mdl = Irp->MdlAddress; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ; pdx->regbase = regbase; KeFlushIoBuffers(mdl, isread, TRUE); PHYSICAL_ADDRESS address = (*pdx->AdapterObject->DmaOperations->MapTransfer) (pdx->AdapterObject, mdl, regbase, pdx->vaddr, pdx->xfer, !isread); return DeallocateObjectKeepRegisters; } 1. Tham số thứ 2 cái mà tôi đặt tên là junk to AdapterControl is whatever(bất cứ khi nào) was in the CurrentIrp field of the device object khi mà bạn gọi hàm AllocateAdapterChannel. Khi bạn sử dụng DEVQUEUE cho hang đợi IRP, bạn cần phải yêu cầu đối tượng DEVQUEUE cái mà là IRP hiện tại. Nếu bạn sử dụng hang đợi các thủ tục của Microsoft IoStartPacker & IoStartNextPacket để quảnlý hang đợi của bạn thì junk sẽ đúng là một IRP. Trong trường hợp này tôi sẽ gọi tên Irp để thay thế. 267/369 2. Có một số điều khác biệt giữa đoạn code điều khiển hoạt động vào và ra sử dụng DMA, vì thế nó rất thuận tiện cho việc điều khiển cả hai hoạ

Các file đính kèm theo tài liệu này:

  • pdfbai_giang_lap_trinh_he_thong.pdf
Tài liệu liên quan