├── DIRS ├── INSTALL.BAT ├── INSTALLC.BAT ├── README.md ├── RFC3347.TXT ├── RFC3720.pdf ├── RFC3783.TXT ├── RFC5048.TXT ├── uSCSI ├── IoCtl.c ├── MAKEFILE ├── Pnp.c ├── Public.h ├── ReadWrite.c ├── SOURCES ├── USCSI.INF ├── Utils.c ├── precomp.h ├── srb.c ├── uSCSI.c └── uSCSI.h ├── uSCSIPort ├── Cmd.c ├── Data.c ├── Discover.c ├── Handler.c ├── Key.c ├── Login.c ├── Nop.c ├── ProtUtils.c ├── Protocol.c ├── Protocol.h ├── R2T.c ├── Reject.c ├── Response.c ├── SNACK.c ├── SOURCES ├── Task.c ├── Text.c ├── TransUtils.c ├── Transport.c ├── Transport.h ├── precomp.h ├── uSCSI.c ├── uSCSI.h ├── uSCSIPort.def ├── uSCSIPort.h └── uSCSIPortPublic.h └── uSCSI设计文档.pdf /DIRS: -------------------------------------------------------------------------------- 1 | DIRS=uSCSIPort uSCSI -------------------------------------------------------------------------------- /INSTALL.BAT: -------------------------------------------------------------------------------- 1 | cd C:\src\DrvDev\uSCSI\objfre_wxp_x86\i386 2 | copy C:\src\DrvDev\uSCSI\uscsi.inf . 3 | devcon install uscsi.inf uSCSI\uSCSI 4 | cd C:\src\DrvDev\uSCSI -------------------------------------------------------------------------------- /INSTALLC.BAT: -------------------------------------------------------------------------------- 1 | cd C:\src\DrvDev\uSCSI\objchk_wxp_x86\i386 2 | copy C:\src\DrvDev\uSCSI\uscsi.inf . 3 | devcon install uscsi.inf uSCSI\uSCSI 4 | cd C:\src\DrvDev\uSCSI -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iSCSI_drv 2 | an iSCSI demo driver for Windows XP 3 | 4 | 1. components 5 | ------------- 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
componentdescription
uSCSIPort.sysiSCSI protocol lib
uSCSI.sysdisk driver
uscsi.infinf file
devcondriver tools
uscsictlcommand line tools , use to add target , connect initiator to target etc.
14 | 15 | 2. installation 16 | ------------- 17 | 1. copy uSCSIPort.sys to system32/drivers 18 | 1. install disk driver 19 | 20 | **devcon install uscsi.inf uSCSI\uSCSI** 21 | 22 | 1. setup StarWind with target name iqn.com.yushang:vdisk.01 23 | 1. add target 24 | 25 | **uscsictl -t iqn.com.yushang:vdisk.01 192.168.1.123** 26 | 27 | 1. connect to target 28 | 29 | **uscsictl -c iqn.com.yushang:vdisk.01** 30 | 31 | 1. initialize disk use Windows disk management MMC 32 | 33 | 3. performance metrics 34 | ---------------------- 35 | on MS VPC , test with HD Tune 36 | * READ: 37 | * min 2.8 m/s 38 | * avg 15.2 m/s 39 | * max 20.1 m/s 40 | * access time 11.1 ms 41 | * burst rate 19.2 m/s 42 | * CPU usage 14.3% 43 | 44 | 4. reference 45 | ------------ 46 | please refer uSCSI设计文档.pdf(Chinese) for details 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /RFC3720.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldoldman/iSCSI_drv/e118622fa43c4756038bc72a2ef4492f807a10f8/RFC3720.pdf -------------------------------------------------------------------------------- /RFC3783.TXT: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group M. Chadalapaka 8 | Request for Comments: 3783 R. Elliott 9 | Category: Informational Hewlett-Packard Co. 10 | May 2004 11 | 12 | 13 | Small Computer Systems Interface (SCSI) 14 | Command Ordering Considerations with iSCSI 15 | 16 | Status of this Memo 17 | 18 | This memo provides information for the Internet community. It does 19 | not specify an Internet standard of any kind. Distribution of this 20 | memo is unlimited. 21 | 22 | Copyright Notice 23 | 24 | Copyright (C) The Internet Society (2004). All Rights Reserved. 25 | 26 | Abstract 27 | 28 | Internet Small Computer Systems Interface (iSCSI) is a Small Computer 29 | Systems Interface (SCSI) transport protocol designed to run on top of 30 | TCP. The iSCSI session abstraction is equivalent to the classic SCSI 31 | "I_T nexus", which represents the logical relationship between an 32 | Initiator and a Target (I and T) required in order to communicate via 33 | the SCSI family of protocols. The iSCSI session provides an ordered 34 | command delivery from the SCSI initiator to the SCSI target. This 35 | document goes into the design considerations that led to the iSCSI 36 | session model as it is defined today, relates the SCSI command 37 | ordering features defined in T10 specifications to the iSCSI 38 | concepts, and finally provides guidance to system designers on how 39 | true command ordering solutions can be built based on iSCSI. 40 | 41 | Table of Contents 42 | 43 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 44 | 2. Definitions and Acronyms . . . . . . . . . . . . . . . . . . . 3 45 | 2.1. Definitions. . . . . . . . . . . . . . . . . . . . . . . 3 46 | 2.2. Acronyms . . . . . . . . . . . . . . . . . . . . . . . . 4 47 | 3. Overview of the iSCSI Protocol . . . . . . . . . . . . . . . . 4 48 | 3.1. Protocol Mapping Description . . . . . . . . . . . . . . 4 49 | 3.2. The I_T Nexus Model. . . . . . . . . . . . . . . . . . . 5 50 | 3.3. Ordered Command Delivery . . . . . . . . . . . . . . . . 6 51 | 3.3.1. Questions. . . . . . . . . . . . . . . . . . . . 6 52 | 3.3.2. The Session Guarantee. . . . . . . . . . . . . . 6 53 | 3.3.3. Ordering Onus. . . . . . . . . . . . . . . . . . 7 54 | 3.3.4. Design Intent. . . . . . . . . . . . . . . . . . 7 55 | 56 | 57 | 58 | Chadalapaka & Elliott Informational [Page 1] 59 | 60 | RFC 3783 Command Ordering May 2004 61 | 62 | 63 | 4. The Command Ordering Scenario. . . . . . . . . . . . . . . . . 8 64 | 4.1. SCSI Layer . . . . . . . . . . . . . . . . . . . . . . . 8 65 | 4.1.1. Command Reference Number (CRN) . . . . . . . . . 8 66 | 4.1.2. Task Attributes. . . . . . . . . . . . . . . . . 8 67 | 4.1.3. Auto Contingent Allegiance (ACA) . . . . . . . . 8 68 | 4.1.4. UA Interlock . . . . . . . . . . . . . . . . . . 9 69 | 4.2. iSCSI Layer. . . . . . . . . . . . . . . . . . . . . . . 9 70 | 5. Connection Failure Considerations. . . . . . . . . . . . . . . 9 71 | 6. Command Ordering System Considerations . . . . . . . . . . . . 10 72 | 7. Reservation Considerations . . . . . . . . . . . . . . . . . . 11 73 | 8. Security Considerations. . . . . . . . . . . . . . . . . . . . 12 74 | 9. References and Bibliography. . . . . . . . . . . . . . . . . . 12 75 | 9.1. Normative References.. . . . . . . . . . . . . . . . . . 12 76 | 9.2. Informative References . . . . . . . . . . . . . . . . . 12 77 | 10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 12 78 | 11. Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . 13 79 | 12. Full Copyright Statement . . . . . . . . . . . . . . . . . . . 14 80 | 81 | 1. Introduction 82 | 83 | iSCSI is a SCSI transport protocol ([iSCSI]) designed to enable 84 | running SCSI application protocols on TCP/IP networks, including 85 | potentially the Internet. Given the size and scope of the Internet, 86 | iSCSI thus enables some exciting new SCSI applications. Potential 87 | new application areas for exploiting iSCSI's value include the 88 | following: 89 | 90 | a) Larger (diameter) Storage Area Networks (SANs) than had been 91 | possible until now 92 | b) Asynchronous remote mirroring 93 | c) Remote tape vaulting 94 | 95 | Each of these applications takes advantage of the practically 96 | unlimited geographical distance that iSCSI enables between a SCSI 97 | initiator and a SCSI target. In each of these cases, because of the 98 | long delays involved, there is a very high incentive for the 99 | initiator to stream SCSI commands back-to-back without waiting for 100 | the SCSI status of previous commands. Command streaming may be 101 | employed primarily by two classes of applications - while one class 102 | may not particularly care about ordered command execution, the other 103 | class does rely on ordered command execution (i.e. there is an 104 | application-level dependency on the ordering among SCSI commands). 105 | As an example, cases b) and c) listed earlier clearly require ordered 106 | command execution. A mirroring application does not want the writes 107 | to be committed out of order on the remote SCSI target, so as to 108 | 109 | 110 | 111 | 112 | 113 | 114 | Chadalapaka & Elliott Informational [Page 2] 115 | 116 | RFC 3783 Command Ordering May 2004 117 | 118 | 119 | preserve the transactional integrity of the data on that target. To 120 | summarize, SCSI command streaming, when coupled with the guarantee of 121 | ordered command execution on the SCSI target, is extremely valuable 122 | for a critical class of applications in long-latency networks. 123 | 124 | This document reviews the various protocol considerations in 125 | designing storage solutions that employ SCSI command ordering. This 126 | document also analyzes and explains the design intent of [iSCSI] with 127 | respect to command ordering. 128 | 129 | 2. Definitions and Acronyms 130 | 131 | 2.1. Definitions 132 | 133 | - I_T nexus: [SAM2] defines the I_T nexus as a relationship between 134 | a SCSI initiator port and a SCSI target port. [iSCSI] defines an 135 | iSCSI session as the iSCSI representation of an I_T nexus. In the 136 | iSCSI context, the I_T nexus (i.e. the iSCSI session) is a 137 | relationship between an iSCSI initiator's end of the session (SCSI 138 | Initiator Port) and the iSCSI target's Portal Group (SCSI Target 139 | Port). 140 | 141 | - PDU (Protocol Data Unit): An iSCSI initiator and iSCSI target 142 | communicate using iSCSI protocol messages. These messages are 143 | called "iSCSI protocol data units" (iSCSI PDUs). 144 | 145 | - SCSI device: A SCSI device is an entity that contains one or more 146 | SCSI ports that are connected to a service delivery subsystem and 147 | supports SCSI application protocols. In the iSCSI context, the 148 | SCSI Device is the component within an iSCSI Node that provides 149 | the SCSI functionality. The SCSI Device Name is defined to be the 150 | iSCSI Name of the node. 151 | 152 | - Session: A group of logically related iSCSI connections that link 153 | an initiator with a target form a session (equivalent to a SCSI 154 | I-T nexus). The number of participating iSCSI connections within 155 | an iSCSI session may vary over time. The multiplicity of 156 | connections at the iSCSI level is completely hidden for the SCSI 157 | layer - each SCSI port in an I_T nexus sees only one peer SCSI 158 | port across all the connections of a session. 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | Chadalapaka & Elliott Informational [Page 3] 171 | 172 | RFC 3783 Command Ordering May 2004 173 | 174 | 175 | 2.2. Acronyms 176 | 177 | Acronym Definition 178 | -------------------------------------------------------------- 179 | ACA Auto Contingent Allegiance 180 | ASC Additional Sense Code 181 | ASCQ Additional Sense Code Qualifier 182 | CRN Command Reference Number 183 | IETF Internet Engineering Task Force 184 | ISID Initiator Session Identifier 185 | ITT Initiator Task Tag 186 | LU Logical Unit 187 | LUN Logical Unit Number 188 | NIC Network Interface Card 189 | PDU Protocol Data Unit 190 | TMF Task Management Function 191 | TSIH Target Session Identifying Handle 192 | SAN Storage Area Network 193 | SCSI Small Computer Systems Interface 194 | TCP Transmission Control Protocol 195 | UA Unit Attention 196 | WG Working Group 197 | 198 | 3. Overview of the iSCSI Protocol 199 | 200 | 3.1. Protocol Mapping Description 201 | 202 | The iSCSI protocol is a mapping of the SCSI remote procedure 203 | invocation model (see [SAM2]) over the TCP protocol. 204 | 205 | SCSI's notion of a task maps to an iSCSI task. Each iSCSI task is 206 | uniquely identified within that I_T nexus by a 32-bit unique 207 | identifier called Initiator Task Tag (ITT). The ITT is both an iSCSI 208 | identifier of the task and a classic SCSI task tag. 209 | 210 | SCSI commands from the initiator to the target are carried in iSCSI 211 | requests called SCSI Command PDUs. SCSI status back to the initiator 212 | is carried in iSCSI responses called SCSI Response PDUs. SCSI Data- 213 | out from the initiator to the target is carried in SCSI Data-Out 214 | PDUs, and the SCSI Data-in back to the initiator is carried in SCSI 215 | Data-in PDUs. 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | Chadalapaka & Elliott Informational [Page 4] 227 | 228 | RFC 3783 Command Ordering May 2004 229 | 230 | 231 | 3.2. The I_T Nexus Model 232 | 233 | In the iSCSI model, the SCSI I_T nexus maps directly to the iSCSI 234 | session, which is an iSCSI protocol abstraction spanning one or more 235 | TCP connections. The iSCSI protocol defines the semantics in order 236 | to realize one logical flow of bidirectional communication on the I_T 237 | nexus, potentially spanning multiple TCP connections (as many as 238 | 2^16). The multiplicity of iSCSI connections is thus completely 239 | contained at the iSCSI layer, while the SCSI layer is presented with 240 | a single I_T nexus, even in a multi-connection session. A session 241 | between a pair of given iSCSI nodes is identified by the session 242 | identifier (SSID) and each connection within a given session is 243 | uniquely identified by a connection identifier (CID) in iSCSI. The 244 | SSID itself has two components - Initiator Session Identifier (ISID) 245 | and a Target Session Identifying Handler (TSIH) - each identifying 246 | one end of the same session. 247 | 248 | There are four crucial functional facets of iSCSI that together 249 | present this single logical flow abstraction to the SCSI layer, even 250 | with an iSCSI session spanning across multiple iSCSI connections. 251 | 252 | a) Ordered command delivery: A sequence of SCSI commands that is 253 | striped across all the connections in the session is 254 | "reordered" by the target iSCSI layer into an identical 255 | sequence based on a Command Sequence Number (CmdSN) that is 256 | unique across the session. The goal is to achieve bandwidth 257 | aggregation from multiple TCP connections, but to still make it 258 | appear to the target SCSI layer as if all the commands had 259 | travelled in one flow. 260 | 261 | b) Connection allegiance: All the PDU exchanges for a SCSI 262 | Command, up to and including the SCSI Response PDU for the 263 | Command, are required to flow on the same iSCSI connection at 264 | any given time. This again is intended to hide the multi- 265 | connection nature of a session because the SCSI layer on either 266 | side will never see the PDU contents out of order (e.g., status 267 | cannot bypass read data for an initiator). 268 | 269 | c) Task set management function handling: [iSCSI] specifies an 270 | ordered sequence of steps for the iSCSI layer on the SCSI 271 | target in handling the two SCSI task management functions 272 | (TMFs) that manage SCSI task sets. The two TMFs are ABORT TASK 273 | SET that aborts all active tasks in a session, and CLEAR TASK 274 | SET that clears the tasks in the task set. The goal of the 275 | sequence of steps is to guarantee that the initiator receives 276 | the SCSI Response PDUs of all unaffected tasks before the TMF 277 | Response itself arrives, regardless of the number of 278 | connections in the iSCSI session. This operational model is 279 | 280 | 281 | 282 | Chadalapaka & Elliott Informational [Page 5] 283 | 284 | RFC 3783 Command Ordering May 2004 285 | 286 | 287 | again intended to preserve the single flow abstraction to the 288 | SCSI layer. 289 | 290 | d) Immediate task management function handling: Even when a TMF 291 | request is marked as "immediate" (i.e. only has a position in 292 | the command stream, but does not consume a CmdSN), [iSCSI] 293 | defines semantics that require the target iSCSI layer to ensure 294 | that the TMF request is executed as if the commands and the TMF 295 | request were all flowing on a single logical channel. This 296 | ensures that the TMF request will act on tasks that it was 297 | meant to manage. 298 | 299 | The following sections will analyze the "Ordered command delivery" 300 | aspect in more detail, since command ordering is the focus of this 301 | document. 302 | 303 | 3.3. Ordered Command Delivery 304 | 305 | 3.3.1. Questions 306 | 307 | A couple of important questions related to iSCSI command ordering 308 | were considered early on in the design of the iSCSI protocol. The 309 | questions were: 310 | 311 | a) What should be the command ordering behavior required of iSCSI 312 | implementations in the presence of transport errors, such as 313 | errors that corrupt the data in a fashion that is not detected 314 | by the TCP checksum (e.g., two offsetting bit flips in the same 315 | bit position), but is detected by the iSCSI CRC digest? 316 | 317 | b) Should [iSCSI] require both initiators and targets to use 318 | ordered command delivery? 319 | 320 | Since the answers to these questions are critical to the 321 | understanding of the ordering behavior required by the iSCSI 322 | protocol, the following sub-sections consider them in more detail. 323 | 324 | 3.3.2. The Session Guarantee 325 | 326 | The final disposition of question a) in section 3.3.1 was reflected 327 | in [RFC3347], "iSCSI MUST specify strictly ordered delivery of SCSI 328 | commands over an iSCSI session between an initiator/target pair, even 329 | in the presence of transport errors." Stated differently, an iSCSI 330 | digest failure, or an iSCSI connection termination, must not cause 331 | the iSCSI layer on a target to allow executing the commands in an 332 | order different from that intended (as indicated by the CmdSN order) 333 | by the initiator. This design choice is enormously helpful in 334 | building storage systems and solutions that can now always assume 335 | 336 | 337 | 338 | Chadalapaka & Elliott Informational [Page 6] 339 | 340 | RFC 3783 Command Ordering May 2004 341 | 342 | 343 | command ordering to be a service characteristic of an iSCSI 344 | substrate. 345 | 346 | Note that by taking the position that an iSCSI session always 347 | guarantees command ordering, [iSCSI] was indirectly implying that the 348 | principal reason for the multi-connection iSCSI session abstraction 349 | was to allow ordered bandwidth aggregation for an I_T nexus. In 350 | deployment models where this cross-connection ordering mandated by 351 | [iSCSI] is deemed expensive, a serious consideration should be given 352 | to deploying multiple single-connection sessions instead. 353 | 354 | 3.3.3. Ordering Onus 355 | 356 | The final resolution of b) in section 3.3.1 by the iSCSI protocol 357 | designers was in favor of not always requiring the initiators to use 358 | command ordering. This resolution is reflected in dropping the 359 | mandatory ACA usage requirement on the initiators, and allowing an 360 | ABORT TASK TMF to plug a command hole etc., since these are conscious 361 | choices an initiator makes in favor of not using ordered command 362 | delivery. The net result can be discerned by a careful reader of 363 | [iSCSI] - the onus of ensuring ordered command delivery is always on 364 | the iSCSI targets, while the initiators may or may not utilize 365 | command ordering. iSCSI targets, being the servers in the client- 366 | server model, do not really attempt to establish whether or not a 367 | client (initiator) intends to take advantage of command ordering 368 | service, but instead simply always provide the guaranteed delivery 369 | service. The rationale here is that there are inherent SCSI and 370 | application-level dependencies, as we shall see in building a command 371 | ordered solution, that are beyond the scope of [iSCSI], to mandate or 372 | even discern the intent with respect to the usage of command 373 | ordering. 374 | 375 | 3.3.4. Design Intent 376 | 377 | To summarize the design intent of [iSCSI]: 378 | 379 | The service delivery subsystem (see [SAM2]) abstraction provided by 380 | an iSCSI session is guaranteed to have the intrinsic property of 381 | ordered delivery of commands to the target SCSI layer under all 382 | conditions. Consequently, the guarantee of the ordered command 383 | delivery is across the entire I_T nexus spanning all the LUs that the 384 | nexus is authorized to access. It is the initiator's discretion as 385 | to whether or not this property will be used. 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | Chadalapaka & Elliott Informational [Page 7] 395 | 396 | RFC 3783 Command Ordering May 2004 397 | 398 | 399 | 4. The Command Ordering Scenario 400 | 401 | A storage systems designer working with SCSI and iSCSI has to 402 | consider the following protocol features in SCSI and iSCSI layers, 403 | each of which has a role to play in realizing the command ordering 404 | goal. 405 | 406 | 4.1. SCSI Layer 407 | 408 | The SCSI application layer has several tools to enforce ordering. 409 | 410 | 4.1.1. Command Reference Number (CRN) 411 | 412 | CRN is an ordered sequence number which, when enabled for a device 413 | server, increments by one for each I_T_L nexus (see [SAM2]). The one 414 | notable drawback with CRN is that there is no SCSI-generic way (such 415 | as through mode pages) to enable or disable the CRN feature. [SAM2] 416 | also leaves the usage semantics of CRN for the SCSI transport 417 | protocol, such as iSCSI, to specify. [iSCSI] chose not to support 418 | the CRN feature for various reasons. 419 | 420 | 4.1.2. Task Attributes 421 | 422 | [SAM2] defines the following four task attributes - SIMPLE, ORDERED, 423 | HEAD OF QUEUE, and ACA. Each task to an LU may be assigned an 424 | attribute. [SAM2] defines the ordering constraints that each of 425 | these attributes conveys to the device server that is servicing the 426 | task. In particular, judicious use of ORDERED and SIMPLE attributes 427 | applied to a stream of pipelined commands could convey the precise 428 | execution schema for the commands that the initiator issues, provided 429 | the commands are received in the same order on the target. 430 | 431 | 4.1.3. Auto Contingent Allegiance (ACA) 432 | 433 | ACA is an LU-level condition that is triggered when a command (with 434 | the NACA bit set to 1) completes with CHECK CONDITION. When ACA is 435 | triggered, it prevents all commands other than those with the ACA 436 | attribute from executing until the CLEAR ACA task management function 437 | is executed, while blocking all the other tasks already in the task 438 | set. See [SAM2] for the detailed semantics of ACA. Since ACA is 439 | closely tied to the notion of a task set, one would ideally have to 440 | select the scope of the task set (by setting the TST bit to 1 in the 441 | control mode page of the LU) to be per-initiator in order to prevent 442 | command failures in one I_T_L nexus from impacting other I_T_L 443 | nexuses through ACA. 444 | 445 | 446 | 447 | 448 | 449 | 450 | Chadalapaka & Elliott Informational [Page 8] 451 | 452 | RFC 3783 Command Ordering May 2004 453 | 454 | 455 | 4.1.4. UA Interlock 456 | 457 | When UA interlock is enabled, the logical unit does not clear any 458 | standard Unit Attention condition reported with autosense, and in 459 | addition, establishes a Unit Attention condition when a task is 460 | terminated with one of BUSY, TASK SET FULL, or RESERVATION CONFLICT 461 | statuses. This so-called "interlocked UA" is cleared only when the 462 | device server executes an explicit REQUEST SENSE ([SPC3]) command 463 | from the same initiator. From a functionality perspective, the scope 464 | of UA interlock today is slightly different from ACA's because it 465 | enforces ordering behavior for completion statuses other than CHECK 466 | CONDITION, but otherwise conceptually has the same design intent as 467 | ACA. On the other hand, ACA is somewhat more sophisticated because 468 | it allows special "cleanup" tasks (ones with ACA attribute) to 469 | execute when ACA is active. One of the principal reasons UA 470 | interlock came into being was that SCSI designers wanted a command 471 | ordering feature without the side effects of using the aforementioned 472 | TST bit in the control mode page. 473 | 474 | 4.2. iSCSI Layer 475 | 476 | As noted in section 3.2 and section 3.3, the iSCSI protocol enforces 477 | and guarantees ordered command delivery per iSCSI session using the 478 | CmdSN, and this is an attribute of the SCSI transport layer. Note 479 | further that any command ordering solution that seeks to realize 480 | ordering from the initiator SCSI layer to the target SCSI layer would 481 | be of practical value only when the command ordering is guaranteed by 482 | the SCSI transport layer. In other words, the related SCSI 483 | application layer protocol features such as ACA etc. are based on the 484 | premise of an ordered SCSI transport. Thus, iSCSI's command ordering 485 | is the last piece in completing the puzzle of building solutions that 486 | rely on ordered command execution, by providing the crucial guarantee 487 | that all the commands handed to the initiator iSCSI layer will be 488 | transported and handed to the target SCSI layer in the same order. 489 | 490 | 5. Connection Failure Considerations 491 | 492 | [iSCSI] mandates that when an iSCSI connection fails, the active 493 | tasks on that connection must be terminated if not recovered within a 494 | certain negotiated time limit. When an iSCSI target does terminate 495 | some subset of tasks due to iSCSI connection dynamics, there is a 496 | danger that the SCSI layer would simply move on to the next tasks 497 | waiting to be processed and execute them out-of-order unbeknownst to 498 | the initiator SCSI layer. To preclude this danger, [iSCSI] further 499 | mandates the following: 500 | 501 | 502 | 503 | 504 | 505 | 506 | Chadalapaka & Elliott Informational [Page 9] 507 | 508 | RFC 3783 Command Ordering May 2004 509 | 510 | 511 | a) The tasks terminated due to the connection failure must be 512 | internally terminated by the iSCSI target "as if" due to a 513 | CHECK CONDITION. While this particular completion status is 514 | never communicated back to the initiator, the "as if" is still 515 | meaningful and required because if the initiator were using ACA 516 | as the command ordering mechanism of choice, a SCSI-level ACA 517 | will be triggered due to this mandatory CHECK CONDITION. This 518 | addresses the aforementioned danger. 519 | 520 | b) After the tasks are terminated due to the connection failure, 521 | the iSCSI target must report a Unit Attention condition on the 522 | next command processed on any connection for each affected 523 | I_T_L nexus of that session. This is required because if the 524 | initiator were using UA interlock as the command ordering 525 | mechanism of choice, a SCSI-level UA will trigger a UA- 526 | interlock. This again addresses the aforementioned danger. 527 | iSCSI targets must report this UA with the status of CHECK 528 | CONDITION, and the ASC/ASCQ value of 47h/7Fh ("SOME COMMANDS 529 | CLEARED BY ISCSI PROTOCOL EVENT"). 530 | 531 | 6. Command Ordering System Considerations 532 | 533 | In general, command ordering is automatically enforced if targets and 534 | initiators comply with the iSCSI specification. However, listed 535 | below are certain additional related implementation considerations 536 | for the iSCSI initiators and targets to take note of. 537 | 538 | a) Even when all iSCSI and SCSI command ordering considerations 539 | earlier noted in this document were applied, it is beneficial 540 | for iSCSI initiators to proactively avoid scenarios that would 541 | otherwise lead to out-of-order command execution. This is 542 | simply because the SCSI command ordering features such as UA 543 | interlock are likely to be costlier in performance when they 544 | are allowed to be triggered. [iSCSI] provides enough guidance 545 | on how to implement this proactive detection of PDU ordering 546 | errors. 547 | 548 | b) The whole notion of command streaming does of course assume 549 | that the target in question supports command queueing. An 550 | iSCSI target desirous of supporting command ordering solutions 551 | should ensure that the SCSI layer on the target supports 552 | command queuing. The remote backup (tape vaulting) 553 | applications that iSCSI enables make an especially compelling 554 | case that tape devices should give a very serious consideration 555 | to supporting command queuing, at least when used in 556 | conjunction with iSCSI. 557 | 558 | 559 | 560 | 561 | 562 | Chadalapaka & Elliott Informational [Page 10] 563 | 564 | RFC 3783 Command Ordering May 2004 565 | 566 | 567 | c) An iSCSI target desirous of supporting high-performance command 568 | ordering solutions that involve specifying a description of 569 | execution schema should ensure that the SCSI layer on the 570 | target in fact does support the ORDERED and SIMPLE task 571 | attributes. 572 | 573 | d) There is some consideration of expanding the scope of UA 574 | interlock to encompass CHECK CONDITION status, and thus make it 575 | the only required command ordering functionality of 576 | implementations to build command ordering solutions. Until 577 | this is resolved in T10, the currently defined semantics of UA 578 | interlock and ACA warrant implementing both features by iSCSI 579 | targets desirous of supporting command ordering solutions. 580 | 581 | 7. Reservation Considerations 582 | 583 | [iSCSI] describes a "principle of conservative reuse" that encourages 584 | iSCSI initiators to reuse the same ISIDs (see section 3.2) to various 585 | SCSI target ports, in order to present the same SCSI initiator port 586 | name to those target ports. This is in fact a very crucial 587 | implementation consideration that must be complied with. [SPC3] 588 | mandates the SCSI targets to associate persistent reservations and 589 | the related registrations with the SCSI initiator port names whenever 590 | they are required by the SCSI transport protocol. Since [iSCSI] 591 | requires the mandatory SCSI initiator port names based on ISIDs, 592 | iSCSI targets are required to work off the SCSI initiator port names, 593 | and thus indirectly the ISIDs, in enforcing the persistent 594 | reservations. 595 | 596 | This fact has the following implications for the implementations: 597 | 598 | a) If a persistent reservation/registration is intended to be used 599 | across multiple SCSI ports of a SCSI device, the initiator 600 | iSCSI implementation must use the same ISID across associated 601 | iSCSI sessions connecting to different iSCSI target portal 602 | groups of the SCSI device. 603 | 604 | b) If a persistent reservation/registration is intended to be used 605 | across the power loss of a SCSI target, the initiator iSCSI 606 | implementation must use the same ISIDs as before in 607 | re-establishing the associated iSCSI sessions upon subsequent 608 | reboot in order to rely on the persist through power loss 609 | capability. 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | Chadalapaka & Elliott Informational [Page 11] 619 | 620 | RFC 3783 Command Ordering May 2004 621 | 622 | 623 | 8. Security Considerations 624 | 625 | For security considerations in using the iSCSI protocol, refer to the 626 | Security Considerations section in [iSCSI]. This document does not 627 | introduce any additional security considerations other than those 628 | already discussed in [iSCSI]. 629 | 630 | 9. References 631 | 632 | 9.1. Normative References 633 | 634 | [iSCSI] Satran, J., Meth, K., Sapuntzakis, C., Chadalapaka, M. and 635 | E. Zeidner, "Internet Small Computer Systems Inferface 636 | (iSCSI)", RFC 3720, May 2004. 637 | 638 | [SAM2] ANSI INCITS.366:2003 SCSI Architecture Model - 2 (SAM-2). 639 | 640 | 9.2. Informative References 641 | 642 | [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC 643 | 793, September 1981. 644 | 645 | [RFC2119] Bradner, S., "Key Words for use in RFCs to Indicate 646 | Requirement Levels", BCP 14, RFC 2119, March 1997. 647 | 648 | [RFC3347] Krueger, M. and R. Haagens, "iSCSI Requirements and Design 649 | Considerations", RFC 3347, July 2002. 650 | 651 | [SPC3] INCITS T10/1416-D, SCSI Primary Commands-3 (SPC-3). 652 | 653 | 10. Acknowledgments 654 | 655 | We are grateful to the IPS working group whose work defined the iSCSI 656 | protocol. Thanks also to David Black (EMC) who encouraged the 657 | publication of this document. Special thanks to Randy Haagens (HP) 658 | for his insights on the topic of command ordering. Thanks are also 659 | due to Elizabeth Rodriguez for carefully reviewing this document. 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | Chadalapaka & Elliott Informational [Page 12] 675 | 676 | RFC 3783 Command Ordering May 2004 677 | 678 | 679 | 11. Authors' Addresses 680 | 681 | Mallikarjun Chadalapaka 682 | Hewlett-Packard Company 683 | 8000 Foothills Blvd. 684 | Roseville, CA 95747-5668, USA 685 | 686 | Phone: +1.916.785.5621 687 | EMail: cbm@rose.hp.com 688 | 689 | 690 | Rob Elliott 691 | Hewlett-Packard Company 692 | MC140801 693 | PO Box 692000 694 | Houston, TX 77269-2000 USA 695 | 696 | Phone: +1.281.518.5037 697 | EMail: elliott@hp.com 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | Chadalapaka & Elliott Informational [Page 13] 731 | 732 | RFC 3783 Command Ordering May 2004 733 | 734 | 735 | 12. Full Copyright Statement 736 | 737 | Copyright (C) The Internet Society (2004). This document is subject 738 | to the rights, licenses and restrictions contained in BCP 78, and 739 | except as set forth therein, the authors retain all their rights. 740 | 741 | This document and the information contained herein are provided on an 742 | "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS 743 | OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET 744 | ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, 745 | INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE 746 | INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED 747 | WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 748 | 749 | Intellectual Property 750 | 751 | The IETF takes no position regarding the validity or scope of any 752 | Intellectual Property Rights or other rights that might be claimed to 753 | pertain to the implementation or use of the technology described in 754 | this document or the extent to which any license under such rights 755 | might or might not be available; nor does it represent that it has 756 | made any independent effort to identify any such rights. Information 757 | on the procedures with respect to rights in RFC documents can be 758 | found in BCP 78 and BCP 79. 759 | 760 | Copies of IPR disclosures made to the IETF Secretariat and any 761 | assurances of licenses to be made available, or the result of an 762 | attempt made to obtain a general license or permission for the use of 763 | such proprietary rights by implementers or users of this 764 | specification can be obtained from the IETF on-line IPR repository at 765 | http://www.ietf.org/ipr. 766 | 767 | The IETF invites any interested party to bring to its attention any 768 | copyrights, patents or patent applications, or other proprietary 769 | rights that may cover technology that may be required to implement 770 | this standard. Please address the information to the IETF at ietf- 771 | ipr@ietf.org. 772 | 773 | Acknowledgement 774 | 775 | Funding for the RFC Editor function is currently provided by the 776 | Internet Society. 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | Chadalapaka & Elliott Informational [Page 14] 787 | 788 | -------------------------------------------------------------------------------- /uSCSI/IoCtl.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID SCSICompleteIrp ( 5 | ULONG Handle , PVOID Ctx , UCHAR ScsiStatus , PUCHAR SenseData , ULONG SenseLen); 6 | 7 | NTSTATUS uFDOIoCtl (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 8 | { 9 | NTSTATUS Status = STATUS_SUCCESS; 10 | PIO_STACK_LOCATION Stack; 11 | PDEVICE_OBJECT DevPdo; 12 | PFDO_EXT FdoExt; 13 | PPDO_EXT PdoExt; 14 | 15 | Stack = IoGetCurrentIrpStackLocation( Irp ); 16 | FdoExt = (PFDO_EXT)DeviceObject->DeviceExtension; 17 | 18 | switch ( Stack->Parameters.DeviceIoControl.IoControlCode ) 19 | { 20 | case IOCTL_ISCSI_CREATE_SESSION: 21 | { 22 | ULONG Len; 23 | PUCHAR Target; 24 | PVOID Session; 25 | USCSI_CALL_BACK C = {0}; 26 | 27 | Target = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; 28 | 29 | if ( Target[Stack->Parameters.DeviceIoControl.InputBufferLength-1] != '\0' || 30 | ( Len = strlen ( Target ) ) != Stack->Parameters.DeviceIoControl.InputBufferLength -1 ) 31 | { 32 | Status = STATUS_INVALID_PARAMETER; 33 | } 34 | else 35 | { 36 | Status = IoCreateDevice(DeviceObject->DriverObject, 37 | sizeof(PDO_EXT) , //user size 38 | NULL, //Name 39 | FILE_DEVICE_UNKNOWN, //Device Type 40 | FILE_DEVICE_SECURE_OPEN| 41 | FILE_AUTOGENERATED_DEVICE_NAME, //Characteristics 42 | TRUE, //Exclusive 43 | &DevPdo); 44 | 45 | if ( NT_SUCCESS(Status) ) 46 | { 47 | PdoExt = (PPDO_EXT)DevPdo->DeviceExtension; 48 | 49 | InitializeListHead ( &PdoExt->PDOList); 50 | PdoExt->IsFDO = FALSE; 51 | PdoExt->Reported = FALSE; 52 | PdoExt->Self = DevPdo; 53 | 54 | PdoExt->Target = 55 | ExAllocatePoolWithTag ( PagedPool , Len + 1 , USCSI_TAG ); 56 | RtlCopyMemory ( PdoExt->Target , Target , Len + 1 ); 57 | 58 | C.CompleteCmd = SCSICompleteIrp; 59 | C.CompleteCtx = PdoExt; 60 | PdoExt->Session = uSCSICreateSession ( PdoExt->Target , &C); 61 | 62 | if ( PdoExt->Session ) 63 | { 64 | ExInterlockedInsertTailList ( &FdoExt->PDOList , 65 | &PdoExt->PDOList, 66 | &FdoExt->PDOLock); 67 | FdoExt->PDOCount++; 68 | IoInvalidateDeviceRelations ( FdoExt->LowerDev , BusRelations ); 69 | //DbgPrint("%s Session to Target %s Created\n" , __FUNCTION__, PdoExt->Target ); 70 | } 71 | else 72 | { 73 | if ( DevPdo ) 74 | IoDeleteDevice ( DevPdo ); 75 | Status = STATUS_UNSUCCESSFUL; 76 | } 77 | } 78 | } 79 | } 80 | break; 81 | 82 | case IOCTL_ISCSI_ADD_TARGETS: 83 | { 84 | PTGTS Tgts; 85 | PTGT Tgt; 86 | ULONG i; 87 | Tgts = (PTGTS)Irp->AssociatedIrp.SystemBuffer; 88 | 89 | if ( sizeof (TGTS) > Stack->Parameters.DeviceIoControl.InputBufferLength) 90 | Status = STATUS_BUFFER_TOO_SMALL; 91 | 92 | else if ( Tgts->Size != Stack->Parameters.DeviceIoControl.InputBufferLength ) 93 | Status = STATUS_INVALID_PARAMETER; 94 | 95 | else 96 | { 97 | Tgt = (PTGT)(Tgts + 1); 98 | for ( i = 0 ; i < Tgts->Count ; i++ ) 99 | { 100 | uSCSIAddTarget ( Tgt->TargetName , Tgt->Addr , Tgt->Port ); 101 | Tgt++; 102 | } 103 | } 104 | } 105 | break; 106 | 107 | case IOCTL_ISCSI_GET_TARGETS: 108 | { 109 | PTGTS Tgts; 110 | PTGT Tgt; 111 | Tgts = (PTGTS)Irp->AssociatedIrp.SystemBuffer; 112 | 113 | if ( sizeof (TGTS) > Stack->Parameters.DeviceIoControl.InputBufferLength) 114 | Status = STATUS_BUFFER_TOO_SMALL; 115 | 116 | else if ( !Tgts->Size ) 117 | { 118 | Tgts->Size = uSCSIGetTargetsSize(); 119 | Irp->IoStatus.Information = Stack->Parameters.DeviceIoControl.OutputBufferLength; 120 | } 121 | 122 | else if ( Tgts->Size != Stack->Parameters.DeviceIoControl.InputBufferLength ) 123 | Status = STATUS_INVALID_PARAMETER; 124 | 125 | else 126 | { 127 | Status = uSCSIPopTargets ( Tgts ); 128 | Irp->IoStatus.Information = Stack->Parameters.DeviceIoControl.OutputBufferLength; 129 | } 130 | } 131 | break; 132 | } 133 | 134 | Irp->IoStatus.Status = Status; 135 | IoCompleteRequest ( Irp , IO_NO_INCREMENT ); 136 | return Status; 137 | } 138 | 139 | // 140 | // 141 | // 142 | 143 | NTSTATUS uPDOIoCtl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 144 | { 145 | NTSTATUS Status = STATUS_SUCCESS; 146 | PIO_STACK_LOCATION Stack; 147 | PPDO_EXT Ext; 148 | 149 | Stack = IoGetCurrentIrpStackLocation( Irp ); 150 | Ext = (PPDO_EXT)DeviceObject->DeviceExtension; 151 | 152 | switch (Stack->Parameters.DeviceIoControl.IoControlCode) 153 | { 154 | case IOCTL_STORAGE_QUERY_PROPERTY: 155 | { 156 | PSTORAGE_PROPERTY_QUERY PropQuery; 157 | PSTORAGE_DESCRIPTOR_HEADER DescriptorHeader; 158 | PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor; 159 | PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor; 160 | 161 | PropQuery = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer; 162 | 163 | if (sizeof (STORAGE_PROPERTY_QUERY) > 164 | Stack->Parameters.DeviceIoControl.InputBufferLength) 165 | { 166 | Status = STATUS_INVALID_PARAMETER; 167 | Irp->IoStatus.Information = 0; 168 | } 169 | else 170 | { 171 | /* 172 | PropertyId: 173 | StorageDeviceProperty = 0, 174 | StorageAdapterProperty, 175 | StorageDeviceIdProperty, 176 | StorageDeviceUniqueIdProperty, // See storduid.h for details 177 | StorageDeviceWriteCacheProperty, 178 | StorageMiniportProperty, 179 | StorageAccessAlignmentProperty, 180 | StorageDeviceSeekPenaltyProperty, 181 | StorageDeviceTrimProperty, 182 | StorageDeviceWriteAggregationProperty 183 | */ 184 | /* 185 | QueryType: 186 | PropertyStandardQuery = 0, 187 | PropertyExistsQuery, 188 | PropertyMaskQuery, 189 | PropertyQueryMaxDefined 190 | */ 191 | DbgPrint("%s IOCTL_STORAGE_QUERY_PROPERTY PropertyId=0x%X QueryType=0x%X\n" , 192 | __FUNCTION__ , PropQuery->PropertyId , PropQuery->QueryType); 193 | 194 | if (StorageDeviceProperty == PropQuery->PropertyId) 195 | { 196 | 197 | if ( sizeof (STORAGE_DESCRIPTOR_HEADER) == 198 | Stack->Parameters.DeviceIoControl.OutputBufferLength) 199 | { 200 | DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)Irp->AssociatedIrp.SystemBuffer; 201 | DescriptorHeader->Version = 1; 202 | DescriptorHeader->Size = 203 | sizeof (STORAGE_DEVICE_DESCRIPTOR)+22; 204 | Irp->IoStatus.Information = sizeof (STORAGE_DESCRIPTOR_HEADER); 205 | } 206 | else 207 | { 208 | DeviceDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer; 209 | 210 | DeviceDescriptor->Version = 1; 211 | DeviceDescriptor->Size = sizeof (STORAGE_DEVICE_DESCRIPTOR); 212 | DeviceDescriptor->DeviceType = DIRECT_ACCESS_DEVICE; 213 | DeviceDescriptor->DeviceTypeModifier = 0; 214 | DeviceDescriptor->RemovableMedia = FALSE; 215 | DeviceDescriptor->CommandQueueing = FALSE; 216 | DeviceDescriptor->VendorIdOffset = sizeof (STORAGE_DEVICE_DESCRIPTOR); 217 | DeviceDescriptor->ProductIdOffset = sizeof (STORAGE_DEVICE_DESCRIPTOR)+8; 218 | DeviceDescriptor->ProductRevisionOffset = sizeof (STORAGE_DEVICE_DESCRIPTOR)+14; 219 | DeviceDescriptor->SerialNumberOffset = sizeof (STORAGE_DEVICE_DESCRIPTOR)+17; 220 | DeviceDescriptor->BusType = BusTypeiScsi; 221 | DeviceDescriptor->RawPropertiesLength = 22; 222 | RtlCopyMemory(&DeviceDescriptor->RawDeviceProperties[DeviceDescriptor->VendorIdOffset],"yushang\0",8); 223 | RtlCopyMemory(&DeviceDescriptor->RawDeviceProperties[DeviceDescriptor->ProductIdOffset],"uSCSI\0",6); 224 | RtlCopyMemory(&DeviceDescriptor->RawDeviceProperties[DeviceDescriptor->ProductRevisionOffset],"r1\0",3); 225 | RtlCopyMemory(&DeviceDescriptor->RawDeviceProperties[DeviceDescriptor->SerialNumberOffset],"A123\0",5); 226 | // 227 | Irp->IoStatus.Information = sizeof (STORAGE_DEVICE_DESCRIPTOR); 228 | } 229 | } 230 | else if ( StorageAdapterProperty == PropQuery->PropertyId ) 231 | { 232 | if ( sizeof (STORAGE_DESCRIPTOR_HEADER) == 233 | Stack->Parameters.DeviceIoControl.OutputBufferLength) 234 | { 235 | DescriptorHeader = (PSTORAGE_DESCRIPTOR_HEADER)Irp->AssociatedIrp.SystemBuffer; 236 | DescriptorHeader->Version = 1; 237 | DescriptorHeader->Size = sizeof (STORAGE_ADAPTER_DESCRIPTOR); 238 | Irp->IoStatus.Information = sizeof (STORAGE_DESCRIPTOR_HEADER); 239 | } 240 | else 241 | { 242 | AdapterDescriptor = (PSTORAGE_ADAPTER_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer; 243 | 244 | AdapterDescriptor->Version = 20; 245 | AdapterDescriptor->Size = sizeof (STORAGE_ADAPTER_DESCRIPTOR); 246 | AdapterDescriptor->MaximumTransferLength = 0x40*0x1000;//0x40000; 247 | AdapterDescriptor->MaximumPhysicalPages = 0x41; 248 | AdapterDescriptor->AlignmentMask = 0x3; 249 | AdapterDescriptor->AdapterUsesPio = FALSE; 250 | AdapterDescriptor->AdapterScansDown = FALSE; 251 | AdapterDescriptor->CommandQueueing = FALSE; 252 | AdapterDescriptor->AcceleratedTransfer = FALSE; 253 | AdapterDescriptor->BusType = BusTypeiScsi; 254 | AdapterDescriptor->BusMajorVersion = 2; 255 | AdapterDescriptor->BusMinorVersion = 0; 256 | Irp->IoStatus.Information = sizeof (STORAGE_ADAPTER_DESCRIPTOR); 257 | } 258 | } 259 | else 260 | { 261 | DbgPrint("*** IOCTL_STORAGE_QUERY_PROPERTY Unprocessed PropertyId=0x%X\n" , PropQuery->PropertyId); 262 | } 263 | } 264 | } 265 | break; 266 | 267 | case IOCTL_DISK_GET_DRIVE_GEOMETRY: 268 | { 269 | PDISK_GEOMETRY Geo = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer; 270 | 271 | DbgPrint("%s IOCTL_DISK_GET_DRIVE_GEOMETRY\n" , __FUNCTION__ ); 272 | 273 | if (sizeof (DISK_GEOMETRY) > Stack->Parameters.DeviceIoControl.OutputBufferLength) 274 | { 275 | Status = STATUS_INVALID_PARAMETER; 276 | Irp->IoStatus.Information = 0; 277 | } 278 | else 279 | { 280 | // 256M 281 | Geo->Cylinders.LowPart = 0x100; 282 | Geo->Cylinders.HighPart = 0; 283 | Geo->MediaType = FixedMedia; 284 | Geo->TracksPerCylinder = 0x100; 285 | Geo->SectorsPerTrack = 0x4; 286 | Geo->BytesPerSector = 0x400; 287 | Irp->IoStatus.Information = sizeof (DISK_GEOMETRY); 288 | } 289 | } 290 | break; 291 | 292 | case IOCTL_SCSI_GET_ADDRESS: 293 | { 294 | PSCSI_ADDRESS Addr; 295 | Addr = (PSCSI_ADDRESS)Irp->AssociatedIrp.SystemBuffer; 296 | 297 | DbgPrint("%s IOCTL_SCSI_GET_ADDRESS\n" , __FUNCTION__ ); 298 | 299 | if ( sizeof (SCSI_ADDRESS) > Stack->Parameters.DeviceIoControl.OutputBufferLength ) 300 | { 301 | Status = STATUS_INVALID_PARAMETER; 302 | Irp->IoStatus.Information = 0; 303 | } 304 | else 305 | { 306 | Addr->Length = sizeof (SCSI_ADDRESS); 307 | Addr->PortNumber = 1; 308 | Addr->PathId = 1; 309 | Addr->TargetId = 1; 310 | Addr->Lun = 0; 311 | Irp->IoStatus.Information = sizeof (SCSI_ADDRESS); 312 | } 313 | } 314 | break; 315 | 316 | case IOCTL_SCSI_PASS_THROUGH_DIRECT: 317 | DbgPrint("%s IOCTL_SCSI_PASS_THROUGH_DIRECT\n" , __FUNCTION__ ); 318 | break; 319 | 320 | case IOCTL_MOUNTDEV_QUERY_STABLE_GUID: 321 | DbgPrint("%s IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n" , __FUNCTION__ ); 322 | break; 323 | 324 | case IOCTL_MOUNTDEV_LINK_CREATED: 325 | DbgPrint("%s IOCTL_MOUNTDEV_LINK_CREATED\n" , __FUNCTION__ ); 326 | break; 327 | 328 | case IOCTL_VOLUME_ONLINE: 329 | DbgPrint("%s IOCTL_VOLUME_ONLINE\n" , __FUNCTION__ ); 330 | break; 331 | 332 | case IOCTL_VOLUME_GET_GPT_ATTRIBUTES: 333 | DbgPrint("%s IOCTL_VOLUME_GET_GPT_ATTRIBUTES\n" , __FUNCTION__ ); 334 | break; 335 | 336 | case IOCTL_SCSI_MINIPORT: 337 | { 338 | PSRB_IO_CONTROL IoCtl; 339 | IoCtl = (PSRB_IO_CONTROL)Irp->AssociatedIrp.SystemBuffer; 340 | 341 | DbgPrint("IOCTL_SCSI_MINIPORT HeaderLength=0x%X Signature=%c%c%c%c%c%c%c%c Timeout=0x%X ControlCode=0x%X ReturnCode=0x%X Length=0x%X\n", 342 | IoCtl->HeaderLength, 343 | IoCtl->Signature[0],IoCtl->Signature[1],IoCtl->Signature[2],IoCtl->Signature[3], 344 | IoCtl->Signature[4],IoCtl->Signature[5],IoCtl->Signature[6],IoCtl->Signature[7], 345 | IoCtl->Timeout , 346 | IoCtl->ControlCode, 347 | IoCtl->ReturnCode, 348 | IoCtl->Length); 349 | } 350 | break; 351 | //HD Tune Pro 5.0 352 | /* 353 | case IOCTL_ATA_PASS_THROUGH: 354 | { 355 | PATA_PASS_THROUGH_EX Pat; 356 | DbgPrint("%s IOCTL_ATA_PASS_THROUGH\n" , __FUNCTION__ ); 357 | 358 | if ( sizeof(ATA_PASS_THROUGH_EX) > 359 | Stack->Parameters.DeviceIoControl.InputBufferLength ) 360 | { 361 | Status = STATUS_INVALID_PARAMETER; 362 | } 363 | else 364 | { 365 | Pat = (PATA_PASS_THROUGH_EX)Irp->AssociatedIrp.SystemBuffer; 366 | DbgPrint("PathId.TargetId.Lun=0x%X.%X.%X AtaFlags=0x%X DataTransferLength=0x%X\n" , 367 | Pat->PathId,Pat->TargetId,Pat->Lun, 368 | Pat->AtaFlags,Pat->DataTransferLength); 369 | if ( Pat->AtaFlags & ATA_FLAGS_DATA_IN && 370 | (Pat->DataTransferLength + sizeof(ATA_PASS_THROUGH_EX)) > 371 | Stack->Parameters.DeviceIoControl.InputBufferLength ) 372 | Status = STATUS_INVALID_PARAMETER; 373 | else 374 | { 375 | 376 | } 377 | } 378 | 379 | } 380 | break; 381 | */ 382 | //ntddft.h 383 | case FT_BALANCED_READ_MODE: 384 | Status = STATUS_UNSUCCESSFUL; 385 | DbgPrint("%s FT_BALANCED_READ_MODE\n" , __FUNCTION__ ); 386 | break; 387 | 388 | default: 389 | DbgPrint("%s *** Unprocessed IoControlCode 0x%X (DevType 0x%X , Function 0x%X)\n" , 390 | __FUNCTION__ , 391 | Stack->Parameters.DeviceIoControl.IoControlCode, 392 | DEVICE_TYPE_FROM_CTL_CODE(Stack->Parameters.DeviceIoControl.IoControlCode), 393 | FUNC_CODE(Stack->Parameters.DeviceIoControl.IoControlCode)); 394 | 395 | break; 396 | } 397 | 398 | Irp->IoStatus.Status = Status; 399 | IoCompleteRequest ( Irp , IO_NO_INCREMENT ); 400 | return Status; 401 | } 402 | 403 | // 404 | // IRP_MJ_DEVICE_CONTROL 405 | // 406 | 407 | NTSTATUS uIoCtl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 408 | { 409 | PCOMMON_EXT Ext; 410 | Ext = (PCOMMON_EXT)DeviceObject->DeviceExtension; 411 | 412 | if ( Ext->IsFDO ) 413 | return uFDOIoCtl ( DeviceObject , Irp ); 414 | else 415 | return uPDOIoCtl ( DeviceObject , Irp ); 416 | } 417 | 418 | -------------------------------------------------------------------------------- /uSCSI/MAKEFILE: -------------------------------------------------------------------------------- 1 | # 2 | # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source 3 | # file to this component. This file merely indirects to the real make file 4 | # that is shared by all the driver components of the Windows NT DDK 5 | # 6 | 7 | !INCLUDE $(NTMAKEENV)\makefile.def 8 | 9 | -------------------------------------------------------------------------------- /uSCSI/Pnp.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | NTSTATUS uStartFdo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 5 | { 6 | NTSTATUS Status = STATUS_SUCCESS; 7 | PIO_STACK_LOCATION Stack; 8 | PFDO_EXT FdoExt; 9 | PDEVICE_RELATIONS Rels; 10 | 11 | Stack = IoGetCurrentIrpStackLocation( Irp ); 12 | FdoExt = DeviceObject->DeviceExtension; 13 | 14 | Status = SendIrpSynchronously(FdoExt->LowerDev,Irp); 15 | if (NT_ERROR(Status)) 16 | goto ErrorOut; 17 | 18 | Status = IoSetDeviceInterfaceState (&FdoExt->InterfaceName , TRUE ); 19 | if (NT_ERROR(Status)) 20 | goto ErrorOut; 21 | 22 | uSCSIInitialize(); 23 | 24 | return Status; 25 | 26 | ErrorOut: 27 | 28 | return Status; 29 | } 30 | 31 | NTSTATUS uPnPFdo (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 32 | { 33 | NTSTATUS Status = STATUS_SUCCESS; 34 | ULONG i = 0 , OldCount; 35 | PIO_STACK_LOCATION Stack; 36 | PLIST_ENTRY PdoEnt , PdoEnt2; 37 | PFDO_EXT FdoExt; 38 | PPDO_EXT PdoExt; 39 | PDEVICE_RELATIONS OldRels , Rels; 40 | 41 | Stack = IoGetCurrentIrpStackLocation( Irp ); 42 | FdoExt = DeviceObject->DeviceExtension; 43 | 44 | KdPrintEx((DPFLTR_USCSI,DBG_USCSI, 45 | "%s MinorFunction 0x%X\n" , __FUNCTION__ , Stack->MinorFunction)); 46 | 47 | switch (Stack->MinorFunction ) 48 | { 49 | case IRP_MN_START_DEVICE: 50 | 51 | Status = uStartFdo (DeviceObject , Irp); 52 | 53 | break; 54 | 55 | case IRP_MN_QUERY_DEVICE_RELATIONS: 56 | 57 | if ( BusRelations == Stack->Parameters.QueryDeviceRelations.Type ) 58 | { 59 | OldRels = (PDEVICE_RELATIONS) Irp->IoStatus.Information; 60 | if (OldRels) 61 | OldCount = OldRels->Count; 62 | else 63 | OldCount = 0; 64 | 65 | Rels = ExAllocatePoolWithTag(PagedPool , 66 | sizeof (DEVICE_RELATIONS) + 67 | (FdoExt->PDOCount + OldCount - 1)*sizeof (PDEVICE_OBJECT), 68 | USCSI_TAG ); 69 | 70 | if ( Rels ) 71 | { 72 | if (OldCount) 73 | RtlCopyMemory (Rels->Objects, OldRels->Objects, 74 | OldCount * sizeof (PDEVICE_OBJECT)); 75 | 76 | Rels->Count = OldCount + FdoExt->PDOCount; 77 | 78 | PdoEnt = &FdoExt->PDOList; 79 | PdoEnt2 = PdoEnt->Flink; 80 | 81 | while ( PdoEnt2 != PdoEnt ) 82 | { 83 | PdoExt = CONTAINING_RECORD ( PdoEnt2, PDO_EXT , PDOList); 84 | if ( !PdoExt->Reported ) 85 | { 86 | ObReferenceObject( PdoExt->Self ); 87 | Rels->Objects[OldCount++] = PdoExt->Self; 88 | PdoExt->Reported = TRUE; 89 | } 90 | PdoEnt2 = PdoEnt2->Flink; 91 | } 92 | 93 | Irp->IoStatus.Information = (ULONG_PTR)Rels; 94 | } 95 | } 96 | break; 97 | 98 | case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: 99 | case IRP_MN_QUERY_CAPABILITIES: 100 | case IRP_MN_QUERY_PNP_DEVICE_STATE: 101 | 102 | IoSkipCurrentIrpStackLocation( Irp ); 103 | 104 | Status = IoCallDriver ( FdoExt->LowerDev , Irp ); 105 | 106 | return Status; 107 | 108 | case 0x18: 109 | //IRP_MN_QUERY_LEGACY_BUS_INFORMATION 110 | Status = SendIrpSynchronously( FdoExt->LowerDev , Irp ); 111 | break; 112 | } 113 | 114 | Irp->IoStatus.Status = Status; 115 | 116 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 117 | 118 | return Status; 119 | } 120 | 121 | NTSTATUS uStartPdo (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 122 | { 123 | NTSTATUS Status = STATUS_SUCCESS; 124 | PPDO_EXT PdoExt; 125 | 126 | PdoExt = (PPDO_EXT)DeviceObject->DeviceExtension; 127 | 128 | InitializeListHead ( &PdoExt->PDOList); 129 | InitializeListHead ( &PdoExt->CmdQueue); 130 | KeInitializeSpinLock ( &PdoExt->CmdQueueLock); 131 | /* 132 | ExInitializePagedLookasideList ( 133 | &PdoExt->CmdLookAside , NULL , NULL , 0 , sizeof (SCSICmd) , USCSI_TAG , 0);*/ 134 | 135 | return Status; 136 | 137 | } 138 | 139 | // 140 | // 141 | // 142 | 143 | NTSTATUS uPnPPdo (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 144 | { 145 | NTSTATUS Status = STATUS_SUCCESS; 146 | PIO_STACK_LOCATION Stack; 147 | PPDO_EXT PdoExt; 148 | PWCHAR Id = NULL; 149 | 150 | Stack = IoGetCurrentIrpStackLocation( Irp ); 151 | PdoExt = DeviceObject->DeviceExtension; 152 | 153 | KdPrintEx((DPFLTR_USCSI,DBG_USCSI, 154 | "%s MinorFunction 0x%X\n" , __FUNCTION__ , Stack->MinorFunction )); 155 | 156 | switch (Stack->MinorFunction) 157 | { 158 | case IRP_MN_START_DEVICE: 159 | 160 | uStartPdo ( DeviceObject , Irp ); 161 | break; 162 | 163 | case IRP_MN_QUERY_ID: 164 | 165 | if (BusQueryHardwareIDs == Stack->Parameters.QueryId.IdType || 166 | BusQueryCompatibleIDs == Stack->Parameters.QueryId.IdType ) 167 | { 168 | Id = ExAllocatePoolWithTag (PagedPool , HARDWAREID_LEN , USCSI_TAG); 169 | if ( Id ) 170 | { 171 | RtlCopyMemory ( Id , HARDWAREID , HARDWAREID_LEN ); 172 | Irp->IoStatus.Information = (ULONG_PTR)Id; 173 | } 174 | else 175 | Status = STATUS_UNSUCCESSFUL; 176 | 177 | } 178 | else if ( BusQueryDeviceID == Stack->Parameters.QueryId.IdType ) 179 | { 180 | Id = ExAllocatePoolWithTag (PagedPool ,DEVICEID_LEN ,USCSI_TAG); 181 | if ( Id ) 182 | { 183 | RtlCopyMemory ( Id , DEVICEID , DEVICEID_LEN ); 184 | Irp->IoStatus.Information = (ULONG_PTR)Id; 185 | } 186 | else 187 | Status = STATUS_UNSUCCESSFUL; 188 | } 189 | /* 190 | else if ( BusQueryInstanceID == Stack->Parameters.QueryId.IdType ) 191 | { 192 | Id = ExAllocatePoolWithTag ( PagedPool , 2 , USCSI_TAG); 193 | if ( Id ) 194 | { 195 | RtlCopyMemory ( Id , L"\0" , 2 ); 196 | Irp->IoStatus.Information = (ULONG_PTR)Id; 197 | } 198 | else 199 | Status = STATUS_UNSUCCESSFUL; 200 | }*/ 201 | KdPrintEx((DPFLTR_USCSI,DBG_USCSI, 202 | "%s IdType %d Id %ws\n" , __FUNCTION__ , 203 | Stack->Parameters.QueryId.IdType , Id )); 204 | break; 205 | 206 | case IRP_MN_QUERY_DEVICE_TEXT: 207 | 208 | // Description 209 | if (DeviceTextDescription == 210 | Stack->Parameters.QueryDeviceText.DeviceTextType) 211 | { 212 | Id = ExAllocatePoolWithTag ( PagedPool , DEVICEDESC_LEN , USCSI_TAG); 213 | if (Id) 214 | { 215 | RtlCopyMemory ( Id , DEVICEDESC , DEVICEDESC_LEN ); 216 | Irp->IoStatus.Information = (ULONG_PTR)Id; 217 | } 218 | else 219 | Status = STATUS_UNSUCCESSFUL; 220 | } 221 | // Location info 222 | else if (DeviceTextLocationInformation == 223 | Stack->Parameters.QueryDeviceText.DeviceTextType) 224 | { 225 | Id = ExAllocatePoolWithTag ( PagedPool , DEVICELOC_LEN , USCSI_TAG); 226 | if (Id) 227 | { 228 | RtlCopyMemory ( Id , DEVICELOC , DEVICELOC_LEN ); 229 | Irp->IoStatus.Information = (ULONG_PTR)Id; 230 | } 231 | else 232 | Status = STATUS_UNSUCCESSFUL; 233 | } 234 | KdPrintEx((DPFLTR_USCSI,DBG_USCSI, 235 | "%s Text %d Id %ws\n" , __FUNCTION__ , 236 | Stack->Parameters.QueryDeviceText.DeviceTextType , Id )); 237 | break; 238 | 239 | case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: 240 | 241 | Status = Irp->IoStatus.Status; 242 | 243 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 244 | 245 | return Status; 246 | 247 | case IRP_MN_QUERY_RESOURCES: 248 | { 249 | PCM_RESOURCE_LIST Res; 250 | Res = ExAllocatePoolWithTag ( PagedPool , sizeof (CM_RESOURCE_LIST) , 251 | USCSI_TAG); 252 | if ( Res ) 253 | { 254 | Res->Count = 1; 255 | Res->List[0].PartialResourceList.Version = 1; 256 | Res->List[0].PartialResourceList.Revision = 1; 257 | Res->List[0].PartialResourceList.Count = 0; 258 | Irp->IoStatus.Information = (ULONG_PTR)Res; 259 | } 260 | else 261 | { 262 | Irp->IoStatus.Information = 0; 263 | Status = STATUS_INSUFFICIENT_RESOURCES; 264 | } 265 | } 266 | break; 267 | 268 | case IRP_MN_QUERY_BUS_INFORMATION: 269 | { 270 | PPNP_BUS_INFORMATION businfo; 271 | businfo = ExAllocatePoolWithTag (PagedPool, sizeof(PNP_BUS_INFORMATION), 272 | USCSI_TAG); 273 | //businfo->BusTypeGuid = GUID_USCSI_BUS; 274 | businfo->LegacyBusType = PNPBus; 275 | businfo->BusNumber = 0; 276 | } 277 | break; 278 | 279 | case IRP_MN_QUERY_CAPABILITIES: 280 | { 281 | PDEVICE_CAPABILITIES Caps; 282 | Caps = Stack->Parameters.DeviceCapabilities.Capabilities; 283 | 284 | Caps->LockSupported = FALSE; 285 | Caps->EjectSupported = FALSE; 286 | Caps->Removable = FALSE; 287 | Caps->DockDevice = FALSE; 288 | Caps->D1Latency = Caps->D2Latency = Caps->D3Latency = 0; 289 | Caps->NoDisplayInUI = 1; 290 | Irp->IoStatus.Information = sizeof (DEVICE_CAPABILITIES); 291 | } 292 | break; 293 | 294 | case IRP_MN_QUERY_PNP_DEVICE_STATE: 295 | { 296 | Irp->IoStatus.Information = 0; 297 | } 298 | break; 299 | } 300 | 301 | Irp->IoStatus.Status = Status; 302 | 303 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 304 | 305 | return Status; 306 | } 307 | 308 | // 309 | // 310 | // 311 | 312 | NTSTATUS uPnP(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 313 | { 314 | PCOMMON_EXT Ext; 315 | 316 | Ext = (PCOMMON_EXT)DeviceObject->DeviceExtension; 317 | 318 | if ( Ext->IsFDO ) 319 | return uPnPFdo (DeviceObject , Irp ); 320 | else 321 | return uPnPPdo (DeviceObject , Irp ); 322 | } -------------------------------------------------------------------------------- /uSCSI/Public.h: -------------------------------------------------------------------------------- 1 | #ifndef _USCSI_PUBLIC_H 2 | #define _USCSI_PUBLIC_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | // {A10A82EB-E4FC-4d1d-9F5A-DFA326E393F5} 9 | static const GUID USCSI_DISK_INTERFACE = 10 | { 0xa10a82eb, 0xe4fc, 0x4d1d, { 0x9f, 0x5a, 0xdf, 0xa3, 0x26, 0xe3, 0x93, 0xf5 } }; 11 | 12 | #define FILE_DEVICE_USCSI 43 13 | 14 | #define BUSENUM_IOCTL(_index_) \ 15 | CTL_CODE (FILE_DEVICE_USCSI, _index_, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) 16 | 17 | #define IOCTL_ISCSI_CREATE_SESSION BUSENUM_IOCTL (0x01) 18 | #define IOCTL_ISCSI_ADD_TARGETS BUSENUM_IOCTL (0x02) 19 | #define IOCTL_ISCSI_GET_TARGETS BUSENUM_IOCTL (0x03) 20 | 21 | #endif -------------------------------------------------------------------------------- /uSCSI/ReadWrite.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | // 5 | // 6 | // 7 | 8 | NTSTATUS uFlush (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 9 | { 10 | NTSTATUS Status = STATUS_SUCCESS; 11 | PIO_STACK_LOCATION Stack; 12 | 13 | Stack = IoGetCurrentIrpStackLocation( Irp ); 14 | 15 | Irp->IoStatus.Status = Status; 16 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 17 | return Status; 18 | } 19 | 20 | // 21 | // 22 | // 23 | 24 | NTSTATUS uCreateClose (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 25 | { 26 | NTSTATUS Status = STATUS_SUCCESS; 27 | PIO_STACK_LOCATION Stack; 28 | 29 | Stack = IoGetCurrentIrpStackLocation( Irp ); 30 | 31 | Irp->IoStatus.Status = Status; 32 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 33 | return Status; 34 | } 35 | 36 | // 37 | // 38 | // 39 | 40 | NTSTATUS uReadWrite (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 41 | { 42 | NTSTATUS Status = STATUS_SUCCESS; 43 | PIO_STACK_LOCATION Stack; 44 | 45 | Stack = IoGetCurrentIrpStackLocation( Irp ); 46 | 47 | Irp->IoStatus.Status = Status; 48 | IoCompleteRequest( Irp , IO_NO_INCREMENT ); 49 | return Status; 50 | } -------------------------------------------------------------------------------- /uSCSI/SOURCES: -------------------------------------------------------------------------------- 1 | TARGETNAME=uSCSI 2 | TARGETTYPE=DRIVER 3 | TARGETPATH=..\obj 4 | TARGETLIBS=..\obj\i386\uSCSIPort.lib \ 5 | $(DDK_LIB_PATH)\scsiport.lib 6 | INCLUDES=..\uSCSIPort 7 | SOURCES= uSCSI.c \ 8 | IoCtl.c \ 9 | Pnp.c \ 10 | Utils.c \ 11 | ReadWrite.c \ 12 | srb.c 13 | 14 | -------------------------------------------------------------------------------- /uSCSI/USCSI.INF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldoldman/iSCSI_drv/e118622fa43c4756038bc72a2ef4492f807a10f8/uSCSI/USCSI.INF -------------------------------------------------------------------------------- /uSCSI/Utils.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | // 5 | // 6 | // 7 | 8 | NTSTATUS CompletionRoutine (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,IN PVOID Context) 9 | { 10 | if (Irp->PendingReturned) 11 | { 12 | IoMarkIrpPending( Irp ); 13 | KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE); 14 | } 15 | return STATUS_MORE_PROCESSING_REQUIRED; 16 | } 17 | 18 | // 19 | // 20 | // 21 | 22 | NTSTATUS SendIrpSynchronously (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 23 | { 24 | KEVENT Event; 25 | NTSTATUS Status; 26 | 27 | KeInitializeEvent(&Event, NotificationEvent, FALSE); 28 | 29 | IoCopyCurrentIrpStackLocationToNext(Irp); 30 | 31 | IoSetCompletionRoutine(Irp, CompletionRoutine, &Event, TRUE, TRUE, TRUE); 32 | 33 | Status = IoCallDriver(DeviceObject, Irp); 34 | if ( STATUS_PENDING == Status ) 35 | { 36 | KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL ); 37 | Status = Irp->IoStatus.Status; 38 | } 39 | 40 | return Status; 41 | } 42 | 43 | // 44 | // 45 | // 46 | 47 | VOID SCSICompleteIrp(ULONG Handle, PVOID Ctx, UCHAR ScsiStatus, PUCHAR SenseData, ULONG SenseLen) 48 | { 49 | PLIST_ENTRY Ent , Ent2; 50 | PIO_STACK_LOCATION Stack; 51 | PSCSICmd Cmd; 52 | PPDO_EXT PdoExt; 53 | PSCSI_REQUEST_BLOCK Srb; 54 | BOOLEAN Found = FALSE; 55 | UCHAR ScsiOpcode; 56 | KIRQL OldIrql; 57 | 58 | PdoExt = (PPDO_EXT)Ctx; 59 | Ent = &PdoExt->CmdQueue; 60 | Ent2 = Ent->Flink; 61 | 62 | KeAcquireSpinLock ( &PdoExt->CmdQueueLock , &OldIrql ); 63 | 64 | while ( Ent2 != Ent ) 65 | { 66 | Cmd = CONTAINING_RECORD ( Ent2 , SCSICmd , List ); 67 | if ( Cmd->Handle == Handle ) 68 | { 69 | Found = TRUE; 70 | break; 71 | } 72 | Ent2 = Ent2->Flink; 73 | } 74 | 75 | if ( !Found ) 76 | { 77 | KeReleaseSpinLock ( &PdoExt->CmdQueueLock , OldIrql ); 78 | DbgPrint("%s Handle=0x%X Not Found!\n", __FUNCTION__ , Handle ); 79 | return; 80 | } 81 | 82 | RemoveHeadList ( Cmd->List.Blink ); 83 | 84 | KeReleaseSpinLock ( &PdoExt->CmdQueueLock , OldIrql ); 85 | 86 | Stack = IoGetCurrentIrpStackLocation ( Cmd->Irp ); 87 | Srb = Stack->Parameters.Scsi.Srb; 88 | ScsiOpcode = Srb->Cdb[0]; 89 | 90 | if ( SenseData ) 91 | { 92 | Srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID; 93 | RtlCopyMemory ( Srb->SenseInfoBuffer , SenseData , SenseLen ); 94 | } 95 | else 96 | Srb->SrbStatus = SRB_STATUS_SUCCESS; 97 | 98 | Srb->ScsiStatus = ScsiStatus; 99 | Cmd->Irp->IoStatus.Status = STATUS_SUCCESS; 100 | 101 | IoCompleteRequest ( Cmd->Irp , IO_NO_INCREMENT ); 102 | 103 | ExFreePoolWithTag ( Cmd , USCSI_TAG); 104 | } 105 | 106 | // 107 | // 108 | // 109 | 110 | NTSTATUS AllocateSCSICommand ( PIRP Irp , PPDO_EXT Ext) 111 | { 112 | PIO_STACK_LOCATION Stack; 113 | PSCSICmd Cmd; 114 | PSCSI_REQUEST_BLOCK Srb; 115 | PUCHAR DataBuf; 116 | UCHAR TaskAttr , Dir , ScsiOpcode; 117 | 118 | Stack = IoGetCurrentIrpStackLocation( Irp ); 119 | Srb = Stack->Parameters.Scsi.Srb; 120 | ScsiOpcode = Srb->Cdb[0]; 121 | 122 | if ( Srb->QueueAction == SRB_SIMPLE_TAG_REQUEST ) 123 | TaskAttr = SCSI_TASK_SIMPLE; 124 | else if ( Srb->QueueAction == SRB_HEAD_OF_QUEUE_TAG_REQUEST ) 125 | TaskAttr = SCSI_TASK_HOQ; 126 | else if ( Srb->QueueAction == SRB_ORDERED_QUEUE_TAG_REQUEST) 127 | TaskAttr = SCSI_TASK_ORDERED; 128 | else 129 | TaskAttr = SCSI_TASK_UNTAGGED; 130 | 131 | if ( (Srb->SrbFlags & SRB_FLAGS_DATA_IN) && ( Srb->SrbFlags & SRB_FLAGS_DATA_OUT )) 132 | Dir = SCSI_CMD_DIR_BOTH; 133 | else if ( Srb->SrbFlags & SRB_FLAGS_DATA_IN ) 134 | Dir = SCSI_CMD_DIR_READ; 135 | else if ( Srb->SrbFlags & SRB_FLAGS_DATA_OUT ) 136 | Dir = SCSI_CMD_DIR_WRITE; 137 | else 138 | Dir = SCSI_CMD_DIR_NONE; 139 | 140 | //Cmd = ExAllocateFromPagedLookasideList ( &Ext->CmdLookAside ); 141 | Cmd = ExAllocatePoolWithTag ( PagedPool , sizeof (SCSICmd) , USCSI_TAG); 142 | InitializeListHead ( &Cmd->List ); 143 | Cmd->Irp = Irp; 144 | /* 145 | if ( ScsiOpcode == SCSIOP_READ || ScsiOpcode == SCSIOP_WRITE ) 146 | DataBuf = MmGetSystemAddressForMdlSafe (Irp->MdlAddress , NormalPagePriority); 147 | else 148 | DataBuf = Srb->DataBuffer;*/ 149 | 150 | if ( NULL != Irp->MdlAddress ) 151 | DataBuf = MmGetSystemAddressForMdlSafe (Irp->MdlAddress , NormalPagePriority); 152 | else 153 | DataBuf = Srb->DataBuffer; 154 | 155 | ExInterlockedInsertTailList ( &Ext->CmdQueue , &Cmd->List , &Ext->CmdQueueLock); 156 | /* 157 | DbgPrint("ScsiOpcode=0x%X SrbFlags=0x%X MdlFlags=0x%X DataBuf=0x%X DataBuffer=0x%X\n" , 158 | ScsiOpcode, 159 | Srb->SrbFlags, 160 | Irp->MdlAddress?Irp->MdlAddress->MdlFlags:0x0, 161 | DataBuf , 162 | Srb->DataBuffer );*/ 163 | 164 | uSCSIProcessSCSICmd ( Ext->Session, 165 | Srb->Cdb , 166 | Srb->CdbLength , 167 | DataBuf, 168 | Srb->DataTransferLength, 169 | Srb->DataTransferLength, 170 | TaskAttr, 171 | Dir, 172 | &Cmd->Handle ); 173 | 174 | IoMarkIrpPending ( Irp ); 175 | return STATUS_PENDING; 176 | } 177 | 178 | // 179 | // 180 | // 181 | 182 | NTSTATUS ProcessCommand (PIRP Irp , PPDO_EXT Ext) 183 | { 184 | PIO_STACK_LOCATION Stack; 185 | PCDB Cdb; 186 | UCHAR OpCode; 187 | PSCSI_REQUEST_BLOCK Srb; 188 | 189 | Stack = IoGetCurrentIrpStackLocation( Irp ); 190 | Srb = Stack->Parameters.Scsi.Srb; 191 | Cdb = (PCDB)Srb->Cdb; 192 | OpCode = *((PUCHAR)Cdb); 193 | 194 | switch ( OpCode ) 195 | { 196 | case SCSIOP_READ_CAPACITY: //0x25 197 | case SCSIOP_TEST_UNIT_READY: //0x00 198 | case SCSIOP_INQUIRY: //0x12 199 | case SCSIOP_MODE_SELECT: //0x15 200 | case SCSIOP_MODE_SENSE: //0x1A 201 | case SCSIOP_READ: //0x28 202 | case SCSIOP_WRITE: //0x2A 203 | case SCSIOP_VERIFY: //0x2F 204 | case SCSIOP_SYNCHRONIZE_CACHE: //0x35 205 | return AllocateSCSICommand ( Irp , Ext); 206 | 207 | default: 208 | DbgPrint("%s *** Unprocessed Opcode 0x%X\n" , __FUNCTION__ , OpCode); 209 | break; 210 | } 211 | 212 | return STATUS_SUCCESS; 213 | } -------------------------------------------------------------------------------- /uSCSI/precomp.h: -------------------------------------------------------------------------------- 1 | #ifndef PRECOMP_H 2 | #define PRECOMP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Public.h" 14 | #include "Protocol.h" 15 | #include "uSCSI.h" 16 | #include "uSCSIPort.h" 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /uSCSI/srb.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | // 5 | // IRP_MJ_SCSI 6 | // 7 | 8 | NTSTATUS uScsi (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) 9 | { 10 | NTSTATUS Status = STATUS_SUCCESS; 11 | PIO_STACK_LOCATION Stack; 12 | PPDO_EXT Ext; 13 | PSCSI_REQUEST_BLOCK Srb; 14 | PSENSE_DATA Sense; 15 | PCDB Cdb; 16 | 17 | Stack = IoGetCurrentIrpStackLocation( Irp ); 18 | Ext = (PPDO_EXT)DeviceObject->DeviceExtension; 19 | Srb = Stack->Parameters.Scsi.Srb; 20 | Sense = (PSENSE_DATA)Srb->SenseInfoBuffer; 21 | Cdb = (PCDB)Srb->Cdb; 22 | /* 23 | DbgPrint("%s Srb.Function=%d SrbStatus=%d ScsiStatus=%d PathId.TargetId.Lun=%d.%d.%d QueueTag=%d QueueAction=%d CdbLength=%d SenseInfoBufferLength=%d SrbFlags=0x%X DataTransferLength=%d TimeOutValue=%d DataBuffer=0x%p SenseInfoBuffer=0x%p NextSrb=0x%p OriginalRequest=0x%p SrbExtension=0x%p cdb[0]=0x%X\n" , 24 | __FUNCTION__ , 25 | Srb->Function, 26 | Srb->SrbStatus, 27 | Srb->ScsiStatus, 28 | Srb->PathId,Srb->TargetId,Srb->Lun, 29 | Srb->QueueTag, 30 | Srb->QueueAction, 31 | Srb->CdbLength, 32 | Srb->SenseInfoBufferLength, 33 | Srb->SrbFlags, 34 | Srb->DataTransferLength, 35 | Srb->TimeOutValue, 36 | Srb->DataBuffer, 37 | Srb->SenseInfoBuffer, 38 | Srb->NextSrb, 39 | Srb->OriginalRequest, 40 | Srb->SrbExtension, 41 | Srb->Cdb[0]);*/ 42 | 43 | // 0x01 44 | if ( SRB_FUNCTION_CLAIM_DEVICE == Srb->Function ) 45 | { 46 | if (!Ext->Claimed ) 47 | { 48 | Ext->Claimed = TRUE; 49 | Srb->DataBuffer = DeviceObject; 50 | } 51 | else 52 | { 53 | //Status = STATUS_DEVICE_BUSY; 54 | } 55 | } 56 | // 0x06 57 | else if ( SRB_FUNCTION_RELEASE_DEVICE == Srb->Function ) 58 | { 59 | if ( Ext->Claimed ) 60 | Ext->Claimed = FALSE; 61 | else 62 | Status = STATUS_UNSUCCESSFUL; 63 | } 64 | // 0x00 65 | else if ( SRB_FUNCTION_EXECUTE_SCSI == Srb->Function ) 66 | { 67 | Status = ProcessCommand( Irp , Ext ); 68 | if ( Status == STATUS_PENDING ) 69 | return Status; 70 | } 71 | // 0x02 72 | else if ( SRB_FUNCTION_IO_CONTROL == Srb->Function ) 73 | { 74 | PSRB_IO_CONTROL IoCtl; 75 | PSENDCMDOUTPARAMS OutParms; 76 | PSENDCMDINPARAMS InParms; 77 | IoCtl = Srb->DataBuffer; 78 | 79 | if ( IOCTL_SCSI_MINIPORT_IDENTIFY == IoCtl->ControlCode) 80 | { 81 | int i; 82 | InParms = (PSENDCMDINPARAMS)((PUCHAR)IoCtl + IoCtl->HeaderLength); 83 | DbgPrint( 84 | "SENDCMDINPARAMS cBufferSize=0x%X irDriveRegs=(0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X) bDriveNumber=0x%X\n" , 85 | InParms->cBufferSize, 86 | InParms->irDriveRegs.bFeaturesReg, 87 | InParms->irDriveRegs.bSectorCountReg, 88 | InParms->irDriveRegs.bSectorNumberReg, 89 | InParms->irDriveRegs.bCylLowReg, 90 | InParms->irDriveRegs.bCylHighReg, 91 | InParms->irDriveRegs.bDriveHeadReg, 92 | InParms->irDriveRegs.bCommandReg, 93 | InParms->bDriveNumber); 94 | 95 | OutParms = (PSENDCMDOUTPARAMS)((PUCHAR)IoCtl + IoCtl->HeaderLength); 96 | DbgPrint("SENDCMDOUTPARAMS cBufferSize=0x%X\n" , OutParms->cBufferSize); 97 | //for Buffer structure refer to ATA-3 p49 98 | // 99 | OutParms->bBuffer[0] = 0x80; 100 | OutParms->bBuffer[1] = 0x40; 101 | // 102 | OutParms->bBuffer[2] = 1; //Num of logical cylinders 103 | OutParms->bBuffer[6] = 1; //Num of logical heads 104 | 105 | //10-19 Serial Number 106 | RtlCopyMemory( &OutParms->bBuffer[20] , "uSCSIDemo1234567890" , 19); 107 | // Model number 108 | for ( i = 54 ; i < 92 ; i++ ) 109 | OutParms->bBuffer[i] = 'M'; 110 | //Command set supported 111 | OutParms->bBuffer[164] = 0x0; //do not support 112 | OutParms->DriverStatus.bDriverError = (UCHAR)SMART_NO_ERROR; 113 | } 114 | 115 | else if ( IOCTL_SCSI_MINIPORT_ENABLE_SMART == IoCtl->ControlCode ) 116 | { 117 | DbgPrint("IOCTL_SCSI_MINIPORT_ENABLE_SMART\n"); 118 | } 119 | 120 | else if ( IOCTL_SCSI_MINIPORT_RETURN_STATUS == IoCtl->ControlCode ) 121 | { 122 | DbgPrint("IOCTL_SCSI_MINIPORT_RETURN_STATUS\n"); 123 | } 124 | 125 | else 126 | { 127 | DbgPrint("*** Unprocessed SRB_FUNCTION_IO_CONTROL HeaderLength=0x%X Signature=%c%c%c%c%c%c%c%c Timeout=0x%X ControlCode=0x%X ReturnCode=0x%X Length=0x%X\n", 128 | IoCtl->HeaderLength, 129 | IoCtl->Signature[0],IoCtl->Signature[1],IoCtl->Signature[2],IoCtl->Signature[3], 130 | IoCtl->Signature[4],IoCtl->Signature[5],IoCtl->Signature[6],IoCtl->Signature[7], 131 | IoCtl->Timeout , 132 | IoCtl->ControlCode, 133 | IoCtl->ReturnCode, 134 | IoCtl->Length); 135 | } 136 | Irp->IoStatus.Information = 0; 137 | } 138 | // 0x07 139 | else if ( SRB_FUNCTION_SHUTDOWN == Srb->Function ) 140 | { 141 | DbgPrint("TODO SRB_FUNCTION_SHUTDOWN\n"); 142 | } 143 | //0x08 144 | else if ( SRB_FUNCTION_FLUSH == Srb->Function ) 145 | { 146 | DbgPrint("TODO SRB_FUNCTION_FLUSH\n"); 147 | } 148 | else 149 | DbgPrint("%s **** Unprocessed Srb function=0x%X CdbLength=0x%X\n" , 150 | __FUNCTION__ , Srb->Function , Srb->CdbLength); 151 | 152 | Srb->SrbStatus = SRB_STATUS_SUCCESS; 153 | 154 | Irp->IoStatus.Status = Status; 155 | IoCompleteRequest ( Irp , IO_NO_INCREMENT ); 156 | return Status; 157 | } -------------------------------------------------------------------------------- /uSCSI/uSCSI.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | // 5 | // 6 | // 7 | 8 | NTSTATUS uAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo) 9 | { 10 | NTSTATUS Status; 11 | PDEVICE_OBJECT DevFdo; 12 | PFDO_EXT FdoExt; 13 | 14 | Status = IoCreateDevice(DriverObject, 15 | sizeof(FDO_EXT) , //user size 16 | NULL, //Name 17 | FILE_DEVICE_BUS_EXTENDER, //Device Type 18 | FILE_DEVICE_SECURE_OPEN| 19 | FILE_AUTOGENERATED_DEVICE_NAME, //Characteristics 20 | TRUE, //Exclusive 21 | &DevFdo); 22 | 23 | if ( NT_ERROR( Status ) ) 24 | goto ErrorOut; 25 | 26 | FdoExt = DevFdo->DeviceExtension; 27 | 28 | FdoExt->Self = DevFdo; 29 | FdoExt->IsFDO = TRUE; 30 | FdoExt->LowerDev = IoAttachDeviceToDeviceStack( DevFdo , Pdo ); 31 | 32 | FdoExt->PDOCount = 0; 33 | KeInitializeSpinLock ( &FdoExt->PDOLock ); 34 | InitializeListHead ( &FdoExt->PDOList ); 35 | 36 | Status = IoRegisterDeviceInterface( FdoExt->LowerDev , 37 | &USCSI_DISK_INTERFACE, 38 | NULL, 39 | &FdoExt->InterfaceName); 40 | if ( NT_ERROR( Status ) ) 41 | goto ErrorOut; 42 | 43 | DevFdo->Flags &= ~DO_DEVICE_INITIALIZING; 44 | 45 | return Status; 46 | 47 | ErrorOut: 48 | 49 | if ( DevFdo ) 50 | { 51 | if (FdoExt->LowerDev) 52 | IoDetachDevice (DevFdo); 53 | IoDeleteDevice (DevFdo); 54 | } 55 | return Status; 56 | } 57 | 58 | // 59 | // 60 | // 61 | 62 | NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) 63 | { 64 | NTSTATUS Status = STATUS_SUCCESS; 65 | 66 | DriverObject->DriverExtension->AddDevice = uAddDevice; 67 | 68 | DriverObject->MajorFunction[ IRP_MJ_READ ] = 69 | DriverObject->MajorFunction[ IRP_MJ_WRITE ] = uReadWrite; 70 | 71 | DriverObject->MajorFunction[ IRP_MJ_CREATE ] = 72 | DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = uCreateClose; 73 | 74 | DriverObject->MajorFunction[ IRP_MJ_SCSI ] = uScsi; 75 | 76 | DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = uFlush; 77 | DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL] = uIoCtl; 78 | DriverObject->MajorFunction[ IRP_MJ_PNP ] = uPnP; 79 | 80 | return Status; 81 | } -------------------------------------------------------------------------------- /uSCSI/uSCSI.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef USCSI_H 3 | #define USCSI_H 4 | 5 | #define USCSI_TAG (ULONG)'uSCI' 6 | 7 | #define FUNC_CODE(n) ((n&0x3FFC)>>2) 8 | 9 | // {BD97F566-02E4-4f6e-B29B-9B87E662A3E7} 10 | DEFINE_GUID(GUID_USCSI_BUS, 11 | 0xBD97F566,0x02E4,0x4f6e,0xB2,0x9B,0x9B,0x87,0xE6,0x62,0xA3,0xE7); 12 | 13 | #define HARDWAREID L"uSCSI\\Disk\0GenDisk\0\0" 14 | #define HARDWAREID_LEN sizeof (HARDWAREID) 15 | 16 | #define COMPATIBLEID HARDWAREID 17 | #define COMPATIBLEID_LEN HARDWAREID_LEN 18 | 19 | #define DEVICEDESC L"uSCSI Disk\0" 20 | #define DEVICEDESC_LEN sizeof(DEVICEDESC) 21 | 22 | #define DEVICEID L"uSCSI\\Disk\0" 23 | #define DEVICEID_LEN sizeof(DEVICEID) 24 | 25 | #define DEVICELOC L"uSCSI at cloud end\0" 26 | #define DEVICELOC_LEN sizeof (DEVICELOC) 27 | 28 | // 29 | // 30 | // 31 | #define DBG_SRB 32 | #define DBG_PNP 33 | #define DBG_IOCTL 34 | 35 | #define TMP_Debug( c ) \ 36 | DbgPrint c 37 | 38 | #ifdef DBG_USCSI 39 | #define USCSI_Debug( c ) \ 40 | DbgPrint c 41 | #else 42 | #define USCSI_Debug( c ) 43 | #endif 44 | 45 | #ifdef DBG_SRB 46 | #define SRB_Debug( c ) \ 47 | DbgPrint c 48 | #else 49 | #define SRB_Debug( c ) 50 | #endif 51 | 52 | #ifdef DBG_PNP 53 | #define PNP_Debug( c ) \ 54 | DbgPrint c 55 | #else 56 | #define PNP_Debug( c ) 57 | #endif 58 | 59 | #ifdef DBG_IOCTL 60 | #define IOCTL_Debug( c ) \ 61 | DbgPrint c 62 | #else 63 | #define IOCTL_Debug( c ) 64 | #endif 65 | 66 | #define DPFLTR_USCSI DPFLTR_IHVDRIVER_ID 67 | 68 | #define DBG_USCSI 0x1f 69 | 70 | typedef struct _COMMON_EXT 71 | { 72 | BOOLEAN IsFDO; 73 | 74 | PDEVICE_OBJECT Self; 75 | PDEVICE_OBJECT LowerDev; 76 | 77 | }COMMON_EXT , *PCOMMON_EXT; 78 | 79 | typedef struct _FDO_EXT 80 | { 81 | COMMON_EXT; 82 | 83 | UNICODE_STRING InterfaceName; 84 | 85 | ULONG PDOCount; 86 | KSPIN_LOCK PDOLock; 87 | LIST_ENTRY PDOList; 88 | 89 | }FDO_EXT , *PFDO_EXT; 90 | 91 | typedef struct _PDO_EXT 92 | { 93 | COMMON_EXT; 94 | 95 | LIST_ENTRY PDOList; 96 | 97 | BOOLEAN Claimed; 98 | BOOLEAN Reported; 99 | 100 | PVOID Session; 101 | PUCHAR Target; 102 | 103 | PAGED_LOOKASIDE_LIST CmdLookAside; 104 | 105 | KSPIN_LOCK CmdQueueLock; 106 | LIST_ENTRY CmdQueue; 107 | 108 | }PDO_EXT , *PPDO_EXT; 109 | 110 | typedef struct _SCSICmd 111 | { 112 | LIST_ENTRY List; 113 | PIRP Irp; 114 | ULONG Handle; 115 | }SCSICmd , *PSCSICmd; 116 | // 117 | // uSCSI.c 118 | // 119 | DRIVER_INITIALIZE DriverEntry; 120 | NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath); 121 | DRIVER_ADD_DEVICE uAddDevice; 122 | NTSTATUS uAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo); 123 | // 124 | // IoCtl.c 125 | // 126 | __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH uIoCtl; 127 | NTSTATUS uIoCtl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 128 | 129 | // 130 | // Pnp.c 131 | // 132 | __drv_dispatchType(IRP_MJ_PNP) DRIVER_DISPATCH uPnP; 133 | NTSTATUS uPnP (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 134 | // 135 | // Utils.c 136 | // 137 | IO_COMPLETION_ROUTINE CompletionRoutine; 138 | NTSTATUS CompletionRoutine (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); 139 | NTSTATUS SendIrpSynchronously (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 140 | NTSTATUS ProcessCommand (PIRP Irp , PPDO_EXT Ext); 141 | 142 | // 143 | // ReadWrite.c 144 | __drv_dispatchType(IRP_MJ_READ) DRIVER_DISPATCH uReadWrite; 145 | __drv_dispatchType(IRP_MJ_WRITE) DRIVER_DISPATCH uReadWrite; 146 | NTSTATUS uReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 147 | 148 | __drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH uCreateClose; 149 | NTSTATUS uCreateClose (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 150 | 151 | __drv_dispatchType(IRP_MJ_FLUSH_BUFFERS) DRIVER_DISPATCH uFlush; 152 | NTSTATUS uFlush (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); 153 | 154 | // 155 | // Srb.c 156 | __drv_dispatchType(IRP_MJ_SCSI) DRIVER_DISPATCH uScsi; 157 | NTSTATUS uScsi (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp); 158 | #endif 159 | -------------------------------------------------------------------------------- /uSCSIPort/Cmd.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | PPDU PiAllocateDataOutPDU( 5 | PuCONINFO ConInfo , PUCHAR DataBuf , ULONG Offset, ULONG DataSize,PULONG DataSN, PPDU RefCmd); 6 | 7 | VOID PiDequeuePendingTask(PuCONINFO ConInfo , PPDU Pdu); 8 | 9 | __inline VOID PiSendDataOut(PuCON_CTX Ctx , PPDU CmdOrR2T ); 10 | 11 | /* 12 | Process Immediate data and/or the first trunk of unsolicited data 13 | 14 | Parms: 15 | ConInfo : Connection 16 | DataBuf : Read buffer / Write Buffer / RW Buffer 17 | WriteSize : 18 | ReadSize : 19 | TaskAttr : 20 | Direction : SCSI_CMD_DIR_READ 21 | SCSI_CMD_DIR_WRITE 22 | SCSI_CMD_DIR_BOTH 23 | SCSI_CMD_DIR_NONE 24 | Return: 25 | Command PDU 26 | */ 27 | PPDU PiAllocateCmdPDU ( 28 | PuCONINFO ConInfo , PUCHAR DataBuf , ULONG WriteSize , ULONG ReadSize , UCHAR TaskAttr , 29 | UCHAR Direction) 30 | { 31 | PPDU Cmd , DataOut; 32 | PSESSION Session; 33 | ULONG FirstBurstLength; 34 | ULONG MaxRecvDataSegmentLength ; 35 | ULONG ExpectedDataTansferLength , DataLen , Remaining ; 36 | 37 | FOUR_BYTE ITT; 38 | 39 | Session = ConInfo->Session; 40 | 41 | MaxRecvDataSegmentLength = KEY_TV(KV(ConInfo,KEY_MaxRecvDataSegmentLength)).Number; 42 | FirstBurstLength = KEY_FV(KV(ConInfo,KEY_FirstBurstLength)).Number; 43 | 44 | switch ( Direction ) 45 | { 46 | // Read only 47 | case SCSI_CMD_DIR_READ: 48 | Cmd = TpAllocatePDU (ConInfo->ConCtx , OP_SCSI_CMD , 0 , 0 ); 49 | Cmd->Bhs->SCSI_CMD.Read = 1; 50 | ExpectedDataTansferLength = ReadSize; 51 | break; 52 | // Write only 53 | case SCSI_CMD_DIR_WRITE: 54 | Cmd = TpAllocatePDU (ConInfo->ConCtx , OP_SCSI_CMD , 0 , 0 ); 55 | Cmd->Bhs->SCSI_CMD.Write = 1; 56 | ExpectedDataTansferLength = WriteSize; 57 | break; 58 | // Read Write 59 | case SCSI_CMD_DIR_BOTH: 60 | Cmd = TpAllocatePDU (ConInfo->ConCtx , OP_SCSI_CMD , 8 , 0 ); 61 | Cmd->Bhs->SCSI_CMD.Read = 1; 62 | Cmd->Bhs->SCSI_CMD.Write = 1; 63 | ExpectedDataTansferLength = WriteSize; 64 | break; 65 | // With no Read/Write 66 | case SCSI_CMD_DIR_NONE: 67 | Cmd = TpAllocatePDU (ConInfo->ConCtx , OP_SCSI_CMD , 0 , 0 ); 68 | ExpectedDataTansferLength = 0; 69 | break; 70 | } 71 | // 72 | ITT.AsULong = InterlockedIncrement(&Session->TaskTag); 73 | REVERSE_BYTES ( &Cmd->Bhs->SCSI_CMD.InitiatorTaskTag , &ITT); 74 | 75 | Cmd->Bhs->SCSI_CMD.Attr = TaskAttr; 76 | 77 | //Write to / Read from this Buffer 78 | if (Direction != SCSI_CMD_DIR_NONE) 79 | Cmd->DataBuffer = DataBuf; 80 | 81 | /* 82 | For unidirectional operations, the Expected Data Transfer Length 83 | field contains the number of bytes of data involved in this SCSI 84 | operation. For a unidirectional write operation (W flag set to 1 and 85 | R flag set to 0), the initiator uses this field to specify the number 86 | of bytes of data it expects to transfer for this operation. For a 87 | unidirectional read operation (W flag set to 0 and R flag set to 1), 88 | the initiator uses this field to specify the number of bytes of data 89 | it expects the target to transfer to the initiator. It corresponds 90 | to the SAM2 byte count. 91 | */ 92 | 93 | REVERSE_BYTES ( &Cmd->Bhs->SCSI_CMD.ExpectedDataTansferLength , &ExpectedDataTansferLength); 94 | // 95 | if ( Direction == SCSI_CMD_DIR_WRITE || Direction == SCSI_CMD_DIR_BOTH ) 96 | { 97 | if ( KEY_FV(KV(ConInfo , KEY_ImmediateData)).Bool ) 98 | { 99 | // Immediate data 100 | DataLen = min ( FirstBurstLength , WriteSize ); 101 | DataLen = min ( DataLen , MaxRecvDataSegmentLength ); 102 | 103 | REVERSE_3BYTES ( &Cmd->Bhs->SCSI_CMD.DataSegmentLength , &DataLen ); 104 | 105 | Cmd->Data = DataBuf; 106 | 107 | if ( ( DataLen < WriteSize) && 108 | ( MaxRecvDataSegmentLength < FirstBurstLength) && 109 | !( KEY_FV(KV(ConInfo , KEY_InitialR2T)).Bool) ) 110 | { 111 | // More Data in Data-Out PDU 112 | Cmd->Bhs->SCSI_CMD.Final = 0; 113 | 114 | Remaining = WriteSize - DataLen; 115 | Remaining = min (Remaining , (FirstBurstLength - DataLen)); 116 | 117 | DataOut = PiAllocateDataOutPDU ( ConInfo , 118 | DataBuf , 119 | DataLen , 120 | Remaining , 121 | &Cmd->DataSN, 122 | NULL ); 123 | 124 | InsertTailList ( &Cmd->DataOut , &DataOut->DataOut ); 125 | } 126 | else 127 | // No More Data 128 | Cmd->Bhs->SCSI_CMD.Final = 1; 129 | } 130 | else 131 | { 132 | if ( KEY_FV(KV(ConInfo , KEY_InitialR2T)).Bool ) 133 | // No Immediate data , No Unsolicited data 134 | // just wait for the R2T 135 | Cmd->Bhs->SCSI_CMD.Final = 1; 136 | else 137 | { 138 | // No Immediate data , But Unsolicited data 139 | Cmd->Bhs->SCSI_CMD.Final = 0; 140 | Remaining = min ( WriteSize , FirstBurstLength ); 141 | 142 | DataOut = PiAllocateDataOutPDU ( ConInfo , 143 | DataBuf , 144 | 0 , 145 | Remaining , 146 | &Cmd->DataSN, 147 | Cmd ); 148 | 149 | InsertTailList ( &Cmd->DataOut , &DataOut->DataOut ); 150 | } 151 | } 152 | } 153 | 154 | if ( Direction == SCSI_CMD_DIR_READ || Direction == SCSI_CMD_DIR_NONE ) 155 | { 156 | /* 157 | Setting both the W and the F bit to 0 is an error 158 | 159 | ***for read only command F bit MUST be set 160 | */ 161 | Cmd->Bhs->SCSI_CMD.Final = 1; 162 | } 163 | 164 | if ( Direction == SCSI_CMD_DIR_BOTH ) 165 | { 166 | PAHS Ahs; 167 | TWO_BYTE AhsLen; 168 | 169 | Ahs = (PAHS)Cmd->Ahs; 170 | 171 | AhsLen.AsUShort = 0x5; 172 | REVERSE_BYTES_SHORT ( &Ahs->AHSLength , &AhsLen ); 173 | 174 | Ahs->AHSType = AHS_TYPE_BIDIR_READ_LENGTH; 175 | 176 | REVERSE_BYTES ( &Ahs->AHS_spec[1] , &ReadSize); 177 | } 178 | /* 179 | DbgPrint("%s Direction=0x%X ITT=0x%X Data=0x%p\n" , 180 | __FUNCTION__ , Direction , ITT.AsULong , Cmd->Data); 181 | */ 182 | return Cmd; 183 | } 184 | 185 | // 186 | // 187 | // 188 | 189 | BOOLEAN PiCheckCmdComplete( PPDU Cmd ) 190 | { 191 | KIRQL OldIrql; 192 | BOOLEAN CanComplete = FALSE; 193 | PLIST_ENTRY DataInEnt,DataInEnt2; 194 | PPDU DataIn; 195 | 196 | if ( !(Cmd->Flags & PDU_F_CMD_CAN_COMPLETE) ) 197 | goto Out; 198 | // 199 | // Check if all transfered 200 | // 201 | KeAcquireSpinLock( &Cmd->DataInOrR2TLock , &OldIrql); 202 | CanComplete = TRUE; 203 | DataInEnt = &Cmd->DataInOrR2T; 204 | DataInEnt2 = DataInEnt->Flink; 205 | 206 | while ( DataInEnt2 != DataInEnt ) 207 | { 208 | DataIn = CONTAINING_RECORD ( DataInEnt2 , PDU , DataInOrR2T ); 209 | if ( !(DataIn->Flags & PDU_F_DATA_IN_PROCESSED) ) 210 | { 211 | CanComplete = FALSE; 212 | break; 213 | } 214 | DataInEnt2 = DataInEnt2->Flink; 215 | } 216 | 217 | KeReleaseSpinLock( &Cmd->DataInOrR2TLock ,OldIrql); 218 | 219 | Out: 220 | return CanComplete; 221 | } 222 | 223 | /* 224 | Pending command completion worker 225 | 226 | Parms: 227 | Parameter : Thread context 228 | */ 229 | VOID PiPendingCompleteCmdWorker (IN PVOID Parameter) 230 | { 231 | NTSTATUS Status; 232 | PWORKER_THREAD_CTX Tctx; 233 | PSESSION Session; 234 | PuCONINFO ConInfo; 235 | PuCON_CTX Ctx; 236 | PPDU Resps , Cmd , LastDataIn; 237 | PLIST_ENTRY CmdEnt , PduEnt; 238 | UCHAR Response = 0, ScsiStatus = 0; 239 | PUCHAR SenseData = NULL; 240 | FOUR_BYTE ITT , BRC , RC; 241 | THREE_BYTE DataSegmentLength; 242 | TWO_BYTE SenseLength; 243 | 244 | Status = STATUS_SUCCESS; 245 | Tctx = (PWORKER_THREAD_CTX)Parameter; 246 | Ctx = Tctx->Ctx; 247 | ConInfo = Ctx->ConInfo; 248 | Session = ConInfo->Session; 249 | 250 | SenseLength.AsUShort = 0; 251 | 252 | while (TRUE) 253 | { 254 | Status = 255 | KeWaitForSingleObject ( &Ctx->PendingCompleteCmdEvent, Executive, KernelMode, FALSE, NULL ); 256 | KeClearEvent( &Ctx->PendingCompleteCmdEvent ); 257 | 258 | while ( CmdEnt = ExInterlockedRemoveHeadList(&Ctx->PendingCompleteCmds, 259 | &Ctx->PendingCompleteCmdLock)) 260 | { 261 | Cmd = CONTAINING_RECORD ( CmdEnt , PDU , PendingCmd); 262 | InitializeListHead ( &Cmd->PendingCmd ); 263 | 264 | REVERSE_BYTES ( &ITT , &Cmd->Bhs->STAT_FIELDS.InitiatorTaskTag ); 265 | 266 | if ( Cmd->Bhs->GENERICBHS.Opcode == OP_SCSI_CMD && Cmd->Bhs->SCSI_CMD.Read ) 267 | { 268 | if ( !PiCheckCmdComplete(Cmd) ) 269 | { 270 | //Can't complete for now , requeue it to tail 271 | TpQueuePendingCompleteCmd ( Cmd->ConCtx , Cmd ); 272 | continue; 273 | } 274 | } 275 | 276 | if ( !IsListEmpty ( &Cmd->Resps )) 277 | { 278 | // 279 | // Process Repsonse 280 | // 281 | PduEnt = RemoveHeadList ( &Cmd->Resps ); 282 | Resps = CONTAINING_RECORD ( PduEnt , PDU , Resps ); 283 | InitializeListHead ( &Resps->Resps ); 284 | 285 | Response = Resps->Bhs->SCSI_RESPONSE.Response; 286 | ScsiStatus = Resps->Bhs->SCSI_RESPONSE.Status; 287 | 288 | KdPrintEx((DPFLTR_USCSI,DBG_PROTOCOL, 289 | "%s ITT 0x%X Resps 0x%p Response 0x%X Status 0x%X\n" , 290 | __FUNCTION__ , 291 | ITT.AsULong, 292 | Resps, 293 | Response , 294 | ScsiStatus)); 295 | 296 | // How to Process these residual counts ? 297 | REVERSE_BYTES ( &BRC , &Resps->Bhs->SCSI_RESPONSE.BiReadResidualCount); 298 | REVERSE_BYTES ( &RC , &Resps->Bhs->SCSI_RESPONSE.ResidualCount); 299 | // 300 | // Sense data available? 301 | // 302 | REVERSE_3BYTES (&DataSegmentLength , &Resps->Bhs->SCSI_RESPONSE.DataSegmentLength); 303 | 304 | if ( DataSegmentLength.AsULong ) 305 | { 306 | REVERSE_BYTES_SHORT ( &SenseLength, Resps->Data ); 307 | if (SenseLength.AsUShort) 308 | { 309 | SenseData = ExAllocatePoolWithTag ( PagedPool , SenseLength.AsUShort , USCSI_TAG); 310 | RtlCopyMemory ( SenseData , Resps->Data + 2 , SenseLength.AsUShort); 311 | } 312 | } 313 | 314 | TpQueuePduRelease( Ctx , Resps ); 315 | } 316 | else 317 | { 318 | LastDataIn = CONTAINING_RECORD ( Cmd->DataInOrR2T.Blink , PDU , DataInOrR2T ); 319 | ScsiStatus = LastDataIn->Bhs->SCSI_DATA_IN.Status; 320 | } 321 | 322 | (*Session->CallBack.CompleteCmd)( ITT.AsULong , 323 | Session->CallBack.CompleteCtx , 324 | ScsiStatus , 325 | SenseData , 326 | SenseLength.AsUShort ); 327 | if ( SenseData ) 328 | { 329 | ExFreePoolWithTag ( SenseData , USCSI_TAG); 330 | SenseData = NULL; 331 | } 332 | 333 | PiDequeuePendingTask ( ConInfo , Cmd ); 334 | TpQueuePduRelease( Ctx , Cmd ); 335 | } 336 | } 337 | 338 | ExFreePoolWithTag (Tctx , USCSI_TAG); 339 | } 340 | 341 | /* 342 | Allocate a iSCSI command PDU and assembly it 343 | 344 | parms: 345 | ConInfo : 346 | Cdb : 347 | CdbLength : 348 | DataBuffer : 349 | WriteSize : 350 | ReadSize : 351 | TaskAttr : 352 | Dir : 353 | 354 | return: 355 | iSCSI command PDU 356 | */ 357 | 358 | PPDU PtAssembleSCSICmd 359 | (PuCONINFO ConInfo, PUCHAR Cdb, ULONG CdbLength, PUCHAR DataBuffer, 360 | ULONG WriteSize, ULONG ReadSize, UCHAR TaskAttr, UCHAR Dir ) 361 | { 362 | PPDU Cmd; 363 | 364 | Cmd = PiAllocateCmdPDU ( ConInfo , DataBuffer , WriteSize , ReadSize , TaskAttr, Dir ); 365 | 366 | if( NULL != Cmd ) 367 | RtlCopyMemory ( Cmd->Bhs->SCSI_CMD.Cdb , Cdb , min (16, CdbLength) ); 368 | 369 | return Cmd; 370 | } -------------------------------------------------------------------------------- /uSCSIPort/Data.c: -------------------------------------------------------------------------------- 1 | #include "precomp.h" 2 | 3 | PPDU PiFindPendingTask(ULONG TaskTag , PuCONINFO ConInfo , BOOLEAN Remove); 4 | 5 | PPDU PiQueueDataInOrR2T(PuCONINFO ConInfo , PPDU Task , PPDU DataIn); 6 | 7 | // 8 | // 9 | // 10 | 11 | BOOLEAN PiCheckDataInHole ( PPDU Cmd ) 12 | { 13 | PLIST_ENTRY DataOrR2TEnt , DataOrR2TEnt2 ,DataOrR2TEnt3 , Last2ndEnt; 14 | PPDU DataOrR2T2 , DataOrR2T3; 15 | FOUR_BYTE DataSN2 , DataSN3; 16 | ULONG Gap = 0; 17 | BOOLEAN Hole = FALSE ; 18 | 19 | DataOrR2TEnt = &Cmd->DataInOrR2T; 20 | 21 | return FALSE; //Temp for test purpose 22 | 23 | if ( !Cmd->ExpDataSN ) 24 | return Hole; 25 | 26 | if ( DataOrR2TEnt->Flink == DataOrR2TEnt ) 27 | { 28 | // Zero 29 | while ( Gap < Cmd->ExpDataSN ) 30 | { 31 | Hole = TRUE; 32 | Cmd->Flags |= PDU_F_DATA_IN_RETRANSMIT; 33 | Gap++; 34 | } 35 | } 36 | 37 | else if ( DataOrR2TEnt->Flink->Flink == DataOrR2TEnt ) 38 | { 39 | // Just one 40 | DataOrR2TEnt2 = DataOrR2TEnt->Flink; 41 | DataOrR2T2 = CONTAINING_RECORD ( DataOrR2TEnt2 , PDU , DataInOrR2T); 42 | REVERSE_BYTES ( &DataSN2 , &DataOrR2T2->Bhs->STAT_FIELDS.DataSN ); 43 | 44 | if ( Cmd->ExpDataSN ) 45 | { 46 | Hole = TRUE; 47 | Cmd->Flags |= PDU_F_DATA_IN_RETRANSMIT; 48 | while ( Gap < DataSN2.AsULong ) 49 | { 50 | // 51 | Gap++; 52 | } 53 | 54 | Gap = DataSN2.AsULong + 1; 55 | while ( Gap <= Cmd->ExpDataSN ) 56 | { 57 | // 58 | Gap++; 59 | } 60 | } 61 | } 62 | 63 | else 64 | { 65 | // At least two 66 | Last2ndEnt = DataOrR2TEnt->Blink->Blink; 67 | DataOrR2TEnt2 = DataOrR2TEnt->Flink; 68 | DataOrR2TEnt3 = DataOrR2TEnt2->Flink; 69 | 70 | while ( TRUE ) 71 | { 72 | DataOrR2T2 = CONTAINING_RECORD ( DataOrR2TEnt2 , PDU , DataInOrR2T); 73 | DataOrR2T3 = CONTAINING_RECORD ( DataOrR2TEnt3 , PDU , DataInOrR2T); 74 | 75 | REVERSE_BYTES ( &DataSN2 , &DataOrR2T2->Bhs->STAT_FIELDS.DataSN); 76 | REVERSE_BYTES ( &DataSN3 , &DataOrR2T3->Bhs->STAT_FIELDS.DataSN); 77 | // 78 | // First DataSN MUST Start with 0 79 | // 80 | if ( DataOrR2TEnt2->Blink == DataOrR2TEnt && DataSN2.AsULong ) 81 | { 82 | Hole = TRUE; 83 | Cmd->Flags |= PDU_F_DATA_IN_RETRANSMIT; 84 | while ( Gap < DataSN2.AsULong ) 85 | { 86 | // Request the missing Data-In(s) 87 | Gap++; 88 | } 89 | } 90 | 91 | if ( DataSN2.AsULong + 1 != DataSN3.AsULong ) 92 | { 93 | Hole = TRUE; 94 | Cmd->Flags |= PDU_F_DATA_IN_RETRANSMIT; 95 | 96 | Gap = DataSN2.AsULong + 1; 97 | while ( Gap < DataSN3.AsULong ) 98 | { 99 | // Request the missing Data-In(s) 100 | Gap++; 101 | } 102 | } 103 | 104 | if ( DataOrR2TEnt2 == Last2ndEnt && DataSN3.AsULong != Cmd->ExpDataSN ) 105 | { 106 | Hole = TRUE; 107 | Cmd->Flags |= PDU_F_DATA_IN_RETRANSMIT; 108 | Gap = DataSN3.AsULong + 1; 109 | while ( Gap < Cmd->ExpDataSN ) 110 | { 111 | // 112 | Gap++; 113 | } 114 | break; 115 | } 116 | else 117 | { 118 | DataOrR2TEnt2 = DataOrR2TEnt2->Flink; 119 | DataOrR2TEnt3 = DataOrR2TEnt2->Flink; 120 | } 121 | } 122 | } 123 | 124 | return Hole; 125 | } 126 | 127 | /* 128 | ConCtx |-----------------|---------|--> Cmd1 129 | | | | | 130 | |_R2Ts - R2T1.2 - R2T2.2 - R2T1.4 - R2T1.5 - R2T2.3 131 | | | | 132 | | |--------------------------|-->Cmd2 133 | | 134 | | |--------|-----------------|--> Cmd1 135 | | | | | 136 | |_DataIns - Di1.1 - Di1.3 - Di2.1 - Di1.6 - Di2.4 137 | | | 138 | |------------------|-->Cmd2 139 | 140 | R2T / Data-In link in 2 queues 141 | 1. R2Ts or DataIns queue of connection by PDUList 142 | 2. SCSI Cmd's DataInOrR2T queue by DataInOrR2T 143 | 144 | R2Ts queue is served by R2TWorker 145 | DataIns queue is served by DataInWorker 146 | 147 | */ 148 | /* 149 | 1. Insert Data-in PDU into a Per-Cmd List , sorted by DataSN 150 | 2. Check PDU Final bit , if set , trigger Data-in PDUs process 151 | */ 152 | VOID PtProcessDataIn (PPDU Pdu, PuCONINFO ConInfo) 153 | { 154 | PPDU Cmd; 155 | PPDU DataAck; 156 | PuCON_CTX Ctx; 157 | FOUR_BYTE DataSN , DataSize , Offset , ITT; 158 | 159 | Ctx = ConInfo->ConCtx; 160 | 161 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->SCSI_DATA_IN.InitiatorTaskTag ); 162 | 163 | Cmd = PiFindPendingTask ( ITT.AsULong , ConInfo , FALSE); 164 | if ( !Cmd ) 165 | { 166 | DbgPrint( "No Matching Command for DataIn %x (ITT 0x%x)\n", Pdu , ITT.AsULong); 167 | TpQueuePduRelease( Ctx , Pdu ); 168 | goto Out; 169 | } 170 | 171 | Pdu->Cmd = Cmd; 172 | 173 | KdPrintEx((DPFLTR_USCSI , DBG_PROTOCOL , 174 | "%s ITT 0x%X Pdu 0x%p FSUO %d%d%d%d\n" , 175 | __FUNCTION__ , 176 | ITT.AsULong , 177 | Pdu, 178 | Pdu->Bhs->SCSI_DATA_IN.Final, 179 | Pdu->Bhs->SCSI_DATA_IN.S, 180 | Pdu->Bhs->SCSI_DATA_IN.U, 181 | Pdu->Bhs->SCSI_DATA_IN.O )); 182 | 183 | // Pdu maybe dropped by PiQueueDataInOrR2T 184 | // in which case PiQueueDataInOrR2T return NULL 185 | // else return the orignal Pdu 186 | 187 | Pdu = PiQueueDataInOrR2T ( ConInfo , Cmd , Pdu ); 188 | 189 | if ( !Pdu ) 190 | goto Out; 191 | 192 | // 193 | // Queue to Copy data 194 | // 195 | TpQueueDataInPDU ( Ctx , Pdu ); 196 | 197 | if (KEY_FV(KV(ConInfo,KEY_ErrorRecoveryLevel)).Number >0 && 198 | Pdu->Bhs->SCSI_DATA_IN.Ack) 199 | { 200 | /* 201 | The initiator MUST ignore the A bit set to 1 for sessions with 202 | ErrorRecoveryLevel=0. 203 | */ 204 | // Allocate a DataAck 205 | DataAck = TpAllocatePDU ( Ctx , OP_SNACK_REQ , 0 , 0 ); 206 | RtlCopyMemory ( &DataAck->Bhs->SNACK.TargetTransferTag, 207 | &Pdu->Bhs->SCSI_DATA_IN.TargetTransferTag, 208 | 4); 209 | //... 210 | TpQueueOutPDU (Ctx , DataAck); 211 | } 212 | 213 | if ( Pdu->Bhs->SCSI_DATA_IN.Final) 214 | { 215 | // Trigger Data Sequence checking 216 | /* 217 | Status can accompany the last Data-In PDU if the command did not end 218 | with an exception (i.e., the status is "good status" - GOOD, 219 | CONDITION MET or INTERMEDIATE CONDITION MET). The presence of status 220 | (and of a residual count) is signaled though the S flag bit. 221 | */ 222 | if ( Pdu->Bhs->SCSI_DATA_IN.S ) 223 | { 224 | REVERSE_BYTES ( &DataSN , &Pdu->Bhs->SCSI_DATA_IN.DataSN); 225 | Cmd->ExpDataSN = DataSN.AsULong; 226 | 227 | if ( !PiCheckDataInHole ( Cmd ) ) 228 | { 229 | //DbgPrint("CompleteCmd=0x%X\n",Cmd); 230 | Cmd->Flags |= PDU_F_CMD_CAN_COMPLETE; 231 | TpQueuePendingCompleteCmd ( Ctx , Cmd ); 232 | } 233 | } 234 | } 235 | else if ( (Cmd->Flags & PDU_F_DATA_IN_RETRANSMIT) && !PiCheckDataInHole ( Cmd ) ) 236 | { 237 | KdPrintEx((DPFLTR_USCSI , DBG_PROTOCOL ,"%s Debug\n" , __FUNCTION__ )); 238 | TpQueuePendingCompleteCmd ( ConInfo->ConCtx , Cmd ); 239 | } 240 | 241 | Out: 242 | 243 | return; 244 | } 245 | /* 246 | Insert Data-in / R2T PDU sorted by DataSN 247 | Parms 248 | Cmd : to which DataInOrR2T belong 249 | DataInOrR2T : Data-in / R2T 250 | Return 251 | NULL , if this is a duplicated Data-In 252 | DataIn , the original DataIn 253 | */ 254 | PPDU PiQueueDataInOrR2T(PuCONINFO ConInfo, PPDU Cmd, PPDU DataInOrR2T) 255 | { 256 | KIRQL OldIrql; 257 | PLIST_ENTRY DataInEnt , DataInEnt2; 258 | FOUR_BYTE Num , Num2; 259 | PPDU DataIn2; 260 | 261 | KeAcquireSpinLock( &Cmd->DataInOrR2TLock, &OldIrql); 262 | DataInEnt = &Cmd->DataInOrR2T; 263 | REVERSE_BYTES ( &Num , &DataInOrR2T->Bhs->STAT_FIELDS.DataSN); 264 | 265 | DataInEnt2 = DataInEnt->Flink; 266 | 267 | while ( DataInEnt2 != DataInEnt ) 268 | { 269 | DataIn2 = CONTAINING_RECORD ( DataInEnt2 , PDU , DataInOrR2T ); 270 | 271 | REVERSE_BYTES ( &Num2 , &DataIn2->Bhs->STAT_FIELDS.DataSN ); 272 | 273 | if ( Num2.AsULong < Num.AsULong ) 274 | break; 275 | else if ( Num2.AsULong == Num.AsULong) 276 | { 277 | KeReleaseSpinLock( &Cmd->DataInOrR2TLock, OldIrql); 278 | // Duplicated Data-In 279 | // Drop it? 280 | DbgPrint("Duplicated DataIn=0x%X Droped\n",DataInOrR2T); 281 | Cmd->DupDataInCount++; 282 | TpQueuePduRelease( ConInfo->ConCtx , DataInOrR2T ); 283 | return NULL; 284 | } 285 | DataInEnt2 = DataInEnt2->Flink; 286 | } 287 | 288 | DataInEnt = &DataInOrR2T->DataInOrR2T; 289 | 290 | InsertHeadList ( DataInEnt2 , DataInEnt); 291 | KeReleaseSpinLock( &Cmd->DataInOrR2TLock, OldIrql); 292 | 293 | return DataInOrR2T; 294 | } 295 | 296 | // 297 | // 298 | // 299 | 300 | VOID PiDataInWorker(IN PVOID Parameter) 301 | { 302 | NTSTATUS Status; 303 | PWORKER_THREAD_CTX Tctx; 304 | PSESSION Session; 305 | PuCONINFO ConInfo; 306 | PuCON_CTX Ctx; 307 | PLIST_ENTRY DataInEnt , DataInEnt2; 308 | PPDU DataIn , Cmd; 309 | ULONG BufferOffset; 310 | THREE_BYTE DataSegmentLength; 311 | FOUR_BYTE ITT; 312 | BOOLEAN AllTransfered; 313 | 314 | Status = STATUS_SUCCESS; 315 | Tctx = (PWORKER_THREAD_CTX)Parameter; 316 | Ctx = Tctx->Ctx; 317 | ConInfo = Ctx->ConInfo; 318 | Session = ConInfo->Session; 319 | 320 | while (TRUE) 321 | { 322 | Status = 323 | KeWaitForSingleObject (&Ctx->DataInEvent, Executive, KernelMode, FALSE, NULL); 324 | KeClearEvent( &Ctx->DataInEvent ); 325 | 326 | while ( DataInEnt = ExInterlockedRemoveHeadList ( &Ctx->DataIns , &Ctx->DataInLock ) ) 327 | { 328 | DataIn = CONTAINING_RECORD ( DataInEnt , PDU , PDUList ); 329 | InitializeListHead ( &DataIn->PDUList); 330 | 331 | Cmd = DataIn->Cmd; 332 | /* 333 | { 334 | LARGE_INTEGER Tick; 335 | KeQueryTickCount( &Tick); 336 | DbgPrint("%s Time=0x%X-%X Cmd=0x%X Pdu=0x%X Bhs=0x%X\n" , 337 | __FUNCTION__ , Tick.HighPart,Tick.LowPart , Cmd , DataIn,DataIn->Bhs); 338 | }*/ 339 | REVERSE_BYTES ( &ITT , &DataIn->Bhs->SCSI_DATA_IN.InitiatorTaskTag); 340 | REVERSE_3BYTES ( &DataSegmentLength , &DataIn->Bhs->SCSI_DATA_IN.DataSegmentLength); 341 | REVERSE_BYTES ( &BufferOffset , &DataIn->Bhs->SCSI_DATA_IN.BufferOffset ); 342 | /* 343 | DbgPrint( 344 | "%s Cmd=0x%X ITT=0x%X DataBuffer=0x%p Offset=0x%X Length=0x%X Data=0x%X S=0x%X Final=0x%X\n", 345 | __FUNCTION__, 346 | Cmd, 347 | ITT.AsULong, 348 | Cmd->DataBuffer, 349 | BufferOffset, 350 | DataSegmentLength.AsULong, 351 | DataIn->Data, 352 | DataIn->Bhs->SCSI_DATA_IN.S, 353 | DataIn->Bhs->SCSI_DATA_IN.Final);*/ 354 | // Copy Data 355 | if (Cmd->DataBuffer) 356 | RtlCopyMemory( Cmd->DataBuffer + BufferOffset, 357 | DataIn->Data, 358 | DataSegmentLength.AsULong ); 359 | 360 | DataIn->Flags |= PDU_F_DATA_IN_PROCESSED; 361 | } 362 | } 363 | 364 | ExFreePoolWithTag (Tctx , USCSI_TAG); 365 | } 366 | 367 | // 368 | // DataBuf : Data Buffer 369 | // Offset : Offset 370 | // DataSize: Data size count from Offset 371 | // for Unsolicited data , DataSize range is (0 , FirstBurstLength] 372 | // for Solicited data , DataSize range is (0 , MaxBurstLength] 373 | /* 374 | ---------- 375 | /|\ 376 | | 377 | | 378 | DataSize 379 | | 380 | | 381 | \|/ 382 | ---------- <- Offset 383 | /|\ 384 | | 385 | | 386 | \|/ 387 | ---------- <- DataBuf 388 | 389 | */ 390 | 391 | PPDU PiAllocateDataOutPDU( 392 | PuCONINFO ConInfo, PUCHAR DataBuf, ULONG Offset, ULONG DataSize, PULONG DataSN, PPDU RefCmd) 393 | { 394 | ULONG DataLen , MaxRecvDataSegmentLength; 395 | PPDU DataOut = NULL , Tmp ; 396 | PLIST_ENTRY Ent; 397 | 398 | MaxRecvDataSegmentLength = KEY_TV(KV(ConInfo,KEY_MaxRecvDataSegmentLength)).Number; 399 | 400 | do 401 | { 402 | DataLen = min (DataSize , MaxRecvDataSegmentLength); 403 | 404 | Tmp = TpAllocatePDU ( ConInfo->ConCtx , OP_DATA_OUT , 0 , 0 ); 405 | 406 | if ( !Tmp ) 407 | goto ErrorOut; 408 | else if ( DataOut ) 409 | InsertTailList ( &DataOut->DataOut , &Tmp->DataOut ); 410 | else 411 | DataOut = Tmp; 412 | 413 | Tmp->Bhs->SCSI_DATA_OUT.Final = 0; 414 | 415 | REVERSE_3BYTES ( &Tmp->Bhs->SCSI_DATA_OUT.DataSegmentLength , &DataLen ); 416 | REVERSE_BYTES ( &Tmp->Bhs->SCSI_DATA_OUT.BufferOffset , &Offset); 417 | 418 | *DataSN++; 419 | REVERSE_BYTES ( &Tmp->Bhs->SCSI_DATA_OUT.DataSN , DataSN); 420 | 421 | RtlCopyMemory ( &Tmp->Bhs->SCSI_DATA_OUT.InitiatorTaskTag , 422 | &RefCmd->Bhs->GENERICBHS.InitiatorTaskTag , 423 | 4); 424 | 425 | if ( RefCmd->Bhs->GENERICBHS.Opcode == OP_R2T ) 426 | RtlCopyMemory ( &Tmp->Bhs->SCSI_DATA_OUT.TargetTansferTag , 427 | &RefCmd->Bhs->R2T.TargetTansferTag , 428 | 4); 429 | 430 | Tmp->Data = DataBuf + Offset; 431 | 432 | Offset += DataLen; 433 | DataSize -= DataLen; // DataSize will NEVER < 0 434 | 435 | }while ( DataSize > 0); 436 | 437 | Tmp->Bhs->SCSI_DATA_OUT.Final = 1; 438 | 439 | return DataOut; 440 | 441 | ErrorOut: 442 | 443 | if (DataOut) 444 | //TpFreePDU( ConInfo->ConCtx , DataOut , 0 ); 445 | TpQueuePduRelease( ConInfo->ConCtx , DataOut ); 446 | 447 | return NULL; 448 | } 449 | /* 450 | Send DataOut Queue of command 451 | if this is a SCSI command with unsolicited data 452 | SCSI command MUST be sent first 453 | */ 454 | VOID PiSendDataOut(PuCON_CTX Ctx , PPDU CmdOrR2T ) 455 | { 456 | PPDU DataOut; 457 | PLIST_ENTRY PduEnt , PduEnt2; 458 | 459 | PduEnt = &CmdOrR2T->DataOut; 460 | PduEnt2 = PduEnt->Flink; 461 | 462 | while ( PduEnt2 != PduEnt ) 463 | { 464 | DataOut = CONTAINING_RECORD ( PduEnt2 , PDU , DataOut ); 465 | TpQueueOutPDU( Ctx , DataOut ); 466 | PduEnt2 = PduEnt2->Flink; 467 | } 468 | } 469 | 470 | -------------------------------------------------------------------------------- /uSCSIPort/Discover.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | -------------------------------------------------------------------------------- /uSCSIPort/Handler.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | //Key Handlers 5 | 6 | VOID PiUpdateKeyNegoState ( KEY_NAME Key , PuCONINFO ConInfo , PLIST_ENTRY Answers); 7 | ULONG PiGetKeyState ( KEY_NAME Key , PuCONINFO ConInfo ); 8 | VOID PiSetKeyState ( KEY_NAME Key , PuCONINFO ConInfo , UCHAR State); 9 | PKEY_VALUE PiFormatKeyValue (PKEY_VALUE Val); 10 | 11 | VOID PiHandleReject ( 12 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 13 | { 14 | KEY_NAME Key; 15 | Key = KeyVal->Key.D; 16 | 17 | PROT_Debug(("%s Reject\n" , PiKeyName(Key))); 18 | // Assert ( PiGetKeyState(KeyVal->Key.D , ConInfo) == KEY_Proposed ) 19 | KV(ConInfo , Key)->Flags |= KEY_Reject; 20 | 21 | PiSetKeyState ( Key , ConInfo , KEY_Defaulted ); 22 | } 23 | 24 | VOID PiHandleIrrelevant ( 25 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 26 | { 27 | KEY_NAME Key; 28 | Key = KeyVal->Key.D; 29 | 30 | PROT_Debug(("%s Irrelevant\n" , PiKeyName(Key))); 31 | // Assert ( PiGetKeyState(KeyVal->Key.D , ConInfo) == KEY_Proposed ) 32 | KV(ConInfo,Key)->Flags |= KEY_Irrelevant; 33 | PiSetKeyState ( Key , ConInfo , KEY_Defaulted ); 34 | } 35 | 36 | VOID PiHandleNone ( 37 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 38 | { 39 | PSESSION Session; 40 | KEY_NAME Key; 41 | Session = ConInfo->Session; 42 | Key = KeyVal->Key.D; 43 | 44 | PROT_Debug(("%s None\n" , PiKeyName(Key))); 45 | 46 | if ( PiKeys[Key].Attrs & KEY_TYPE_LOV ) 47 | { 48 | KV(ConInfo , Key)->Flags |= KEY_None; 49 | PiUpdateKeyNegoState ( Key , ConInfo , Answers ); 50 | //PiSetKeyState ( Key , ConInfo , KEY_Agreed ); 51 | } 52 | else 53 | { 54 | PROT_Debug(("%s Protocol Error\n" , __FUNCTION__)); 55 | PiSetKeyState ( Key , ConInfo , KEY_Defaulted ); 56 | } 57 | } 58 | 59 | VOID PiHandleNotUnderstood ( 60 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 61 | { 62 | KEY_NAME Key; 63 | Key = KeyVal->Key.D; 64 | 65 | PROT_Debug(("%s NotUnderstood\n" ,KeyVal->Key.U , KEY_FV(KeyVal).String)); 66 | /* 67 | KV(ConInfo , Key)->Flags |= KEY_NotUnderstood; 68 | 69 | PiSetKeyState ( Key , ConInfo , KEY_Defaulted );*/ 70 | } 71 | 72 | 73 | 74 | VOID PiHandleIllegal ( 75 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 76 | { 77 | PKEY_VALUE Kv; 78 | KEY_VALUE Kv2 = {0}; 79 | KEY_NAME Key; 80 | 81 | Key = KeyVal->Key.D; 82 | PROT_Debug(("%s Illegal\n" , PiKeyName(Key))); 83 | //Mark Reject 84 | KeyVal->Flags &= ~KEY_Illegal; 85 | KeyVal->Flags |= KEY_Reject; 86 | 87 | Kv = PiFormatKeyValue ( KeyVal ); 88 | InsertTailList ( Answers , &Kv->Vals); 89 | } 90 | 91 | 92 | VOID PiHandleDeclarative ( 93 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers) 94 | { 95 | ULONG TLen , ILen; 96 | PUCHAR Tmp; 97 | KEY_NAME Key; 98 | PKEY_VALUE Kv , *Kv2 , Kv3; 99 | 100 | Key = KeyVal->Key.D; 101 | if ( Key == KEY_TargetAlias ) return;//temp 102 | Kv = KV(ConInfo , Key); 103 | Kv2 = AddrKV(ConInfo , Key); 104 | 105 | if (PiIsKeyBool(Key)) 106 | { 107 | KEY_TV(Kv).Bool = KEY_FV(KeyVal).Bool; 108 | PROT_Debug (("Boolean 0x%X accepted\n" , KEY_TV(Kv).Bool)); 109 | } 110 | else if ( PiIsKeyNumber(Key)) 111 | { 112 | KEY_TV(Kv).Number = KEY_FV(KeyVal).Number; 113 | PROT_Debug (("Number 0x%X accepted\n" , KEY_TV(Kv).Number)); 114 | } 115 | else if ( PiIsKeyString(Key)) 116 | { 117 | TLen = strlen ( KEY_FV(KeyVal).String ); 118 | if (TLen) 119 | { 120 | ILen = strlen ( Kv->Value[KEY_I].String ); 121 | if ( ILen) 122 | { 123 | Kv3 = PiAllocateKeyValStr( ILen + 1 , TLen + 1 ); 124 | Tmp = KEY_IV(Kv3).String; 125 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) + ILen + 1); 126 | KEY_IV(Kv3).String = Tmp; 127 | } 128 | else 129 | { 130 | Kv3 = PiAllocateKeyValStr1( TLen + 1 ); 131 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE)); 132 | } 133 | RtlCopyMemory ( KEY_TV(Kv3).String , KEY_FV(KeyVal).String , TLen + 1); 134 | ExFreePoolWithTag (Kv , USCSI_TAG); 135 | *Kv2 = Kv3; 136 | PROT_Debug (("String '%s' accepted\n" , KEY_TV(Kv3).String)); 137 | } 138 | } 139 | else if (PiIsKeyLov(Key)) 140 | { 141 | TLen = strlen ( KEY_FV(KeyVal).Lov.Val ); 142 | if (TLen) 143 | { 144 | ILen = strlen ( Kv->Value[KEY_I].Lov.Val ); 145 | if ( ILen) 146 | { 147 | Kv3 = PiAllocateKeyValStr( ILen + 1 , TLen + 1 ); 148 | Tmp = KEY_IV(Kv3).Lov.Val; 149 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) + ILen + 1); 150 | KEY_IV(Kv3).String = Tmp; 151 | } 152 | else 153 | { 154 | Kv3 = PiAllocateKeyValStr1( TLen + 1 ); 155 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE)); 156 | } 157 | RtlCopyMemory ( KEY_TV(Kv3).Lov.Val , KEY_FV(KeyVal).Lov.Val , TLen + 1); 158 | ExFreePoolWithTag (Kv , USCSI_TAG); 159 | *Kv2 = Kv3; 160 | PROT_Debug (("Lov '%s' accepted\n" , KEY_TV(Kv3).Lov.Val)); 161 | } 162 | } 163 | } 164 | /* 165 | Handler for most predefined Keys 166 | */ 167 | VOID PiInternalHandler ( 168 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 169 | { 170 | PSESSION Session; 171 | KEY_NAME Key; 172 | PKEY_VALUE Kv, *Kv2 , Kv3; 173 | 174 | Session = ConInfo->Session; 175 | Key = KeyVal->Key.D; 176 | 177 | PROT_Debug(("%s " , PiKeyName(Key))); 178 | 179 | Kv = KV(ConInfo , Key); 180 | Kv2 = AddrKV(ConInfo , Key); 181 | 182 | if ( PiIsKeyDeclarative( Key ) ) 183 | { 184 | PiHandleDeclarative(KeyVal , ConInfo , Questions , Answers); 185 | return; 186 | } 187 | 188 | // Answer value is Reject 189 | if ( KeyVal->Flags & KEY_Reject ) 190 | PiHandleReject( KeyVal , ConInfo , Questions , Answers ); 191 | // Answer value is Irrelevant 192 | else if ( KeyVal->Flags & KEY_Irrelevant ) 193 | PiHandleIrrelevant( KeyVal , ConInfo , Questions , Answers ); 194 | // Answer value is None 195 | else if ( KeyVal->Flags & KEY_None ) 196 | PiHandleNone ( KeyVal , ConInfo , Questions , Answers ); 197 | // only for incoming Keys 198 | // Answer value is "NotUnderstood" 199 | else if ( KeyVal->Flags & KEY_NotUnderstood ) 200 | PiHandleNotUnderstood (KeyVal , ConInfo , Questions , Answers ); 201 | // Value is Illegal , for example , out of range 202 | else if ( KeyVal->Flags & KEY_Illegal) 203 | PiHandleIllegal( KeyVal , ConInfo , Questions , Answers ); 204 | 205 | else if ( PiIsKeyBool(Key) ) 206 | { 207 | if ( PiKeys[Key].Attrs & KEY_FUNC_OR ) 208 | KEY_PV(Kv).Bool = KEY_FV(Kv).Bool | KEY_FV(KeyVal).Bool; 209 | else if ( PiKeys[Key].Attrs & KEY_FUNC_AND ) 210 | KEY_PV(Kv).Bool = KEY_FV(Kv).Bool & KEY_FV(KeyVal).Bool; 211 | PROT_Debug (("Boolean 0x%X->0x%X\n" , KEY_FV(Kv).Bool , KEY_PV(Kv).Bool)); 212 | } 213 | else if ( PiIsKeyNumber(Key) ) 214 | { 215 | if ( PiKeys[Key].Attrs & KEY_FUNC_MIN ) 216 | KEY_PV(Kv).Number = min ( KEY_FV(Kv).Number , KEY_FV(KeyVal).Number); 217 | else if ( PiKeys[Key].Attrs & KEY_FUNC_MAX ) 218 | KEY_PV(Kv).Number = max ( KEY_FV(Kv).Number , KEY_FV(KeyVal).Number); 219 | PROT_Debug (("Number 0x%X->0x%X\n" , KEY_FV(Kv).Number , KEY_PV(Kv).Number)); 220 | } 221 | else if ( PiIsKeyString(Key) ) 222 | { 223 | PUCHAR Tmp , Tmp2; 224 | ULONG PLen , FLen , Tmp3; 225 | PLen = strlen ( KEY_FV(KeyVal).String ); 226 | if (PLen) 227 | { 228 | FLen = strlen ( KEY_FV(Kv).String); 229 | if (FLen) 230 | { 231 | Kv3 = PiAllocateKeyValStr( FLen + 1, PLen + 1 ); 232 | Tmp = KEY_FV(Kv3).String; 233 | Tmp2 = KEY_PV(Kv3).String; 234 | Tmp3 = Kv3->Size; 235 | 236 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) ); 237 | 238 | KEY_FV(Kv3).String = Tmp; 239 | RtlCopyMemory ( KEY_FV(Kv3).String , KEY_FV(Kv).String , FLen + 1); 240 | 241 | KEY_PV(Kv3).String = Tmp2; 242 | Kv3->Size = Tmp3; 243 | 244 | } 245 | else 246 | { 247 | Kv3 = PiAllocateKeyValStr1( PLen + 1 ); 248 | Tmp2 = KEY_PV(Kv3).String; 249 | Tmp3 = Kv3->Size; 250 | 251 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) ); 252 | 253 | KEY_PV(Kv3).String = Tmp2; 254 | Kv3->Size = Tmp3; 255 | } 256 | 257 | RtlCopyMemory ( KEY_PV(Kv3).String , KEY_FV(KeyVal).String , PLen + 1); 258 | 259 | PROT_Debug (("String 0x%X->0x%X\n" , KEY_FV(Kv).String , KEY_PV(Kv).String)); 260 | } 261 | } 262 | else if ( PiIsKeyLov(Key) ) 263 | { 264 | PUCHAR Tmp , Tmp2; 265 | ULONG PLen , FLen , Tmp3; 266 | PLen = strlen ( KEY_FV(KeyVal).Lov.Val ); 267 | if (PLen) 268 | { 269 | FLen = strlen ( KEY_FV(Kv).Lov.Val); 270 | if (FLen) 271 | { 272 | Kv3 = PiAllocateKeyValStr( FLen + 1, PLen + 1 ); 273 | Tmp = KEY_FV(Kv3).Lov.Val; 274 | Tmp2 = KEY_PV(Kv3).Lov.Val; 275 | Tmp3 = Kv3->Size; 276 | 277 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) ); 278 | 279 | KEY_FV(Kv3).Lov.Val = Tmp; 280 | RtlCopyMemory ( KEY_FV(Kv3).Lov.Val , KEY_FV(Kv).Lov.Val , FLen + 1); 281 | 282 | KEY_FV(Kv3).Lov.Sel = KEY_FV(Kv).Lov.Sel; 283 | 284 | KEY_PV(Kv3).Lov.Val = Tmp2; 285 | Kv3->Size = Tmp3; 286 | 287 | } 288 | else 289 | { 290 | Kv3 = PiAllocateKeyValStr1( PLen + 1 ); 291 | Tmp2 = KEY_PV(Kv3).Lov.Val; 292 | Tmp3 = Kv3->Size; 293 | 294 | RtlCopyMemory ( Kv3 , Kv , sizeof (KEY_VALUE) ); 295 | 296 | KEY_PV(Kv3).Lov.Val = Tmp2; 297 | Kv3->Size = Tmp3; 298 | } 299 | 300 | RtlCopyMemory ( KEY_PV(Kv3).Lov.Val , KEY_FV(KeyVal).Lov.Val , PLen + 1); 301 | 302 | PROT_Debug (("Lov 0x%X->0x%X\n" , KEY_FV(Kv).Lov.Val , KEY_PV(Kv).Lov.Val)); 303 | } 304 | } 305 | 306 | PiUpdateKeyNegoState ( Key , ConInfo , Answers ); 307 | 308 | } 309 | 310 | VOID PiAuthMethodHandler ( 311 | PKEY_VALUE KeyVal , PuCONINFO ConInfo , PLIST_ENTRY Questions , PLIST_ENTRY Answers ) 312 | { 313 | KEY_NAME Key; 314 | 315 | Key = KeyVal->Key.D; 316 | 317 | //DbgPrint("%s\n" , __FUNCTION__); 318 | // TODO : Dispatch to different AuthMethod handler 319 | if (KeyVal->Flags & KEY_None ) 320 | { 321 | KV(ConInfo , Key)->Flags |= KEY_None; 322 | PiUpdateKeyNegoState ( Key , ConInfo , Answers ); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /uSCSIPort/Login.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID PiProcessKey( PPDU Pdu , PuCONINFO ConInfo , BOOLEAN AllocatePDU , UCHAR Opcode ); 5 | 6 | PVOID PiFormatKeyValues(PLIST_ENTRY KeyVals , PULONG DataLen); 7 | 8 | PPDU PiAllocateTxtPDU ( PuCONINFO ConInfo , PPDU Pdu , PUCHAR Data , ULONG Len , UCHAR Opcode); 9 | 10 | VOID PiSetKeyState ( KEY_NAME Key , PuCONINFO ConInfo , UCHAR State); 11 | 12 | VOID PiProtError (PuCONINFO ConInfo , PUCHAR Func ); 13 | 14 | VOID PiCommitPending( PuCONINFO ConInfo ); 15 | 16 | VOID PiCalcNextStage(PuCONINFO ConInfo); 17 | 18 | // 19 | //Forward declaration 20 | // 21 | 22 | VOID PiAnswerPdu( PPDU Pdu , PuCONINFO ConInfo , UCHAR Opcode ); 23 | 24 | BOOLEAN PiCanLoginTransit (PuCONINFO ConInfo ); 25 | 26 | PPDU PiCollectStgParms(PuCONINFO ConInfo , BOOLEAN Answer); 27 | 28 | VOID PiLoginComplete( PuCONINFO ConInfo ); 29 | 30 | #define PiAnswerLoginPdu( Pdu , ConInfo) \ 31 | PiAnswerPdu ( Pdu , ConInfo , OP_LOGIN_REQ) 32 | 33 | #define PiLoginProcessKey(Pdu , ConInfo , AllocatePDU ) \ 34 | PiProcessKey (Pdu , ConInfo , AllocatePDU , OP_LOGIN_REQ) 35 | 36 | // 37 | // 38 | // 39 | VOID PtProcessLogin( PPDU Pdu , PuCONINFO ConInfo) 40 | { 41 | PPDU NewPdu , CPdu , LPdu; 42 | PLIST_ENTRY PduEnt , PduEnt2; 43 | ULONG DataSegLen; 44 | 45 | DataSegLen = (Pdu->Bhs->GENERICBHS.DataSegmentLength[0] << 16 ) | 46 | (Pdu->Bhs->GENERICBHS.DataSegmentLength[1] << 8 ) | 47 | (Pdu->Bhs->GENERICBHS.DataSegmentLength[2] ); 48 | 49 | DbgPrint( 50 | "%s Pdu=0x%p VersionMax=0x%X VersionActive=0x%X CSG=0x%X NSG=0x%X C=0x%X T=0x%X DataSegLen=0x%X\n" , 51 | __FUNCTION__ , 52 | Pdu , 53 | Pdu->Bhs->LOGIN_RESPONSE.VersionMax, 54 | Pdu->Bhs->LOGIN_RESPONSE.VersionActive, 55 | Pdu->Bhs->LOGIN_RESPONSE.CSG, 56 | Pdu->Bhs->LOGIN_RESPONSE.NSG, 57 | Pdu->Bhs->LOGIN_RESPONSE.Continue, 58 | Pdu->Bhs->LOGIN_RESPONSE.Transit, 59 | DataSegLen); 60 | // 61 | // There are some errors 62 | // 63 | if ( Pdu->Bhs->LOGIN_RESPONSE.StatusClass ) 64 | { 65 | /*DbgPrint("%s Status=0x%X%X\n", 66 | __FUNCTION__ ,Pdu->Bhs->LOGIN_RESPONSE.StatusClass,Pdu->Bhs->LOGIN_RESPONSE.StatusDetail); 67 | */ 68 | ConInfo->LoginState = LOGIN_STAT_IDLE; 69 | goto Error; 70 | } 71 | // 72 | // Continue sending Initiator Logical PDU if any 73 | // 74 | if ( ConInfo->Answer ) 75 | { 76 | #ifdef STRICT_PROT_CHECK 77 | if ( DataSegLen ) 78 | PiProtError ( ConInfo , __FUNCTION__ ); // This must be a SYNC Pdu 79 | #endif 80 | goto Answer; 81 | } 82 | // 83 | // Part of Target Logical PDU 84 | // 85 | /*An initiator receiving a Text or Login 86 | Response with the C bit set to 1 MUST answer with a Text or Login 87 | Request with no data segment (DataSegmentLength 0).*/ 88 | if ( Pdu->Bhs->LOGIN_RESPONSE.Continue ) 89 | { 90 | #ifdef STRICT_PROT_CHECK 91 | if ( Pdu->Bhs->LOGIN_RESPONSE.Transit ) 92 | PiProtError( ConInfo , __FUNCTION__ ); 93 | #endif 94 | if ( !ConInfo->Logical ) 95 | // Head 96 | ConInfo->Logical = Pdu; 97 | else 98 | // Part of 99 | InsertTailList ( &ConInfo->Logical->Continue, &Pdu->Continue ); 100 | goto Answer; 101 | } 102 | // Logical PDU completed 103 | else if ( ConInfo->Logical ) 104 | { 105 | LPdu = ConInfo->Logical; 106 | ConInfo->Logical = NULL; 107 | } 108 | else 109 | LPdu = Pdu; 110 | // 111 | // Process Target Logical PDU 112 | // 113 | PiLoginProcessKey ( LPdu , 114 | ConInfo , 115 | /*A Login Response with a T bit set to 1 MUST NOT contain key=value 116 | pairs that may require additional answers from the initiator within 117 | the same stage*/ 118 | !Pdu->Bhs->LOGIN_RESPONSE.Transit ); 119 | #ifdef STRICT_PROT_CHECK 120 | if (ConInfo->LoginState == LOGIN_STAT_STARTED && 121 | Pdu->Bhs->LOGIN_RESPONSE.Transit ) 122 | { 123 | PiProtError( ConInfo , __FUNCTION__ ); 124 | } 125 | else if 126 | #else 127 | if 128 | #endif 129 | (ConInfo->LoginState == LOGIN_STAT_STARTED && 130 | !Pdu->Bhs->LOGIN_RESPONSE.Transit) 131 | { 132 | if ( PiCanLoginTransit (ConInfo) ) 133 | { 134 | PiCalcNextStage( ConInfo ); 135 | PiCommitPending( ConInfo ); 136 | ConInfo->LoginState = LOGIN_STAT_PENDING_TRANSIT; 137 | /*DbgPrint("%s Stage Transition 0x%X -> 0x%X Proposed\n" , 138 | __FUNCTION__ , ConInfo->CSG , ConInfo->NSG);*/ 139 | } 140 | } 141 | else if ( ConInfo->LoginState == LOGIN_STAT_PENDING_TRANSIT && 142 | Pdu->Bhs->LOGIN_RESPONSE.Transit ) 143 | { 144 | if ( LOGIN_STAGE_FullFeaturePhase == Pdu->Bhs->LOGIN_RESPONSE.NSG ) 145 | { 146 | // Final Login Response 147 | //DbgPrint("%s Login Successfully CID=0x%X\n" , __FUNCTION__ , ConInfo->CID); 148 | 149 | ConInfo->LoginState = LOGIN_STAT_IDLE; 150 | ConInfo->State = CON_STAT_LOGGED_IN; 151 | 152 | if ( IsLeadingCon(ConInfo) ) 153 | { 154 | ConInfo->Session->State = SESSION_STAT_LOGGED_IN; 155 | REVERSE_BYTES_SHORT( &ConInfo->Session->TSIH , 156 | &Pdu->Bhs->LOGIN_RESPONSE.TSIH); 157 | } 158 | 159 | goto LoginComplete; 160 | } 161 | /* 162 | DbgPrint("%s Stage Transition 0x%X -> 0x%X Agreed\n" , 163 | __FUNCTION__, ConInfo->CSG , Pdu->Bhs->LOGIN_RESPONSE.NSG);*/ 164 | 165 | ConInfo->CSG = Pdu->Bhs->LOGIN_RESPONSE.NSG; 166 | PiCalcNextStage(ConInfo); 167 | 168 | ConInfo->LoginState = LOGIN_STAT_STARTED; 169 | // 170 | // Prepare next stage nego parms 171 | // 172 | PiCollectStgParms ( ConInfo , TRUE); 173 | } 174 | else if ( ConInfo->LoginState == LOGIN_STAT_PENDING_TRANSIT && 175 | !Pdu->Bhs->LOGIN_RESPONSE.Transit ) 176 | { 177 | /* 178 | 5.3.3 179 | If the target responds to a Login Request that has the T bit set to 1 180 | with a Login Response that has the T bit set to 0, the initiator 181 | should keep sending the Login Request (even empty) with the T bit set 182 | to 1, while it still wants to switch stage, until it receives the 183 | Login Response that has the T bit set to 1 or it receives a key that 184 | requires it to set the T bit to 0. 185 | */ 186 | //DbgPrint("%s Stage Transition Pending\n" , __FUNCTION__); 187 | } 188 | // 189 | // 190 | // 191 | Answer: 192 | 193 | PiAnswerLoginPdu ( Pdu , ConInfo ); 194 | return; 195 | 196 | Error: 197 | //TODO : may need to free the remaining Initiator's Logical PDU 198 | LoginComplete: 199 | 200 | PiLoginComplete(ConInfo); 201 | return; 202 | } 203 | 204 | BOOLEAN PiInitiatorName( PUCHAR *Name); 205 | 206 | BOOLEAN PiInitiatorAlias( PUCHAR *Alias); 207 | 208 | BOOLEAN PiAuthMethod( PUCHAR *Method); 209 | 210 | VOID PiDumpKey (PKEY_VALUE KeyVal); 211 | 212 | 213 | #define SETNEGOSTATE(Key,ConInfo) \ 214 | if ( (PiKeys[Key].Attrs & KEY_DECLARATIVE) ) \ 215 | PiSetKeyState( Key , ConInfo , KEY_Agreed); \ 216 | else \ 217 | PiSetKeyState( Key , ConInfo , KEY_Proposed) 218 | 219 | /* 220 | This function will ALWAYS allocate at least 1 PDU , 221 | even if there should be no nego parms , in which case , 222 | the PDU function as a sync signal 223 | */ 224 | 225 | PPDU PiCollectStgParms(PuCONINFO ConInfo , BOOLEAN Answer) 226 | { 227 | PPDU NewPdu = NULL; 228 | PKEY_VALUE *Parm; 229 | PUCHAR Val , DataOut; 230 | ULONG Len , DataLen; 231 | LIST_ENTRY ParmList; 232 | PLIST_ENTRY ParmEnt; 233 | BOOLEAN Dynamic; 234 | 235 | InitializeListHead ( &ParmList ); 236 | // 237 | // Security Nego 238 | // 239 | if (ConInfo->CSG == LOGIN_STAGE_SecurityNegotiation ) 240 | { 241 | 242 | Parm = AddrKV(ConInfo , KEY_InitiatorName); 243 | if (!*Parm) 244 | { 245 | Dynamic = PiInitiatorName(&Val); 246 | if (Dynamic) 247 | { 248 | Len = strlen (Val) + 1; 249 | *Parm = PiAllocateKeyValStr0( Len ); 250 | RtlCopyMemory (KEY_IV(*Parm).String , Val , Len-1 ); 251 | *(KEY_IV(*Parm).String + Len-1 ) = '\0'; 252 | ExFreePoolWithTag (Val , USCSI_TAG); 253 | } 254 | else 255 | { 256 | *Parm = PiAllocateKeyValue(); 257 | KEY_IV(*Parm).String = Val; 258 | } 259 | 260 | } 261 | (*Parm)->Flags = 0; 262 | (*Parm)->Key.D = KEY_InitiatorName; 263 | SETNEGOSTATE( KEY_InitiatorName , ConInfo); 264 | InsertTailList (&ParmList , &(*Parm)->Vals ); 265 | 266 | Parm = AddrKV(ConInfo , KEY_SessionType); 267 | if (!*Parm) 268 | *Parm = PiAllocateKeyValue(); 269 | (*Parm)->Flags = 0; 270 | (*Parm)->Key.D = KEY_SessionType; 271 | SETNEGOSTATE( KEY_SessionType , ConInfo); 272 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 273 | 274 | if ( ConInfo->Session->NormalSession ) 275 | { 276 | KEY_IV(*Parm).String = "Normal"; 277 | 278 | Parm = AddrKV(ConInfo , KEY_TargetName); 279 | if (!*Parm) 280 | *Parm = PiAllocateKeyValue(); 281 | (*Parm)->Flags = 0; 282 | (*Parm)->Key.D = KEY_TargetName; 283 | SETNEGOSTATE( KEY_TargetName , ConInfo); 284 | KEY_IV(*Parm).String = ConInfo->Session->Tgt->Tgt.TargetName; 285 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 286 | } 287 | else 288 | KEY_IV(*Parm).String = "Discovery"; 289 | 290 | Parm = AddrKV(ConInfo , KEY_InitiatorAlias); 291 | if (!*Parm) 292 | { 293 | Dynamic = PiInitiatorAlias ( &Val); 294 | if (Dynamic) 295 | { 296 | Len = strlen (Val) + 1; 297 | *Parm = PiAllocateKeyValStr0( Len ); 298 | RtlCopyMemory (KEY_IV(*Parm).String , Val , Len-1 ); 299 | *(KEY_IV(*Parm).String + Len-1 ) = '\0'; 300 | ExFreePoolWithTag (Val , USCSI_TAG); 301 | } 302 | else 303 | { 304 | *Parm = PiAllocateKeyValue(); 305 | KEY_IV(*Parm).String = Val; 306 | } 307 | } 308 | (*Parm)->Flags = 0; 309 | (*Parm)->Key.D = KEY_InitiatorAlias; 310 | SETNEGOSTATE( KEY_InitiatorAlias , ConInfo); 311 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 312 | 313 | Parm = AddrKV(ConInfo , KEY_AuthMethod); 314 | if (!*Parm) 315 | { 316 | Dynamic = PiAuthMethod ( &Val); 317 | if (Dynamic) 318 | { 319 | Len = strlen (Val) + 1; 320 | *Parm = PiAllocateKeyValStr0( Len ); 321 | RtlCopyMemory (KEY_FV(*Parm).String , Val , Len-1 ); 322 | *(KEY_FV(*Parm).String + Len-1 ) = '\0'; 323 | ExFreePoolWithTag (Val , USCSI_TAG); 324 | } 325 | else 326 | { 327 | *Parm = PiAllocateKeyValue(); 328 | KEY_FV(*Parm).String = Val; 329 | } 330 | } 331 | (*Parm)->Flags = 0; 332 | (*Parm)->Key.D = KEY_AuthMethod; 333 | SETNEGOSTATE( KEY_AuthMethod , ConInfo); 334 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 335 | 336 | 337 | } 338 | // 339 | // Operational Nego 340 | // 341 | if ( ConInfo->CSG == LOGIN_STAGE_LoginOperationalNegotiation ) 342 | { 343 | 344 | Parm = AddrKV(ConInfo , KEY_MaxConnections); 345 | 346 | (*Parm)->Flags = 0; 347 | (*Parm)->Key.D = KEY_MaxConnections; 348 | SETNEGOSTATE( KEY_MaxConnections , ConInfo); 349 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 350 | 351 | Parm = AddrKV(ConInfo , KEY_HeaderDigest); 352 | 353 | (*Parm)->Flags = 0; 354 | (*Parm)->Key.D = KEY_HeaderDigest; 355 | SETNEGOSTATE( KEY_HeaderDigest , ConInfo); 356 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 357 | 358 | Parm = AddrKV(ConInfo , KEY_DataDigest); 359 | 360 | (*Parm)->Flags = 0; 361 | (*Parm)->Key.D = KEY_DataDigest; 362 | SETNEGOSTATE( KEY_DataDigest , ConInfo); 363 | InsertTailList (&ParmList ,&(*Parm)->Vals ); 364 | 365 | 366 | } 367 | // 368 | // Full Feature Nego 369 | // 370 | if ( ConInfo->CSG == LOGIN_STAGE_FullFeaturePhase ) 371 | { 372 | 373 | } 374 | 375 | DataOut = PiFormatKeyValues( &ParmList , &DataLen); 376 | if (!DataOut) 377 | goto Out; 378 | /* 379 | { 380 | PUCHAR Tmp = DataOut ,End = DataOut + DataLen - 1; 381 | do 382 | { 383 | DbgPrint("Parm : %s\n" ,Tmp); 384 | while( *Tmp++ != '\0'); 385 | }while( Tmp < End ); 386 | }*/ 387 | 388 | NewPdu = PiAllocateTxtPDU ( ConInfo , 389 | NULL , 390 | DataOut , 391 | DataLen , 392 | OP_LOGIN_REQ); 393 | 394 | if ( !NewPdu ) 395 | goto Out; 396 | 397 | ParmEnt = ParmList.Flink; 398 | while ( ParmEnt != &ParmList ) 399 | { 400 | PKEY_VALUE Answer; 401 | Answer = CONTAINING_RECORD (ParmEnt , KEY_VALUE , Vals); 402 | KEYOUT(Answer); 403 | ParmEnt = ParmEnt->Flink; 404 | } 405 | 406 | if (Answer) 407 | ConInfo->Answer = NewPdu; 408 | 409 | return NewPdu; 410 | 411 | Out: 412 | 413 | if ( DataOut ) 414 | ExFreePoolWithTag (DataOut , USCSI_TAG); 415 | 416 | return NewPdu; 417 | } 418 | 419 | // 420 | 421 | VOID PiCalcNextStage(PuCONINFO ConInfo) 422 | { 423 | if ( LOGIN_STAGE_SecurityNegotiation == ConInfo->CSG ) 424 | { 425 | if (ConInfo->SkipOperationalNegotiation) 426 | ConInfo->NSG = LOGIN_STAGE_FullFeaturePhase; 427 | else 428 | ConInfo->NSG = LOGIN_STAGE_LoginOperationalNegotiation; 429 | } 430 | else if ( LOGIN_STAGE_LoginOperationalNegotiation == ConInfo->CSG ) 431 | ConInfo->NSG = LOGIN_STAGE_FullFeaturePhase; 432 | } 433 | /* 434 | Logic for a login stage transition 435 | */ 436 | BOOLEAN PiCanLoginTransit (PuCONINFO ConInfo ) 437 | { 438 | if ( ConInfo->MoreWorks ) 439 | { 440 | ConInfo->MoreWorks = FALSE; 441 | return FALSE; 442 | } 443 | 444 | return TRUE; 445 | } 446 | 447 | 448 | VOID PiLoginComplete( PuCONINFO ConInfo ) 449 | { 450 | KeSetEvent ( &ConInfo->LoginEvent , IO_NO_INCREMENT , FALSE ); 451 | } 452 | 453 | PPDU PiAllocateCmdPDU ( 454 | PuCONINFO ConInfo , PUCHAR DataBuf , ULONG DataSize , ULONG ReadSize , UCHAR Direction); 455 | 456 | // 457 | // 458 | // 459 | 460 | NTSTATUS PtLogin( PuCONINFO ConInfo ) 461 | { 462 | PPDU Login , CPdu; 463 | PLIST_ENTRY PduEnt , PduEnt2; 464 | BOOLEAN LeadingCon; 465 | 466 | ConInfo->State = CON_STAT_IN_LOGIN; 467 | 468 | ConInfo->CSG = ConInfo->SSG; 469 | ConInfo->LoginState = LOGIN_STAT_STARTED; 470 | ConInfo->SkipOperationalNegotiation = 1; 471 | 472 | Login = PiCollectStgParms (ConInfo , FALSE); 473 | 474 | PduEnt2 = PduEnt = &Login->Continue; 475 | LeadingCon = IsLeadingCon( ConInfo ); 476 | 477 | do 478 | { 479 | CPdu = CONTAINING_RECORD (PduEnt2 , PDU , Continue); 480 | 481 | REVERSE_BYTES_SHORT (&CPdu->Bhs->LOGIN_REQUEST.CID , &ConInfo->CID); 482 | 483 | if ( !LeadingCon ) 484 | REVERSE_BYTES_SHORT ( &CPdu->Bhs->LOGIN_REQUEST.TSIH , 485 | &ConInfo->Session->TSIH); 486 | 487 | CPdu->Bhs->LOGIN_REQUEST.CSG = ConInfo->CSG; 488 | CPdu->Bhs->LOGIN_REQUEST.ISID.Type = 0x00; 489 | CPdu->Bhs->LOGIN_REQUEST.ISID.B[0]=0x12; 490 | CPdu->Bhs->LOGIN_REQUEST.ISID.B[1]=0x34; 491 | 492 | PduEnt2 = PduEnt2->Flink; 493 | 494 | } while ( PduEnt2 != PduEnt ); 495 | 496 | TpQueueOutPDU ( ConInfo->ConCtx , Login); 497 | 498 | KeWaitForSingleObject ( &ConInfo->LoginEvent , Executive , KernelMode , FALSE , NULL ); 499 | 500 | // Test 501 | //PtNopOut( ConInfo , TRUE ); 502 | 503 | if ( CON_STAT_LOGGED_IN == ConInfo->State ) 504 | return STATUS_SUCCESS; 505 | else 506 | return STATUS_UNSUCCESSFUL; 507 | } -------------------------------------------------------------------------------- /uSCSIPort/Nop.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | PPDU PiFindPendingTask(ULONG TaskTag , PuCONINFO ConInfo , BOOLEAN Remove ); 5 | 6 | /* 7 | Parms 8 | TPing : Target Nopin PDU if not NULL 9 | Echo : TRUE Target Nopin expected 10 | FALSE Target Nopin not expected 11 | Immediate : Relevant only when Echo == TRUE 12 | 13 | Return 14 | Nopout PDU 15 | NULL if failed 16 | */ 17 | 18 | PPDU PiAllocateNopOut(PuCONINFO ConInfo , PPDU TPing , BOOLEAN Echo, BOOLEAN Immediate) 19 | { 20 | PPDU Pdu; 21 | PSESSION Session; 22 | PuCON_CTX Ctx; 23 | FOUR_BYTE F; 24 | 25 | Session = ConInfo->Session; 26 | Ctx = ConInfo->ConCtx; 27 | F.AsULong = 0xFFFFFFFF; 28 | 29 | if ( TPing ) 30 | { 31 | // Reuse the Nopin PDU 32 | Pdu = TPing; 33 | Pdu->Bhs->NOP_OUT.Opcode = OP_NOP_OUT; 34 | RtlZeroMemory ( &Pdu->Bhs->NOP_OUT.Reserved5 , 16 ); 35 | } 36 | else 37 | { 38 | Pdu = TpAllocatePDU ( Ctx , OP_NOP_OUT , 0 , 0x400 ); 39 | if ( !Pdu ) 40 | goto Out; 41 | 42 | REVERSE_BYTES ( &Pdu->Bhs->NOP_OUT.TargetTransferTag , &F); 43 | } 44 | /* 45 | The NOP-Out MUST have the Initiator Task Tag set to a valid value 46 | only if a response in the form of NOP-In is requested 47 | */ 48 | if ( Echo ) 49 | { 50 | // Echo Expected 51 | // 1. This is an Initiator Ping requiring Echo 52 | // 2. This is an Initaitor Echo requiring Echo 53 | Session->TaskTag++; 54 | REVERSE_BYTES ( &Pdu->Bhs->NOP_OUT.InitiatorTaskTag , &Session->TaskTag ); 55 | Pdu->Bhs->NOP_OUT.Immediate = Immediate; 56 | } 57 | else 58 | { 59 | // No Echo expected 60 | // 1. This is an a State Sync 61 | // 2. This is an Initaitor Echo requiring no Echo 62 | REVERSE_BYTES ( &Pdu->Bhs->NOP_OUT.InitiatorTaskTag , &F); 63 | /* 64 | If the Initiator Task Tag contains 0xffffffff, the I bit MUST be set 65 | to 1 ... 66 | */ 67 | Pdu->Bhs->NOP_OUT.Immediate = 1; 68 | } 69 | 70 | Pdu->Bhs->NOP_OUT.Final = 1; 71 | 72 | Out: 73 | 74 | return Pdu; 75 | } 76 | 77 | /* 78 | Parms 79 | Pdu : Nopin 80 | */ 81 | 82 | VOID PtProcessNopIn ( PPDU Pdu, PuCONINFO ConInfo) 83 | { 84 | PPDU NopOut; 85 | PuCON_CTX Ctx; 86 | FOUR_BYTE F , TTT , ITT; 87 | LARGE_INTEGER Time; 88 | ULONG Tmp; 89 | EIGHT_BYTE Tmp2 , Tmp3 , Tmp4; 90 | 91 | Ctx = ConInfo->ConCtx; 92 | 93 | F.AsULong = 0xFFFFFFFF; 94 | REVERSE_BYTES ( &TTT , &Pdu->Bhs->NOP_IN.TargetTransferTag); 95 | 96 | //DbgPrint("%s ConInfo=0x%p Pdu=0x%p TTT=0x%X\n" ,__FUNCTION__ , ConInfo , Pdu ,TTT.AsULong); 97 | // Target Echo 98 | if ( TTT.AsULong == F.AsULong ) 99 | { 100 | KeQueryTickCount ( &Time ); 101 | 102 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->NOP_IN.InitiatorTaskTag); 103 | NopOut = PiFindPendingTask ( ITT.AsULong , ConInfo , TRUE); 104 | if ( NULL == NopOut ) 105 | { 106 | DbgPrint("No Matching NopOut for Pdu=0x%x ITT=0x%x\n" , Pdu , ITT.AsULong); 107 | goto Out; 108 | } 109 | 110 | // Caculate Time Span 111 | 112 | REVERSE_BYTES_QUAD ( &Tmp4 , &Time); 113 | REVERSE_BYTES_QUAD ( &Tmp2 , &Tmp4); 114 | REVERSE_BYTES_QUAD ( &Tmp4 , &NopOut->SendingTime); 115 | REVERSE_BYTES_QUAD ( &Tmp3 , &Tmp4); 116 | 117 | Tmp = (ULONG)(Tmp2.AsULongLong - Tmp3.AsULongLong) * ConInfo->Session->Ns100Unit / 10; 118 | 119 | // Update Connection Statistics 120 | 121 | if ( ConInfo->Stats[CON_STATS_MIN] ) 122 | ConInfo->Stats[CON_STATS_MIN] = min ( ConInfo->Stats[CON_STATS_MIN] , Tmp ); 123 | else 124 | ConInfo->Stats[CON_STATS_MIN] = Tmp; 125 | 126 | ConInfo->Stats[CON_STATS_AVG] = (ConInfo->Stats[CON_STATS_AVG] + Tmp ) >> 1; 127 | 128 | if (ConInfo->Stats[CON_STATS_MAX]) 129 | ConInfo->Stats[CON_STATS_MAX] = max (ConInfo->Stats[CON_STATS_MAX] , Tmp); 130 | else 131 | ConInfo->Stats[CON_STATS_MAX] = Tmp; 132 | /* 133 | DbgPrint("%s Traffics ( Min %d Max %d Avg %d) ms\n" , 134 | __FUNCTION__ , 135 | ConInfo->Stats[CON_STATS_MIN], 136 | ConInfo->Stats[CON_STATS_MAX], 137 | ConInfo->Stats[CON_STATS_AVG]);*/ 138 | 139 | TpQueuePduRelease( Ctx , NopOut ); 140 | 141 | // Test 142 | NopOut = PiAllocateNopOut ( ConInfo , NULL , TRUE , FALSE); 143 | if ( !NopOut ) 144 | goto Out; 145 | 146 | TpQueueOutPDU ( Ctx , NopOut ); 147 | } 148 | //Target Ping Requiring Nopout 149 | else 150 | { 151 | /* 152 | Upon receipt of a NOP-In with the Target Transfer Tag set to a valid 153 | value (not the reserved 0xffffffff), the initiator MUST respond with 154 | a NOP-Out. 155 | */ 156 | NopOut = PiAllocateNopOut ( ConInfo , Pdu , FALSE , FALSE); 157 | if ( !NopOut ) 158 | goto Out; 159 | 160 | TpQueueOutPDU ( Ctx , NopOut ); 161 | } 162 | 163 | Out: 164 | 165 | TpQueuePduRelease( Ctx , Pdu ); 166 | return; 167 | } 168 | 169 | /* 170 | Parms 171 | Echo : Does this Nop-Out expect a Nop-In ? 172 | */ 173 | VOID PtNopOut (PuCONINFO ConInfo , BOOLEAN Echo) 174 | { 175 | PPDU Pdu; 176 | 177 | Pdu = PiAllocateNopOut ( ConInfo , NULL , TRUE , TRUE); 178 | 179 | if ( Pdu ) 180 | TpQueueOutPDU ( ConInfo->ConCtx , Pdu ); 181 | } -------------------------------------------------------------------------------- /uSCSIPort/ProtUtils.c: -------------------------------------------------------------------------------- 1 | #include "precomp.h" 2 | 3 | PKEY_VALUE PiFormatKeyValue (PKEY_VALUE Val); 4 | 5 | ULONG PiGetKeyState (KEY_NAME Key , PuCONINFO ConInfo ); 6 | 7 | VOID PiSetKeyState ( KEY_NAME Key , PuCONINFO ConInfo , UCHAR State); 8 | 9 | BOOLEAN PiInitiatorName( PUCHAR *Name) 10 | { 11 | *Name = "iqn.2009-09.yushang.com.uSCSI:vDisk.0001"; 12 | return FALSE; 13 | } 14 | 15 | BOOLEAN PiInitiatorAlias( PUCHAR *Alias) 16 | { 17 | *Alias = "Personal Disk of yushang"; 18 | return FALSE; 19 | } 20 | 21 | BOOLEAN PiAuthMethod( PUCHAR *Method) 22 | { 23 | *Method = "CHAP,KRB5,SPKM1,SPKM2,SRP,None"; 24 | return FALSE; 25 | } 26 | 27 | VOID PiProtError (PuCONINFO ConInfo , PUCHAR Func ) 28 | { 29 | PROT_Debug (("%s\n" , Func )); 30 | //TODO: Colse connection 31 | } 32 | 33 | /* 34 | Memory Layout 35 | 36 | -------- <- KEY_VALUE 37 | ... 38 | -------- <- Value[0].String if any 39 | ... 40 | -------- <- Value[1].String if any 41 | ... 42 | -------- <- FormatStr if any 43 | ... 44 | -------- 45 | */ 46 | PKEY_VALUE PiAllocateKeyVal(ULONG StrLen , ULONG StrLen2 , ULONG StrLen3) 47 | { 48 | PKEY_VALUE Kv; 49 | 50 | Kv = ExAllocatePoolWithTag ( PagedPool , 51 | sizeof (KEY_VALUE) + StrLen + StrLen2 + StrLen3, 52 | USCSI_TAG); 53 | if ( !Kv ) 54 | return Kv; 55 | 56 | InitializeListHead (&Kv->Vals); 57 | Kv->Size = sizeof (KEY_VALUE) + StrLen + StrLen2 + StrLen3; 58 | Kv->Len = 0; 59 | Kv->Flags = 0; 60 | Kv->NegoRetries = 0; 61 | Kv->Formal = KEY_F; 62 | Kv->Pending = KEY_P; 63 | 64 | if ( StrLen ) 65 | Kv->Value[0].String = (PUCHAR)(Kv+1); 66 | else 67 | Kv->Value[0].String = NULL; 68 | 69 | if (StrLen2) 70 | Kv->Value[1].String = (PUCHAR)(Kv+1) + StrLen; 71 | else 72 | Kv->Value[1].String = NULL; 73 | 74 | if (StrLen3) 75 | Kv->FormatStr = (PUCHAR)(Kv+1) + StrLen + StrLen2; 76 | else 77 | Kv->FormatStr = NULL; 78 | 79 | return Kv; 80 | } 81 | 82 | /* 83 | Called every turn of PiProcessKeys 84 | */ 85 | VOID PiCheckNegoState( PuCONINFO ConInfo , PLIST_ENTRY Answers ) 86 | { 87 | ULONG KeyState; 88 | PKEY_VALUE Kv , Kv2; 89 | KEY_NAME Key; 90 | 91 | for ( Key = 0 ; Key < KEY_LastKey ; Key++) 92 | { 93 | if ( Key == KEY_MaxSessionKey ) 94 | continue; 95 | 96 | Kv = KV( ConInfo , Key ); 97 | 98 | // Some Key may not get initialized 99 | 100 | if (!Kv) 101 | continue; 102 | 103 | if ( KEY_Proposed == PiGetKeyState ( Key , ConInfo) ) 104 | { 105 | Kv->NegoRetries++; 106 | 107 | if ( Kv->NegoRetries <= PiKeys[Key].NegoRetries ) 108 | { 109 | // Repropose it 110 | Kv2 = PiFormatKeyValue ( Kv ); 111 | InsertTailList ( Answers , &Kv2->Vals ); 112 | ConInfo->MoreWorks = TRUE; 113 | } 114 | else if ( PiKeys[Key].NegoFailAction == KEY_NEGO_FAIL_INGORE ) 115 | { 116 | PiSetKeyState ( Key , ConInfo , KEY_Agreed ); 117 | } 118 | else if ( PiKeys[Key].NegoFailAction == KEY_NEGO_FAIL_CLOSE ) 119 | { 120 | // Close XPT 121 | PROT_Debug(("Close Connection\n")); 122 | } 123 | } 124 | } 125 | } 126 | 127 | // 128 | // Allocate a PDU for LOGIN or TEXT 129 | // Segmentation if needed 130 | // 131 | 132 | PPDU PiAllocateTxtPDU(PuCONINFO ConInfo , PPDU Pdu , PUCHAR Data , ULONG Len , UCHAR Opcode) 133 | { 134 | LONG Remaining , DataLen , MaxDataSeg; 135 | PPDU NewPdu , Tmp; 136 | PUCHAR Data2; 137 | 138 | Data2 = Data; 139 | NewPdu = NULL; 140 | Remaining = Len; 141 | 142 | /*5.3 143 | The default MaxRecvDataSegmentLength is used during Login 144 | */ 145 | MaxDataSeg = (LONG)KEY_FV(KV(ConInfo,KEY_MaxRecvDataSegmentLength)).Number; 146 | 147 | do 148 | { 149 | DataLen = min ( Remaining , MaxDataSeg ); 150 | 151 | Tmp = TpAllocatePDU( ConInfo->ConCtx , Opcode , 0 , 0 ); 152 | 153 | if ( !Tmp ) 154 | goto ErrorOut; 155 | else if ( NewPdu ) 156 | InsertTailList ( &NewPdu->Continue , &Tmp->Continue); //Part of 157 | else 158 | NewPdu = Tmp; //Head 159 | 160 | Tmp->Data = Data2; 161 | 162 | Tmp->Bhs->GENERICBHS.Immediate = 1; 163 | REVERSE_3BYTES ( &Tmp->Bhs->GENERICBHS.DataSegmentLength , &DataLen); 164 | /* 165 | Tmp->Bhs->GENERICBHS.DataSegmentLength[0] = (UCHAR)(( DataLen & 0xFF0000 ) >> 16); 166 | Tmp->Bhs->GENERICBHS.DataSegmentLength[1] = (UCHAR)(( DataLen & 0xFF00 ) >> 8); 167 | Tmp->Bhs->GENERICBHS.DataSegmentLength[2] = (UCHAR)( DataLen & 0xFF );*/ 168 | 169 | if ( Opcode == OP_LOGIN_REQ) 170 | Tmp->Bhs->LOGIN_REQUEST.Continue = 1; 171 | else if ( Opcode == OP_TXT_REQ ) 172 | Tmp->Bhs->TXT_REQUEST.Continue = 1; 173 | 174 | //??? 175 | if (Pdu) 176 | RtlCopyMemory ( Tmp->Bhs->TXT_REQUEST.TargetTransferTag, 177 | Pdu->Bhs->TXT_RESPONSE.TargetTransferTag, 178 | 4); 179 | Data2 += DataLen; 180 | 181 | Remaining -= DataLen; 182 | 183 | }while ( Remaining > 0 ); 184 | // 185 | // Set last one's Buffer so we can release it later 186 | // 187 | Tmp->Buffer = Data; 188 | // 189 | // and Continue bit 190 | // 191 | if ( Opcode == OP_LOGIN_REQ) 192 | Tmp->Bhs->LOGIN_REQUEST.Continue = 0; 193 | else if ( Opcode == OP_TXT_REQ ) 194 | Tmp->Bhs->TXT_REQUEST.Continue = 0; 195 | 196 | return NewPdu; 197 | 198 | ErrorOut: 199 | 200 | if (NewPdu) 201 | { 202 | TpQueuePduRelease ( ConInfo->ConCtx , NewPdu ); 203 | /* 204 | while ( !IsListEmpty ( &NewPdu->Continue) ) 205 | { 206 | PPDU Tmp; 207 | PLIST_ENTRY Ent; 208 | Ent = RemoveHeadList ( &NewPdu->Continue ); 209 | Tmp = CONTAINING_RECORD ( Ent , PDU , Continue ); 210 | ExFreePoolWithTag( Tmp , USCSI_TAG ); 211 | } 212 | ExFreePoolWithTag( NewPdu , USCSI_TAG );*/ 213 | } 214 | 215 | return NULL; 216 | } 217 | 218 | /* 219 | Called for normal LOGIN or TEXT response PDU 220 | */ 221 | VOID PiAnswerPdu( PPDU Pdu , PuCONINFO ConInfo , UCHAR Opcode ) 222 | { 223 | LONG DataLen; 224 | PPDU LPdu , LPdu2; 225 | PLIST_ENTRY PduEnt; 226 | 227 | LPdu = ConInfo->Answer; 228 | 229 | if ( !LPdu ) 230 | //Allocate a PDU with DataSegmentLength = 0 , the so-called SYNC PDU 231 | LPdu = TpAllocatePDU( ConInfo->ConCtx , Opcode , 0 , 0 ); 232 | 233 | if ( !IsListEmpty ( &LPdu->Continue) ) 234 | { 235 | // Part of a logical PDU 236 | LPdu2 = CONTAINING_RECORD ( LPdu->Continue.Flink , PDU , Continue ); 237 | ConInfo->Answer = LPdu2; 238 | InitializeListHead ( &LPdu->Continue ); 239 | 240 | if ( Opcode == OP_LOGIN_REQ ) 241 | LPdu->Bhs->LOGIN_REQUEST.Continue = 1; 242 | else if ( Opcode == OP_TXT_REQ) 243 | LPdu->Bhs->TXT_REQUEST.Continue = 1; 244 | } 245 | else 246 | { 247 | // End of a logical PDU 248 | ConInfo->Answer = NULL; 249 | 250 | if ( Opcode == OP_LOGIN_REQ && ConInfo->LoginState == LOGIN_STAT_PENDING_TRANSIT ) 251 | LPdu->Bhs->LOGIN_REQUEST.Transit = 1; 252 | 253 | else if ( Opcode == OP_TXT_REQ && ConInfo->TextState == TXT_STAT_PENDING_COMPLETE) 254 | LPdu->Bhs->TXT_REQUEST.Final = 1; 255 | } 256 | 257 | if ( Opcode == OP_LOGIN_REQ ) 258 | { 259 | LPdu->Bhs->LOGIN_REQUEST.CSG = ConInfo->CSG; 260 | /* 261 | 10.12.3 262 | The next stage value is only valid when the T bit is 1; otherwise, it is reserved 263 | */ 264 | if ( LPdu->Bhs->LOGIN_REQUEST.Transit ) 265 | LPdu->Bhs->LOGIN_REQUEST.NSG = ConInfo->NSG; 266 | 267 | REVERSE_BYTES_SHORT ( &LPdu->Bhs->LOGIN_REQUEST.CID , &ConInfo->CID); 268 | if (!IsLeadingCon(ConInfo)) 269 | REVERSE_BYTES_SHORT ( &LPdu->Bhs->LOGIN_REQUEST.TSIH , &ConInfo->Session->TSIH); 270 | } 271 | /* 272 | DbgPrint("%s LPdu=0x%X CSG=0x%X NSG=0x%X C=0x%X T=0x%X\n" , 273 | __FUNCTION__ , LPdu ,LPdu->Bhs->LOGIN_REQUEST.CSG,LPdu->Bhs->LOGIN_REQUEST.NSG, 274 | LPdu->Bhs->LOGIN_REQUEST.Continue,LPdu->Bhs->LOGIN_REQUEST.Transit);*/ 275 | 276 | TpQueueOutPDU( ConInfo->ConCtx , LPdu ); 277 | } 278 | 279 | // 280 | // TODO Called at the end of a successful nego 281 | // 282 | 283 | VOID PiCommitPending( PuCONINFO ConInfo ) 284 | { 285 | KEY_NAME Key; 286 | UCHAR Tmp; 287 | PKEY_VALUE Kv; 288 | 289 | /* 290 | for ( Key = 0 ; Key < KEY_LastKey ; Key++) 291 | { 292 | Kv = KV( ConInfo , Key ); 293 | 294 | if (!Kv) 295 | continue; 296 | 297 | if (PiIsKeyDeclarative (Key)) 298 | continue; 299 | 300 | Tmp = Kv->Formal; 301 | Kv->Formal = Kv->Pending; 302 | Kv->Pending = Tmp; 303 | }*/ 304 | } 305 | 306 | // 307 | // 308 | // 309 | 310 | __inline ULONG PiHashTaskTag(ULONG Task) 311 | { 312 | return (Task % PENDING_TASK_HASH_SIZE); 313 | } 314 | 315 | // 316 | // 317 | // 318 | 319 | VOID PiQueuePendingTask(PuCONINFO ConInfo , PPDU Pdu) 320 | { 321 | ULONG Slot; 322 | FOUR_BYTE ITT; 323 | 324 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->GENERICBHS.InitiatorTaskTag ); 325 | Slot = PiHashTaskTag ( ITT.AsULong ); 326 | InsertTailList ( &ConInfo->PendingTasks[Slot] , &Pdu->PDUList ); 327 | } 328 | 329 | // 330 | // 331 | // 332 | 333 | VOID PiDequeuePendingTask(PuCONINFO ConInfo , PPDU Pdu) 334 | { 335 | KIRQL OldIrql; 336 | 337 | KeAcquireSpinLock( &ConInfo->PendingTasksLock , &OldIrql); 338 | RemoveHeadList ( Pdu->PDUList.Blink ); 339 | InitializeListHead ( &Pdu->PDUList ); 340 | KeReleaseSpinLock( &ConInfo->PendingTasksLock , OldIrql); 341 | } 342 | 343 | // 344 | // 345 | // 346 | 347 | PPDU PiFindPendingTask(ULONG TaskTag , PuCONINFO ConInfo , BOOLEAN Remove) 348 | { 349 | FOUR_BYTE ITT; 350 | PLIST_ENTRY Entry , Tmp; 351 | PPDU Pdu; 352 | KIRQL OldIrql; 353 | 354 | KeAcquireSpinLock( &ConInfo->PendingTasksLock , &OldIrql); 355 | 356 | Entry = &ConInfo->PendingTasks[ PiHashTaskTag(TaskTag) ]; 357 | 358 | Tmp = Entry->Flink; 359 | 360 | while ( Tmp != Entry ) 361 | { 362 | Pdu = CONTAINING_RECORD (Tmp , PDU , PDUList ); 363 | 364 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->GENERICBHS.InitiatorTaskTag); 365 | if (ITT.AsULong == TaskTag ) 366 | { 367 | if (Remove) 368 | { 369 | RemoveHeadList ( Pdu->PDUList.Blink ); 370 | InitializeListHead ( &Pdu->PDUList ); 371 | } 372 | KeReleaseSpinLock( &ConInfo->PendingTasksLock , OldIrql); 373 | return Pdu; 374 | } 375 | 376 | Tmp = Tmp->Flink; 377 | } 378 | KeReleaseSpinLock( &ConInfo->PendingTasksLock , OldIrql); 379 | return NULL; 380 | } 381 | 382 | // 383 | // Called by TDI when PDU sending complete 384 | // This function may run at DISPATCH_LEVEL 385 | // 386 | 387 | NTSTATUS PiCompletePDU (PDEVICE_OBJECT DevObj , PIRP Irp , PVOID Ctx) 388 | { 389 | PuCONINFO ConInfo; 390 | PPDU Pdu; 391 | FOUR_BYTE ExpectedDataTansferLength , DataSegmentLength , ITT; 392 | UCHAR Opcode; 393 | 394 | Pdu = (PPDU)Ctx; 395 | 396 | ConInfo = Pdu->ConCtx->ConInfo; 397 | Opcode = Pdu->Bhs->GENERICBHS.Opcode; 398 | 399 | KeQueryTickCount ( &Pdu->SendingTime); 400 | 401 | if ( Irp->PendingReturned ) 402 | IoMarkIrpPending( Irp ); 403 | 404 | if ( Opcode == OP_DATA_OUT) 405 | goto Out; 406 | 407 | if (Opcode == OP_SCSI_CMD || 408 | Opcode == OP_SCSI_TASK_REQ || 409 | Opcode == OP_SNACK_REQ ) 410 | goto Queue; 411 | 412 | if ( Opcode == OP_NOP_OUT) 413 | { 414 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->NOP_OUT.InitiatorTaskTag ); 415 | 416 | if ( ITT.AsULong != 0xFFFFFFFF ) 417 | // Echo Expected 418 | // Queue it in Pending Task Queue 419 | goto Queue; 420 | 421 | // Just Release it 422 | goto Release; 423 | } 424 | 425 | if ( Opcode == OP_LOGIN_REQ || 426 | Opcode == OP_LOGOUT_REQ || 427 | Opcode == OP_TXT_REQ) 428 | goto Release; 429 | 430 | Out: 431 | return STATUS_SUCCESS; 432 | 433 | Release: 434 | 435 | TpQueuePduRelease( Pdu->ConCtx , Pdu ); 436 | return STATUS_SUCCESS; 437 | 438 | Queue: 439 | 440 | PiQueuePendingTask ( ConInfo , Pdu ); 441 | return STATUS_SUCCESS; 442 | } 443 | 444 | // 445 | // Release PDU at PASSIVE_LEVEL 446 | // 447 | 448 | VOID PiPduReleaser(IN PVOID Parameter) 449 | { 450 | NTSTATUS Status; 451 | PLIST_ENTRY PduEnt; 452 | PPDU Pdu; 453 | PSESSION Session = (PSESSION)Parameter; 454 | 455 | while (TRUE) 456 | { 457 | Status = 458 | KeWaitForSingleObject(&Session->PduReleaseEvent, Executive, KernelMode, FALSE, NULL ); 459 | KeClearEvent( &Session->PduReleaseEvent ); 460 | 461 | while ( PduEnt = 462 | ExInterlockedRemoveHeadList ( &Session->PduRelease , &Session->PduReleaseLock ) ) 463 | { 464 | Pdu = CONTAINING_RECORD ( PduEnt , PDU , PDUList ); 465 | InitializeListHead ( &Pdu->PDUList); 466 | TpFreePDU( Pdu->ConCtx , Pdu , 0 ); 467 | } 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /uSCSIPort/Protocol.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | BOOLEAN PiInitKeys(); 5 | 6 | // 7 | // Protocol Handlers 8 | // 9 | 10 | VOID PtProcessAsyncMsg ( PPDU Pdu , PuCONINFO ConInfo) 11 | { 12 | KdPrintEx((DPFLTR_USCSI,DBG_PROTOCOL, 13 | "%s ConInfo 0x%p Pdu 0x%p\n" , 14 | __FUNCTION__ , 15 | ConInfo , 16 | Pdu)); 17 | } 18 | 19 | 20 | VOID PtProcessLogout ( PPDU Pdu , PuCONINFO ConInfo) 21 | { 22 | KdPrintEx((DPFLTR_USCSI,DBG_PROTOCOL, 23 | "%s ConInfo 0x%p Pdu 0x%p\n" , 24 | __FUNCTION__ , 25 | ConInfo , 26 | Pdu)); 27 | } 28 | 29 | NTSTATUS PtInit() 30 | { 31 | if ( PiInitKeys() ) 32 | return STATUS_SUCCESS; 33 | 34 | return STATUS_UNSUCCESSFUL; 35 | } 36 | 37 | NTSTATUS PtUnInit() 38 | { 39 | //TODO: 40 | return STATUS_SUCCESS; 41 | } 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /uSCSIPort/Protocol.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _PROTOCOL_H 3 | #define _PROTOCOL_H 4 | 5 | #define BHS_SIZE 48 6 | 7 | #define OP_NOP_OUT 0x00 8 | #define OP_SCSI_CMD 0x01 9 | #define OP_SCSI_TASK_REQ 0x02 10 | #define OP_LOGIN_REQ 0x03 11 | #define OP_TXT_REQ 0x04 12 | #define OP_DATA_OUT 0x05 13 | #define OP_LOGOUT_REQ 0x06 14 | #define OP_SNACK_REQ 0x10 15 | 16 | #define OP_NOP_IN 0x20 17 | #define OP_SCSI_REP 0x21 18 | #define OP_SCSI_TASK_REP 0x22 19 | #define OP_LOGIN_REP 0x23 20 | #define OP_TXT_REP 0x24 21 | #define OP_DATA_IN 0x25 22 | #define OP_LOGOUT_REP 0x26 23 | #define OP_R2T 0x31 24 | #define OP_ASYNC_MSG 0x32 25 | #define OP_REJECT 0x3f 26 | 27 | typedef union _BHS 28 | { 29 | struct _BHS_BUF 30 | { 31 | UCHAR Buf[48]; 32 | }BHS_BUF , *PBHS_BUF; 33 | 34 | struct _GENERICBHS 35 | { 36 | UCHAR Opcode:6; 37 | UCHAR Immediate:1; 38 | UCHAR Reserved1:1; 39 | 40 | UCHAR Opcode_spec:7; 41 | UCHAR Final:1; 42 | 43 | UCHAR Opcode_spec2[2]; 44 | UCHAR TotalAHSLength; 45 | UCHAR DataSegmentLength[3]; 46 | union 47 | { 48 | UCHAR LUN[8]; 49 | UCHAR Opcode_spec3[8]; 50 | }; 51 | UCHAR InitiatorTaskTag[4]; 52 | UCHAR Opcode_spec4[28]; 53 | }GENERICBHS , *PGENERICBHS; 54 | 55 | struct _STAT_FIELDS 56 | { 57 | UCHAR Reserved1[16]; 58 | 59 | UCHAR InitiatorTaskTag[4]; 60 | 61 | UCHAR TargetTransferTag[4]; 62 | 63 | union 64 | { 65 | // 24 66 | UCHAR CmdSN[4]; 67 | UCHAR StatSN[4]; 68 | }; 69 | union 70 | { 71 | // 28 72 | UCHAR ExpStatSN[4]; 73 | UCHAR ExpCmdSN[4]; 74 | }; 75 | union 76 | { 77 | // 32 78 | UCHAR Reserved2[4]; 79 | UCHAR MaxCmdSN[4]; 80 | }; 81 | union 82 | { 83 | // 36 84 | UCHAR Reserved3[4]; 85 | UCHAR DataSN[4]; 86 | UCHAR R2TSN[4]; 87 | UCHAR ExpDataSN[4]; 88 | }; 89 | }STAT_FIELDS , *PSTAT_FIELDS; 90 | //0x01 91 | struct _SCSI_CMD 92 | { 93 | UCHAR Opcode:6; 94 | UCHAR Immediate:1; 95 | UCHAR Reserved:1; 96 | 97 | UCHAR Attr:3; 98 | UCHAR Reserved2:2; 99 | UCHAR Write:1; 100 | UCHAR Read:1; 101 | UCHAR Final:1; 102 | 103 | UCHAR Reserved3[2]; 104 | UCHAR TotalAHSLength; 105 | UCHAR DataSegmentLength[3]; 106 | 107 | UCHAR LUN[8]; 108 | 109 | UCHAR InitiatorTaskTag[4]; 110 | UCHAR ExpectedDataTansferLength[4]; 111 | UCHAR CmdSN[4]; 112 | UCHAR ExpStatSN[4]; 113 | 114 | UCHAR Cdb[16]; 115 | 116 | }SCSI_CMD , *PSCSI_CMD; 117 | //0x21 118 | struct _SCSI_RESPONSE 119 | { 120 | UCHAR Opcode:6; 121 | UCHAR Reserved:2; 122 | 123 | UCHAR Reserved3:1; 124 | UCHAR U:1; 125 | UCHAR O:1; 126 | UCHAR u:1; 127 | UCHAR o:1; 128 | UCHAR Reserved2:2; 129 | UCHAR Final:1; 130 | 131 | UCHAR Response; 132 | UCHAR Status; 133 | 134 | UCHAR TotalAHSLength; 135 | UCHAR DataSegmentLength[3]; 136 | 137 | UCHAR Reserved4[8]; 138 | 139 | UCHAR InitiatorTaskTag[4]; 140 | union 141 | { 142 | UCHAR SNACKTag[4]; 143 | UCHAR Reserved5[4]; 144 | }; 145 | UCHAR StatSN[4]; 146 | UCHAR ExpCmdSN[4]; 147 | UCHAR MaxCmdSN[4]; 148 | union 149 | { 150 | UCHAR ExpDataSN[4]; 151 | UCHAR Reserved6[4]; 152 | }; 153 | union 154 | { 155 | UCHAR BiReadResidualCount[4]; 156 | UCHAR Reserved7[4]; 157 | }; 158 | union 159 | { 160 | UCHAR ResidualCount[4]; 161 | UCHAR Reserved8[4]; 162 | }; 163 | }SCSI_RESPONSE , *PSCSI_RESPONSE; 164 | 165 | //0x02 166 | struct _TASK_MANAGEMENT 167 | { 168 | UCHAR Opcode:6; 169 | UCHAR Immediate:1; 170 | UCHAR Reserved1:1; 171 | 172 | UCHAR Function:7; 173 | UCHAR Final:1; 174 | 175 | UCHAR Reserved2[2]; 176 | UCHAR TotalAHSLength; 177 | UCHAR DataSegmentLength[3]; 178 | union 179 | { 180 | UCHAR LUN[8]; 181 | UCHAR Reserved3[8]; 182 | }; 183 | UCHAR InitiatorTaskTag[4]; 184 | UCHAR RefedTaskTag[4]; 185 | UCHAR CmdSN[4]; 186 | UCHAR ExpStatSN[4]; 187 | union 188 | { 189 | UCHAR RefCmdSN[4]; 190 | UCHAR Reserved4[4]; 191 | }; 192 | union 193 | { 194 | UCHAR ExpDataSN[4]; 195 | UCHAR Reserved5[4]; 196 | }; 197 | UCHAR Reserved6[28]; 198 | }TASK_MANAGEMENT , *PTASK_MANAGEMENT; 199 | 200 | //0x05 201 | struct _SCSI_DATA_OUT 202 | { 203 | UCHAR Opcode:6; 204 | UCHAR Reserved1:2; 205 | 206 | UCHAR Reserved2:7; 207 | UCHAR Final:1; 208 | UCHAR Reserved3[2]; 209 | 210 | UCHAR TotalAHSLength; 211 | UCHAR DataSegmentLength[3]; 212 | union 213 | { 214 | UCHAR LUN[8]; 215 | UCHAR Reserved4[8]; 216 | }; 217 | UCHAR InitiatorTaskTag[4]; 218 | UCHAR TargetTansferTag[4]; 219 | UCHAR Reserved5[4]; 220 | UCHAR ExpStatSN[4]; 221 | UCHAR Reserved6[4]; 222 | UCHAR DataSN[4]; 223 | UCHAR BufferOffset[4]; 224 | UCHAR Reserved7[4]; 225 | }SCSI_DATA_OUT , *PSCSI_DATA_OUT; 226 | //0x25 227 | struct _SCSI_DATA_IN 228 | { 229 | UCHAR Opcode:6; 230 | UCHAR Reserved1:2; 231 | 232 | UCHAR S:1; 233 | UCHAR U:1; 234 | UCHAR O:1; 235 | UCHAR Reserved2:3; 236 | UCHAR Ack:1; 237 | UCHAR Final:1; 238 | 239 | UCHAR Reserved3; 240 | union 241 | { 242 | UCHAR Status; 243 | UCHAR Reserved4; 244 | }; 245 | 246 | UCHAR TotalAHSLength; 247 | UCHAR DataSegmentLength[3]; 248 | union 249 | { 250 | UCHAR LUN[8]; 251 | UCHAR Reserved5[8]; 252 | }; 253 | UCHAR InitiatorTaskTag[4]; 254 | UCHAR TargetTransferTag[4]; 255 | union 256 | { 257 | UCHAR StatSN[4]; 258 | UCHAR Reserved6[4]; 259 | }; 260 | UCHAR ExpCmdSN[4]; 261 | UCHAR MaxCmdSN[4]; 262 | UCHAR DataSN[4]; 263 | UCHAR BufferOffset[4]; 264 | UCHAR ResidualCount[4]; 265 | }SCSI_DATA_IN , *PSCSI_DATA_IN; 266 | 267 | //0x31 268 | struct _R2T 269 | { 270 | UCHAR Opcode:6; 271 | UCHAR Reserved1:2; 272 | 273 | UCHAR Reserved2:7; 274 | UCHAR Final:1; 275 | UCHAR Reserved3[2]; 276 | 277 | UCHAR TotalAHSLength; 278 | UCHAR DataSegmentLength[3]; 279 | UCHAR LUN[8]; 280 | 281 | UCHAR InitiatorTaskTag[4]; 282 | UCHAR TargetTansferTag[4]; 283 | UCHAR StatSN[4]; 284 | UCHAR ExpCmdSN[4]; 285 | UCHAR MaxCmdSN[4]; 286 | UCHAR R2TSN[4]; 287 | UCHAR BufferOffset[4]; 288 | UCHAR DesiredDataTransferLength[4]; 289 | }R2T , *PR2T; 290 | 291 | //0x32 292 | struct _ASYNC_MESSAGE 293 | { 294 | UCHAR Opcode:6; 295 | UCHAR Reserved1:2; 296 | 297 | UCHAR Reserved2:7; 298 | UCHAR Final:1; 299 | UCHAR Reserved3[2]; 300 | 301 | UCHAR TotalAHSLength; 302 | UCHAR DataSegmentLength[3]; 303 | union 304 | { 305 | UCHAR LUN[8]; 306 | UCHAR Reserved4[8]; 307 | }; 308 | UCHAR FFFF[4]; 309 | UCHAR Reserved5[4]; 310 | UCHAR StatSN[4]; 311 | UCHAR ExpCmdSN[4]; 312 | UCHAR MaxCmdSN[4]; 313 | UCHAR AsyncEvent; 314 | UCHAR AsyncVCode; 315 | union 316 | { 317 | UCHAR Parameter1[2]; 318 | UCHAR Reserved6[2]; 319 | }; 320 | union 321 | { 322 | UCHAR Parameter2[2]; 323 | UCHAR Reserved7[2]; 324 | }; 325 | union 326 | { 327 | UCHAR Parameter3[2]; 328 | UCHAR Reserved8[2]; 329 | }; 330 | UCHAR Reserved9[4]; 331 | }ASYNC_MESSAGE , *PASYNC_MESSAGE; 332 | 333 | //0x04 334 | struct _TXT_REQUEST 335 | { 336 | UCHAR Opcode:6; 337 | UCHAR Immediate:1; 338 | UCHAR Reserved1:1; 339 | 340 | UCHAR Reserved2:6; 341 | UCHAR Continue:1; 342 | UCHAR Final:1; 343 | 344 | UCHAR Reserved3[2]; 345 | 346 | UCHAR TotalAHSLength; 347 | UCHAR DataSegmentLength[3]; 348 | union 349 | { 350 | UCHAR LUN[8]; 351 | UCHAR Reserved4[8]; 352 | }; 353 | UCHAR InitiatorTaskTag[4]; 354 | UCHAR TargetTransferTag[4]; 355 | UCHAR CmdSN[4]; 356 | UCHAR ExpStatSN[4]; 357 | UCHAR Reserved5[16]; 358 | 359 | }TXT_REQUEST,*PTXT_REQUEST; 360 | 361 | //0x24 362 | struct _TXT_RESPONSE 363 | { 364 | UCHAR Opcode:6; 365 | UCHAR Reserved1:2; 366 | 367 | UCHAR Reserved2:6; 368 | UCHAR Continue:1; 369 | UCHAR Final:1; 370 | UCHAR Reserved3[2]; 371 | 372 | UCHAR TotalAHSLength; 373 | UCHAR DataSegmentLength[3]; 374 | union 375 | { 376 | UCHAR LUN[8]; 377 | UCHAR Reserved4[8]; 378 | }; 379 | UCHAR InitiatorTaskTag[4]; 380 | UCHAR TargetTransferTag[4]; 381 | UCHAR StatSN[4]; 382 | UCHAR ExpCmdSN[4]; 383 | UCHAR MaxCmdSN[4]; 384 | UCHAR Reserved5[12]; 385 | }TXT_RESPONSE , *PTXT_RESPONSE; 386 | 387 | //0x03 388 | struct _LOGIN_REQUEST 389 | { 390 | UCHAR Opcode:6; 391 | UCHAR Immediate:1; 392 | UCHAR Reserved1:1; 393 | 394 | UCHAR NSG:2; 395 | UCHAR CSG:2; 396 | UCHAR Reserved2:2; 397 | UCHAR Continue:1; 398 | UCHAR Transit:1; 399 | 400 | UCHAR VersionMax; 401 | UCHAR VersionMin; 402 | UCHAR TotalAHSLength; 403 | UCHAR DataSegmentLength[3]; 404 | struct _ISID 405 | { 406 | UCHAR A:6; 407 | UCHAR Type:2; 408 | UCHAR B[2] , C , D[2]; 409 | }ISID; 410 | 411 | UCHAR TSIH[2]; 412 | UCHAR InitiatorTaskTag[4]; 413 | UCHAR CID[2]; 414 | UCHAR Reserved3[2]; 415 | UCHAR CmdSN[4]; 416 | union 417 | { 418 | UCHAR ExpStatSN[4]; 419 | UCHAR Reserved4[4]; 420 | }; 421 | UCHAR Reserved5[16]; 422 | }LOGIN_REQUEST , *PLOGIN_REQUEST; 423 | 424 | //0x23 425 | struct _LOGIN_RESPONSE 426 | { 427 | UCHAR Opcode:6; 428 | UCHAR Immediate:1; 429 | UCHAR Reserved1:1; 430 | 431 | UCHAR NSG:2; 432 | UCHAR CSG:2; 433 | UCHAR Reserved2:2; 434 | UCHAR Continue:1; 435 | UCHAR Transit:1; 436 | 437 | UCHAR VersionMax; 438 | UCHAR VersionActive; 439 | UCHAR TotalAHSLength; 440 | UCHAR DataSegmentLength[3]; 441 | UCHAR ISID[6]; 442 | UCHAR TSIH[2]; 443 | 444 | UCHAR InitiatorTaskTag[4]; 445 | UCHAR Reserved3[4]; 446 | UCHAR StatSN[4]; 447 | UCHAR ExpCmdSN[4]; 448 | UCHAR MaxCmdSN[4]; 449 | 450 | UCHAR StatusClass; 451 | UCHAR StatusDetail; 452 | 453 | UCHAR Reserved4[8]; 454 | 455 | }LOGIN_RESPONSE , *PLOGIN_RESPONSE; 456 | 457 | //0x06 458 | struct _LOGOUT_REQUEST 459 | { 460 | UCHAR Opcode:6; 461 | UCHAR Immediate:1; 462 | UCHAR Reserved1:1; 463 | 464 | UCHAR ReasonCode:7; 465 | UCHAR Final:1; 466 | UCHAR Reserved2[2]; 467 | 468 | UCHAR TotalAHSLength; 469 | UCHAR DataSegmentLength[3]; 470 | 471 | UCHAR Reserved3[8]; 472 | 473 | UCHAR InitiatorTaskTag[4]; 474 | union 475 | { 476 | UCHAR CID[2]; 477 | UCHAR Reserved4[2]; 478 | }; 479 | UCHAR Reserved5[2]; 480 | UCHAR CmdSN[4]; 481 | UCHAR ExpStatSN[4]; 482 | 483 | UCHAR Reserved6[16]; 484 | }LOGOUT_REQUEST , *PLOGOUT_REQUEST; 485 | 486 | //0x26 487 | struct _LOGOUT_RESPONSE 488 | { 489 | UCHAR Opcode:6; 490 | UCHAR Reserved1:2; 491 | 492 | UCHAR Reserved2:7; 493 | UCHAR Final:1; 494 | 495 | UCHAR Response; 496 | UCHAR Reserved3; 497 | 498 | UCHAR TotalAHSLength; 499 | UCHAR DataSegmentLength[3]; 500 | 501 | UCHAR Reserved4[8]; 502 | 503 | UCHAR InitiatorTaskTag[4]; 504 | UCHAR Reserved5[4]; 505 | UCHAR StatSN[4]; 506 | 507 | UCHAR ExpCmdSN[4]; 508 | UCHAR MaxCmdSN[4]; 509 | 510 | UCHAR Reserved6[4]; 511 | UCHAR Time2Wait[2]; 512 | UCHAR Time2Retain[2]; 513 | UCHAR Reserved7[4]; 514 | 515 | }LOGOUT_RESPONSE , *PLOGOUT_RESPONSE; 516 | 517 | //0x10 518 | struct _SNACK 519 | { 520 | UCHAR Opcode:6; 521 | UCHAR Reserved1:2; 522 | 523 | UCHAR Type:4; 524 | UCHAR Reserved2:3; 525 | UCHAR Final:1; 526 | 527 | UCHAR Reserved3[2]; 528 | 529 | UCHAR TotalAHSLength; 530 | UCHAR DataSegmentLength[3]; 531 | union 532 | { 533 | UCHAR LUN[8]; 534 | UCHAR Reserved4[8]; 535 | }; 536 | UCHAR InitiatorTaskTag[4]; 537 | union 538 | { 539 | UCHAR TargetTransferTag[4]; 540 | UCHAR SNACKTag[4]; 541 | }; 542 | UCHAR Reserved5[4]; 543 | UCHAR ExpStatSN[4]; 544 | UCHAR Reserved6[8]; 545 | UCHAR BegRun[4]; 546 | UCHAR RunLength[4]; 547 | 548 | }SNACK , *PSNACK; 549 | 550 | //0x3f 551 | struct _REJECT 552 | { 553 | UCHAR Opcode:6; 554 | UCHAR Reserved1:2; 555 | 556 | UCHAR Reserved2:7; 557 | UCHAR Final:1; 558 | 559 | UCHAR Reason; 560 | UCHAR Reserved3; 561 | 562 | UCHAR TotalAHSLength; 563 | UCHAR DataSegmentLength[3]; 564 | UCHAR Reserved4[8]; 565 | UCHAR FFFF[4]; 566 | 567 | UCHAR StatSN[4]; 568 | UCHAR ExpCmdSN[4]; 569 | UCHAR MaxCmdSN[4]; 570 | union 571 | { 572 | UCHAR DataSN[4]; 573 | UCHAR R2TSN[4]; 574 | UCHAR Reserved5[4]; 575 | }; 576 | UCHAR Reserved6[8]; 577 | 578 | }REJECT , *PREJECT; 579 | 580 | //0x00 581 | struct _NOP_OUT 582 | { 583 | UCHAR Opcode:6; 584 | UCHAR Immediate:1; 585 | UCHAR Reserved1:1; 586 | 587 | UCHAR Reserved2:7; 588 | UCHAR Final:1; 589 | UCHAR Reserved3[2]; 590 | 591 | UCHAR TotalAHSLength; 592 | UCHAR DataSegmentLength[3]; 593 | union 594 | { 595 | UCHAR LUN[8]; 596 | UCHAR Reserved4[8]; 597 | }; 598 | UCHAR InitiatorTaskTag[4]; 599 | UCHAR TargetTransferTag[4]; 600 | UCHAR CmdSN[4]; 601 | UCHAR ExpStatSN[4]; 602 | UCHAR Reserved5[16]; 603 | }NOP_OUT , *PNOP_OUT; 604 | 605 | //0x20 606 | struct _NOP_IN 607 | { 608 | UCHAR Opcode:6; 609 | UCHAR Reserved1:2; 610 | 611 | UCHAR Reserved2:7; 612 | UCHAR Final:1; 613 | UCHAR Reserved3[2]; 614 | 615 | UCHAR TotalAHSLength; 616 | UCHAR DataSegmentLength[3]; 617 | union 618 | { 619 | UCHAR LUN[8]; 620 | UCHAR Reserved4[8]; 621 | }; 622 | UCHAR InitiatorTaskTag[4]; 623 | UCHAR TargetTransferTag[4]; 624 | UCHAR StatSN[4]; 625 | UCHAR ExpCmdSN[4]; 626 | UCHAR MaxCmdSN[4]; 627 | UCHAR Reserved5[12]; 628 | }NOP_IN , *PNOP_IN; 629 | 630 | }BHS , *PBHS; 631 | 632 | typedef struct _AHS 633 | { 634 | UCHAR AHSLength[2]; 635 | UCHAR AHSType; 636 | UCHAR AHS_spec[1]; 637 | }AHS, *PAHS; 638 | 639 | #define AHS_TYPE_EX_CDB 0x01 640 | #define AHS_TYPE_BIDIR_READ_LENGTH 0x02 641 | 642 | // 643 | // Keys 644 | // 645 | 646 | #define KEY_DIR_INITIATOR 0x00000001 647 | #define KEY_DIR_TARGET 0x00000002 648 | #define KEY_DIR_BOTH (KEY_DIR_INITIATOR|KEY_DIR_TARGET) 649 | #define KEY_DIR_MASK KEY_DIR_BOTH 650 | 651 | #define KEY_PHASE_IALL 0x00000004 //by Initiator , All phases 652 | #define KEY_PHASE_TALL 0x00000008 //by Target , All phases 653 | 654 | #define KEY_PHASE_ILO 0x00000010 //by Initiator , Leading Connection Only 655 | #define KEY_PHASE_IIO 0x00000020 //by Initiator , Initialization only 656 | #define KEY_PHASE_ISEC 0x00000040 //by Initiator , Security Nego Stage only 657 | #define KEY_PHASE_IFFPO 0x00000080 //by Initiator , Full Feature Phase only 658 | 659 | #define KEY_PHASE_IMASK (KEY_PHASE_ILO|KEY_PHASE_IIO|KEY_PHASE_IFFPO|KEY_PHASE_IALL) 660 | 661 | #define KEY_PHASE_TLO 0x00000100 //by Target , Leading Connection only 662 | #define KEY_PHASE_TIO 0x00000200 663 | #define KEY_PHASE_TSEC 0x00000400 664 | #define KEY_PHASE_TFFPO 0x00000800 665 | 666 | #define KEY_PHASE_TMASK (KEY_PHASE_TLO|KEY_PHASE_TIO|KEY_PHASE_TFFPO|KEY_PHASE_TALL) 667 | 668 | #define KEY_DECLARATIVE 0x00001000 // 669 | 670 | #define KEY_SCOPE_CO 0x00002000 //Connection wide 671 | #define KEY_SCOPE_SW 0x00004000 //Session wide 672 | 673 | #define KEY_IRRELEVANT_WHEN_DISCOVERY 0x00008000 674 | 675 | #define KEY_FUNC_MIN 0x00010000 676 | #define KEY_FUNC_MAX 0x00020000 677 | #define KEY_FUNC_OR 0x00040000 678 | #define KEY_FUNC_AND 0x00080000 679 | 680 | #define KEY_TYPE_STRING 0x00100000 681 | #define KEY_TYPE_BOOLEAN 0x00200000 682 | #define KEY_TYPE_NUMBER 0x00400000 683 | #define KEY_TYPE_LOV 0x00800000 684 | #define KEY_TYPE_BINARY_NUMBER 0x01000000 685 | 686 | #define KEY_DEREGISTERED 0x10000000 687 | 688 | #define KEY_NEGO_FAIL_INGORE 0x01 689 | #define KEY_NEGO_FAIL_CLOSE 0x02 690 | 691 | #define KEY_NEGO_MAX_RETRIES 0x01 692 | 693 | typedef struct _KEY_VALUE KEY_VALUE , *PKEY_VALUE; 694 | 695 | typedef VOID (*KEYHANDLER)(KeyVal , ConInfo , Questions , Answers ); 696 | 697 | typedef struct _KEY_META 698 | { 699 | PUCHAR Key; 700 | UCHAR KeyLen; 701 | BOOLEAN RngValid; 702 | ULONG Low , High; 703 | ULONG Attrs; 704 | UCHAR NegoFailAction:2; 705 | UCHAR NegoRetries:6; 706 | KEYHANDLER Handler; 707 | }KEY_META , *PKEY_META; 708 | 709 | #define PiKeyName(K) (PiKeys[K].Key) 710 | #define PiKeyAttrs(K) (PiKeys[K].Attrs) 711 | #define PiIsKeyDeclarative(K) (PiKeys[K].Attrs & KEY_DECLARATIVE) 712 | #define PiIsKeyBool(K) (PiKeys[K].Attrs & KEY_TYPE_BOOLEAN) 713 | #define PiIsKeyString(K) (PiKeys[K].Attrs & KEY_TYPE_STRING) 714 | #define PiIsKeyLov(K) (PiKeys[K].Attrs & KEY_TYPE_LOV) 715 | #define PiIsKeyNumber(K) (PiKeys[K].Attrs & KEY_TYPE_NUMBER) 716 | 717 | #define PiIsKeyDeregistered(K) (PiKeys[K].Attrs & KEY_DEREGISTERED) 718 | 719 | typedef enum _KEY_NAME KEY_NAME; 720 | 721 | typedef struct _KEY_VALUE 722 | { 723 | LIST_ENTRY Vals; 724 | ULONG Size; 725 | ULONG Flags; 726 | UCHAR Formal , Pending; 727 | 728 | struct _Key 729 | { 730 | KEY_NAME D; //Predefined 731 | union 732 | { 733 | PUCHAR U; //Unknown 734 | }; 735 | }Key; 736 | /*Negotiations MUST be handled as 737 | atomic operations. For example, all negotiated values go into effect 738 | after the negotiation concludes in agreement or are ignored if the 739 | negotiation fails. 740 | */ 741 | union 742 | { 743 | BOOLEAN Bool; 744 | PUCHAR String; 745 | ULONG Number; 746 | struct _Lov 747 | { 748 | PUCHAR Val; 749 | PUCHAR Sel; 750 | }Lov; 751 | struct _Range 752 | { 753 | ULONG Low , High; 754 | }Range; 755 | }Value[2]; 756 | 757 | UCHAR NegoRetries; 758 | 759 | ULONG Len; // Key Pair (key=val) Length , including null terminator 760 | PUCHAR FormatStr; // Formatted Key Pair String 761 | }KEY_VALUE , *PKEY_VALUE; 762 | 763 | #define KEY_F 0x00 764 | #define KEY_P 0x01 765 | 766 | #define KEY_FV(k) (k)->Value[(k)->Formal] 767 | #define KEY_PV(k) (k)->Value[(k)->Pending] 768 | 769 | // 770 | // for KEY_DECLARATIVE Key 771 | // Value[KEY_INITIATOR] store initiator value 772 | // Value[KEY_TARGET] store target value 773 | // 774 | #define KEY_I 0x00 775 | #define KEY_T 0x01 776 | 777 | #define KEY_IV(k) (k)->Value[KEY_I] 778 | #define KEY_TV(k) (k)->Value[KEY_T] 779 | 780 | #define KV(ConInfo , Key) \ 781 | ( PiKeys[Key].Attrs & KEY_SCOPE_SW ? \ 782 | ConInfo->Session->Parameter[Key]: \ 783 | ConInfo->Parameter[KEY_CON_IDX(Key)]) 784 | 785 | #define AddrKV(ConInfo , Key) \ 786 | ( PiKeys[Key].Attrs & KEY_SCOPE_SW ? \ 787 | &ConInfo->Session->Parameter[Key]: \ 788 | &ConInfo->Parameter[KEY_CON_IDX(Key)]) 789 | 790 | // KEY_VALUE + FormatStr 791 | #define PiAllocateKeyValFormatStr(Len) PiAllocateKeyVal(0,0,Len) 792 | // KEY_VALUE + Value.String 793 | #define PiAllocateKeyValStr(Len,Len2) PiAllocateKeyVal(Len,Len2,0) 794 | #define PiAllocateKeyValStr0(Len) PiAllocateKeyVal(Len,0,0) 795 | #define PiAllocateKeyValStr1(Len) PiAllocateKeyVal(0,Len,0) 796 | // KEY_VALUE 797 | #define PiAllocateKeyValue() PiAllocateKeyVal(0,0,0) 798 | // This function should put here , but just for convenience 799 | PKEY_VALUE PiAllocateKeyVal(ULONG StrLen , ULONG StrLen2 , ULONG StrLen3); 800 | 801 | //Key nego states 802 | #define KEY_Defaulted 0x00000001 803 | #define KEY_Proposed 0x00000002 804 | #define KEY_Pending 0x00000004 805 | #define KEY_Agreed 0x00000008 806 | #define KEY_STATE_MASK (KEY_Defaulted | KEY_Proposed | KEY_Pending |KEY_Agreed ) 807 | 808 | #define KEY_Formatted 0x00000010 809 | // Number Format 810 | #define KEY_FMT_NUM_DEC 0x00000020 811 | #define KEY_FMT_NUM_HEX 0x00000040 812 | #define KEY_FMT_NUM_BASE64 0x00000080 813 | // Format Pending Value 814 | #define KEY_FMT_PENDING 0x00000100 815 | 816 | #define KEY_Illegal 0x00800000 817 | #define KEY_Irrelevant 0x01000000 818 | #define KEY_Reject 0x02000000 819 | #define KEY_None 0x04000000 820 | //The Value is "NotUnderstood" 821 | #define KEY_NotUnderstood 0x08000000 822 | //The Key is NotUnderstood 823 | #define KEY_NotUnderstood2 0x10000000 824 | 825 | // Key Indexes 826 | 827 | typedef enum _KEY_NAME 828 | { 829 | KEY_MaxConnections = 0 , 830 | KEY_SendTargets , 831 | KEY_TargetName , 832 | KEY_InitiatorName, 833 | KEY_TargetAlias , 834 | KEY_InitiatorAlias, 835 | KEY_TargetAddress, 836 | KEY_TargetPortalGroupTag, 837 | KEY_InitialR2T , 838 | KEY_ImmediateData, 839 | KEY_MaxBurstLength, 840 | KEY_FirstBurstLength, 841 | KEY_DefaultTime2Wait, 842 | KEY_DefaultTime2Retain, 843 | KEY_MaxOutstandingR2T, 844 | KEY_DataPDUInOrder, 845 | KEY_DataSequenceInOrder, 846 | KEY_ErrorRecoveryLevel, 847 | KEY_SessionType, 848 | 849 | KEY_MaxSessionKey, 850 | 851 | // Followings are Connection Wide 852 | KEY_HeaderDigest, 853 | KEY_DataDigest, 854 | KEY_MaxRecvDataSegmentLength, 855 | KEY_AuthMethod, 856 | 857 | KEY_LastKey 858 | } KEY_NAME; 859 | 860 | #define KEYIN(k) \ 861 | PROT_Debug(("%s Key <<< " , __FUNCTION__ )); \ 862 | PiDumpKey( k ) 863 | 864 | #define KEYOUT(k) \ 865 | PROT_Debug(("%s Key >>> " , __FUNCTION__ )); \ 866 | PiDumpKey( k ) 867 | 868 | extern PKEY_META PiKeys; 869 | 870 | typedef struct _PDU PDU , *PPDU; 871 | typedef struct _uCONINFO uCONINFO,*PuCONINFO; 872 | 873 | typedef union _THREE_BYTE 874 | { 875 | struct 876 | { 877 | UCHAR Byte0; 878 | UCHAR Byte1; 879 | UCHAR Byte2; 880 | }; 881 | 882 | ULONG AsULong:24; 883 | 884 | }THREE_BYTE , *PTHREE_BYTE; 885 | 886 | #define REVERSE_3BYTES(Destination, Source) { \ 887 | PTHREE_BYTE d = (PTHREE_BYTE)(Destination); \ 888 | PTHREE_BYTE s = (PTHREE_BYTE)(Source); \ 889 | d->Byte2 = s->Byte0; \ 890 | d->Byte1 = s->Byte1; \ 891 | d->Byte0 = s->Byte2; \ 892 | } 893 | 894 | 895 | // 896 | // Response.c 897 | // 898 | VOID 899 | PtProcessResponse ( 900 | PPDU Pdu , 901 | PuCONINFO ConInfo 902 | ); 903 | // 904 | // Data.c 905 | // 906 | VOID 907 | PtProcessDataIn ( 908 | PPDU Pdu , 909 | PuCONINFO ConInfo 910 | ); 911 | // 912 | // R2T.c 913 | // 914 | VOID 915 | PtProcessR2T( 916 | PPDU Pdu , 917 | PuCONINFO ConInfo 918 | ); 919 | // 920 | // Task.c 921 | // 922 | VOID 923 | PtProcessTask ( 924 | PPDU Pdu , 925 | PuCONINFO ConInfo 926 | ); 927 | // 928 | // Text.c 929 | // 930 | VOID 931 | PtProcessText( 932 | PPDU Pdu , 933 | PuCONINFO ConInfo 934 | ); 935 | // 936 | // Login.c 937 | // 938 | VOID 939 | PtProcessLogin( 940 | PPDU Pdu , 941 | PuCONINFO ConInfo 942 | ); 943 | 944 | NTSTATUS 945 | PtLogin( 946 | PuCONINFO ConInfo 947 | ); 948 | 949 | VOID 950 | PtProcessLogout ( 951 | PPDU Pdu , 952 | PuCONINFO ConInfo 953 | ); 954 | // 955 | // Reject.c 956 | // 957 | VOID 958 | PtProcessReject ( 959 | PPDU Pdu , 960 | PuCONINFO ConInfo 961 | ); 962 | // 963 | // Nop.c 964 | // 965 | VOID 966 | PtProcessNopIn ( 967 | PPDU Pdu , 968 | PuCONINFO ConInfo 969 | ); 970 | 971 | VOID 972 | PtNopOut ( 973 | PuCONINFO ConInfo , 974 | BOOLEAN Echo 975 | ); 976 | // 977 | // Protocol.c 978 | // 979 | NTSTATUS 980 | PtInit(); 981 | 982 | NTSTATUS 983 | PtUnInit(); 984 | 985 | VOID 986 | PtProcessAsyncMsg ( 987 | PPDU Pdu , 988 | PuCONINFO ConInfo 989 | ); 990 | 991 | // Cmd.c 992 | PPDU 993 | PtAssembleSCSICmd ( 994 | PuCONINFO ConInfo , 995 | PUCHAR Cdb , 996 | ULONG CdbLength , 997 | PUCHAR DataBuffer , 998 | ULONG WriteSize , 999 | ULONG ReadSize, 1000 | UCHAR TaskAttr, 1001 | UCHAR Dir 1002 | ); 1003 | #endif -------------------------------------------------------------------------------- /uSCSIPort/R2T.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | PPDU PiFindPendingTask(ULONG TaskTag , PuCONINFO ConInfo , BOOLEAN Remove); 5 | 6 | __inline VOID PiSendDataOut(PuCON_CTX Ctx , PPDU CmdOrR2T ); 7 | 8 | PPDU PiQueueDataInOrR2T(PuCONINFO ConInfo , PPDU Cmd , PPDU DataInOrR2T); 9 | 10 | PPDU PiAllocateDataOutPDU( 11 | PuCONINFO ConInfo , PUCHAR DataBuf , ULONG Offset, ULONG DataSize,PULONG DataSN,PPDU RefCmd); 12 | 13 | // 14 | // 15 | // 16 | 17 | VOID PtProcessR2T(PPDU Pdu , PuCONINFO ConInfo) 18 | { 19 | PPDU Cmd; 20 | PSESSION Session; 21 | PuCON_CTX Ctx; 22 | FOUR_BYTE ITT; 23 | 24 | Ctx = ConInfo->ConCtx; 25 | Session = ConInfo->Session; 26 | 27 | InterlockedIncrement ( (PLONG)&Session->R2TCount ); 28 | 29 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->R2T.InitiatorTaskTag); 30 | Cmd = PiFindPendingTask ( ITT.AsULong , ConInfo , FALSE); 31 | Pdu->Cmd = Cmd; 32 | 33 | Pdu = PiQueueDataInOrR2T ( ConInfo , Cmd , Pdu ); 34 | if ( Pdu ) 35 | { 36 | TpQueueR2TPDU ( Ctx , Pdu ); 37 | } 38 | } 39 | 40 | // 41 | // Sender for R2Ts 42 | // 43 | 44 | VOID PiR2TWorker(IN PVOID Parameter) 45 | { 46 | NTSTATUS Status; 47 | PSESSION Session; 48 | PWORKER_THREAD_CTX Tctx; 49 | PuCONINFO ConInfo; 50 | PuCON_CTX Ctx; 51 | PLIST_ENTRY R2TEnt; 52 | PPDU R2T, Cmd, DataOut; 53 | ULONG DesiredDataTransferLength; 54 | ULONG MaxRecvDataSegmentLength , MaxBurstLength; 55 | ULONG Offset = 0 ; 56 | 57 | Status = STATUS_SUCCESS; 58 | 59 | Tctx = (PWORKER_THREAD_CTX)Parameter; 60 | Ctx = Tctx->Ctx; 61 | ConInfo = Ctx->ConInfo; 62 | Session = ConInfo->Session; 63 | 64 | MaxRecvDataSegmentLength = KEY_TV(KV(ConInfo , KEY_MaxRecvDataSegmentLength)).Number; 65 | MaxBurstLength = KEY_FV(KV(ConInfo,KEY_MaxBurstLength)).Number; 66 | /* 67 | Within a connection, outstanding R2Ts MUST be fulfilled by the 68 | initiator in the order in which they were received 69 | */ 70 | while (TRUE ) 71 | { 72 | Status = KeWaitForSingleObject ( &Ctx->R2TEvent , 73 | Executive , 74 | KernelMode , 75 | FALSE , 76 | NULL ); 77 | 78 | KeClearEvent(&Ctx->R2TEvent); 79 | 80 | while ( R2TEnt = ExInterlockedRemoveHeadList ( &Ctx->R2Ts , &Ctx->R2TLock ) ) 81 | { 82 | R2T = (PPDU)CONTAINING_RECORD ( R2TEnt , PDU , PDUList ); 83 | InitializeListHead ( &R2T->PDUList); 84 | Cmd = R2T->Cmd; 85 | /* 86 | The Desired Data 87 | Transfer Length MUST NOT be 0 and MUST not exceed MaxBurstLength p143 88 | */ 89 | REVERSE_BYTES ( &DesiredDataTransferLength , 90 | &R2T->Bhs->R2T.DesiredDataTransferLength); 91 | REVERSE_BYTES ( &Offset , &R2T->Bhs->R2T.BufferOffset); 92 | 93 | #ifdef STRICT_PROT_CHECK 94 | if ( DesiredDataTransferLength > MaxBurstLength || !DesiredDataTransferLength) 95 | PiProtError( ConInfo , __FUNCTION__ ); 96 | #endif 97 | /* 98 | DbgPrint("%s DataBuffer=0x%X Offset=0x%X DesiredDataTransferLength=0x%X\n", 99 | __FUNCTION__ , Cmd->DataBuffer,Offset,DesiredDataTransferLength);*/ 100 | DataOut = PiAllocateDataOutPDU ( ConInfo , 101 | Cmd->DataBuffer, 102 | Offset , 103 | DesiredDataTransferLength, 104 | &R2T->DataSN , 105 | R2T ); 106 | 107 | InsertTailList ( &R2T->DataOut , &DataOut->DataOut ); 108 | 109 | InterlockedDecrement ( (PLONG)&Session->R2TCount ); 110 | 111 | PiSendDataOut ( Ctx , R2T ); 112 | } 113 | } 114 | 115 | ExFreePoolWithTag ( Tctx , USCSI_TAG ); 116 | //Ctx->WorkItemFlags |= CONCTX_R2T_SAFE; 117 | } -------------------------------------------------------------------------------- /uSCSIPort/Reject.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID PtProcessReject ( PPDU Pdu , PuCONINFO ConInfo) 5 | { 6 | PROT_Debug(("%s ConInfo 0x%p Pdu 0x%p\n" , __FUNCTION__ , ConInfo , Pdu)); 7 | } -------------------------------------------------------------------------------- /uSCSIPort/Response.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | PPDU PiFindPendingTask(ULONG TaskTag , PuCONINFO ConInfo , BOOLEAN Remove); 5 | 6 | BOOLEAN PiCheckDataInHole(PPDU Cmd ); 7 | 8 | // 9 | // 10 | // 11 | BOOLEAN PiCheckCmdComplete( PPDU Cmd ); 12 | 13 | VOID PtProcessResponse(PPDU Pdu, PuCONINFO ConInfo) 14 | { 15 | PPDU Cmd; 16 | FOUR_BYTE ITT , ExpDataSN , BRC , RC; 17 | 18 | REVERSE_BYTES ( &BRC , &Pdu->Bhs->SCSI_RESPONSE.BiReadResidualCount); 19 | REVERSE_BYTES ( &RC , &Pdu->Bhs->SCSI_RESPONSE.ResidualCount); 20 | 21 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->SCSI_RESPONSE.InitiatorTaskTag); 22 | Cmd = PiFindPendingTask ( ITT.AsULong , ConInfo , FALSE); 23 | Pdu->Cmd = Cmd; 24 | /* 25 | DbgPrint( 26 | "%s ITT=0x%X Pdu=0x%p UOuo=(%d%d%d%d) Response=0x%X Status=0x%X BRC=0x%X RC=0x%X\n" , 27 | __FUNCTION__ , 28 | ITT.AsULong , 29 | Pdu, 30 | Pdu->Bhs->SCSI_RESPONSE.U, 31 | Pdu->Bhs->SCSI_RESPONSE.O, 32 | Pdu->Bhs->SCSI_RESPONSE.u, 33 | Pdu->Bhs->SCSI_RESPONSE.o, 34 | Pdu->Bhs->SCSI_RESPONSE.Response, 35 | Pdu->Bhs->SCSI_RESPONSE.Status, 36 | BRC.AsULong , 37 | RC.AsULong);*/ 38 | 39 | REVERSE_BYTES ( &ExpDataSN , &Pdu->Bhs->SCSI_RESPONSE.ExpDataSN); 40 | Cmd->ExpDataSN = ExpDataSN.AsULong; 41 | // 42 | // Queue Response , its processing may be delayed 43 | // if there are any missing Data-In/R2T(s) 44 | // 45 | InsertTailList ( &Cmd->Resps , &Pdu->Resps ); 46 | 47 | if ( !PiCheckDataInHole ( Cmd ) ) 48 | { 49 | Cmd->Flags |= PDU_F_CMD_CAN_COMPLETE; 50 | TpQueuePendingCompleteCmd ( Cmd->ConCtx , Cmd ); 51 | } 52 | } -------------------------------------------------------------------------------- /uSCSIPort/SNACK.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID PtSNACK (PuCONINFO ConInfo) 5 | { 6 | PPDU Pdu; 7 | 8 | Pdu = TpAllocatePDU ( ConInfo->ConCtx , OP_SNACK_REQ , 0 , 0 ); 9 | } -------------------------------------------------------------------------------- /uSCSIPort/SOURCES: -------------------------------------------------------------------------------- 1 | TARGETNAME=uSCSIPort 2 | TARGETTYPE=EXPORT_DRIVER 3 | TARGETPATH=..\obj 4 | TARGETLIBS=$(SDK_LIB_PATH)\tdi.lib 5 | INCLUDES=. 6 | SOURCES=uSCSI.c \ 7 | Transport.c \ 8 | TransUtils.c \ 9 | Protocol.c \ 10 | ProtUtils.c \ 11 | Key.c \ 12 | Login.c \ 13 | Discover.c \ 14 | Text.c \ 15 | Handler.c \ 16 | Nop.c \ 17 | Cmd.c \ 18 | Data.c \ 19 | R2T.c \ 20 | Reject.c \ 21 | Response.c \ 22 | Task.c 23 | 24 | -------------------------------------------------------------------------------- /uSCSIPort/Task.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID PtProcessTask ( PPDU Pdu , PuCONINFO ConInfo) 5 | { 6 | PROT_Debug(("%s ConInfo 0x%p Pdu 0x%p\n" , __FUNCTION__ , ConInfo , Pdu)); 7 | } -------------------------------------------------------------------------------- /uSCSIPort/Text.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | VOID PiProcessKey( PPDU Pdu , PuCONINFO ConInfo , BOOLEAN AllocatePDU ,UCHAR Opcode); 5 | 6 | VOID PiAnswerPdu( PPDU Pdu , PuCONINFO ConInfo , UCHAR Opcode ); 7 | 8 | BOOLEAN PiCanTxtComplete (PuCONINFO ConInfo ); 9 | 10 | VOID PiProtError (PuCONINFO ConInfo , PUCHAR Func ); 11 | 12 | #define PiAnswerTextPdu( Pdu , ConInfo) \ 13 | PiAnswerPdu ( Pdu , ConInfo , OP_TXT_REQ) 14 | 15 | #define PiTextProcessKey(Pdu , ConInfo , AllocatePDU ) \ 16 | PiProcessKey (Pdu , ConInfo , AllocatePDU , OP_TXT_REQ) 17 | 18 | // 19 | // 20 | // 21 | 22 | VOID PtProcessText( PPDU Pdu , PuCONINFO ConInfo) 23 | { 24 | PPDU LPdu = NULL; 25 | 26 | PROT_Debug(("%s ConInfo 0x%p Pdu 0x%p\n" , __FUNCTION__ , ConInfo , Pdu)); 27 | 28 | if ( ConInfo->Answer ) 29 | { 30 | #ifdef STRICT_PROT_CHECK 31 | if ( DataSegLen ) 32 | PiProtError ( ConInfo , __FUNCTION__ ); 33 | #endif 34 | goto Answer; 35 | } 36 | 37 | /*An initiator receiving a Text or Login 38 | Response with the C bit set to 1 MUST answer with a Text or Login 39 | Request with no data segment (DataSegmentLength 0).*/ 40 | if ( Pdu->Bhs->TXT_RESPONSE.Continue ) 41 | { 42 | #ifdef STRICT_PROT_CHECK 43 | if ( Pdu->Bhs->TXT_RESPONSE.Transit ) 44 | PiProtError( ConInfo , __FUNCTION__ ); 45 | #endif 46 | if ( !ConInfo->Logical ) 47 | ConInfo->Logical = Pdu; //Head 48 | else 49 | // Part of 50 | InsertTailList ( &ConInfo->Logical->Continue, &Pdu->Continue ); 51 | goto Answer; 52 | } 53 | // 54 | // Logical Pdu completed 55 | // 56 | if (ConInfo->Logical ) 57 | { 58 | LPdu = ConInfo->Logical; 59 | ConInfo->Logical = NULL; 60 | } 61 | else 62 | LPdu = Pdu; 63 | 64 | PiTextProcessKey ( LPdu , 65 | ConInfo , 66 | /* 67 | A Text Response with the F bit set to 1 MUST NOT contain key=value 68 | pairs that may require additional answers from the initiator 69 | */ 70 | !LPdu->Bhs->TXT_RESPONSE.Final ); 71 | #ifdef STRICT_PROT_CHECK 72 | if ( ConInfo->TextState == TXT_STAT_STARTED && Pdu->Bhs->TXT_RESPONSE.Final) 73 | { 74 | PiProtError (ConInfo , __FUNCTION__); 75 | } 76 | else if 77 | #else 78 | if 79 | #endif 80 | ( ConInfo->TextState == TXT_STAT_STARTED && 81 | !Pdu->Bhs->TXT_RESPONSE.Final) 82 | { 83 | if ( PiCanTxtComplete(ConInfo) ) 84 | { 85 | PROT_Debug(("%s Completion Requested\n" , __FUNCTION__)); 86 | ConInfo->TextState = TXT_STAT_PENDING_COMPLETE; 87 | } 88 | } 89 | else if ( ConInfo->TextState == TXT_STAT_PENDING_COMPLETE && 90 | Pdu->Bhs->TXT_RESPONSE.Final ) 91 | { 92 | // End Conversation 93 | PROT_Debug(("%s Completion Agreed\n" , __FUNCTION__)); 94 | ConInfo->TextState = TXT_STAT_IDLE; 95 | return; 96 | } 97 | else if ( ConInfo->TextState == TXT_STAT_PENDING_COMPLETE && 98 | !Pdu->Bhs->TXT_RESPONSE.Final) 99 | { 100 | //NewPdu->Bhs->TXT_REQUEST.Final = 1; 101 | } 102 | 103 | Answer: 104 | 105 | PiAnswerTextPdu( Pdu , ConInfo ); 106 | } 107 | 108 | /* 109 | Logic for ending a Text conversation 110 | */ 111 | BOOLEAN PiCanTxtComplete (PuCONINFO ConInfo ) 112 | { 113 | if (ConInfo->MoreWorks) 114 | return FALSE; 115 | 116 | return TRUE; 117 | } -------------------------------------------------------------------------------- /uSCSIPort/Transport.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | BOOLEAN TiVerifyDataDigest( IN PuCON_CTX Ctx, IN PPDU Pdu); 5 | 6 | BOOLEAN TiVerifyHeaderDigest( IN PuCON_CTX Ctx, IN PPDU Pdu); 7 | 8 | PPDU TiAllocatePDU( IN PuCON_CTX Ctx ); 9 | 10 | NTSTATUS PiCompletePDU (IN PDEVICE_OBJECT DevObj , IN PIRP Irp , IN PVOID Ctx); 11 | 12 | // 13 | // PDU Assembler 14 | // 15 | 16 | // Called at DISPATCH_LEVEL 17 | // for Now PDU buffer is allocated from NonPagedPool 18 | 19 | VOID TiReceive( 20 | IN PVOID TdiEventContext, 21 | IN CONNECTION_CONTEXT ConnectionContext, 22 | IN ULONG ReceiveFlags, 23 | IN ULONG BytesIndicated, 24 | IN ULONG BytesAvailable, 25 | OUT ULONG *BytesTaken, 26 | IN PVOID Tsdu, 27 | OUT PIRP *IoRequestPacket) 28 | { 29 | NTSTATUS Status; 30 | PUCHAR PTsdu; 31 | PuCON_CTX Ctx; 32 | PPDU Pdu; 33 | ULONG Remaining; 34 | ULONG Copied; 35 | THREE_BYTE DataSegmentLength; 36 | 37 | Status = STATUS_SUCCESS; 38 | Ctx = (PuCON_CTX)ConnectionContext; 39 | PTsdu = (PUCHAR)Tsdu; 40 | /* 41 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT, 42 | "%s ReceiveFlags 0x%X BytesIndicated 0x%X BytesAvailable 0x%X Tsdu 0x%p\n" , 43 | __FUNCTION__ , 44 | ReceiveFlags , 45 | BytesIndicated , 46 | BytesAvailable, 47 | Tsdu));*/ 48 | 49 | Remaining = BytesIndicated; 50 | 51 | while ( Remaining > 0) 52 | { 53 | if ( !Ctx->PendingPDU ) 54 | { 55 | Pdu = TiAllocatePDU (Ctx); 56 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT,"%s Process Pdu 0x%p\n" , __FUNCTION__,Pdu )); 57 | } 58 | else 59 | Pdu = Ctx->PendingPDU; 60 | 61 | //BHS 62 | if (Pdu->Bytes < BHS_SIZE) 63 | { 64 | Copied = min ( (BHS_SIZE - Pdu->Bytes) , Remaining); 65 | RtlCopyMemory ( (PUCHAR)Pdu->Bhs + Pdu->Bytes , PTsdu , Copied ); 66 | 67 | PTsdu += Copied; 68 | Remaining -= Copied; 69 | Pdu->Bytes += Copied; 70 | *BytesTaken += Copied; 71 | 72 | if (Pdu->Bytes < BHS_SIZE) 73 | Ctx->PendingPDU = Pdu; //Pending for BHS 74 | else 75 | { 76 | // BHS Completed , allocate buffer for AHS or DATA 77 | if (!TiVerifyHeaderDigest( Ctx , Pdu ) ) 78 | { 79 | // Close connection and return 80 | } 81 | /* 82 | TotalAHSLength: 83 | 84 | Total length of all AHS header segments in units of four byte words 85 | including padding, if any. p115 86 | */ 87 | Pdu->BufferSize = Pdu->Bhs->GENERICBHS.TotalAHSLength << 2; 88 | 89 | /* 90 | DataSegmentLength: 91 | 92 | This is the data segment payload length in bytes (excluding padding). 93 | The DataSegmentLength MUST be 0 whenever the PDU has no data segment. p115 94 | 95 | The (optional) Data Segment contains PDU associated data. Its 96 | payload effective length is provided in the BHS field - 97 | DataSegmentLength. The Data Segment is also padded to an integer 98 | number of 4 byte words. p118 99 | */ 100 | REVERSE_3BYTES ( &DataSegmentLength , &Pdu->Bhs->GENERICBHS.DataSegmentLength); 101 | Pdu->BufferSize += DataSegmentLength.AsULong; 102 | 103 | if ( Pdu->BufferSize &0x3 ) 104 | { 105 | Pdu->BufferSize &= ~0x3; 106 | Pdu->BufferSize += 0x4; 107 | } 108 | 109 | if ( Pdu->BufferSize ) 110 | { 111 | Pdu->Buffer = ExAllocatePoolWithTag ( NonPagedPool, Pdu->BufferSize, USCSI_TAG ); 112 | Ctx->PendingPDU = Pdu; //Pending for AHS and DATA 113 | } 114 | else 115 | // PDU Completed 116 | goto QueuePdu; 117 | } 118 | } 119 | //AHS and Data 120 | else 121 | { 122 | Copied = min ( (Pdu->BufferSize + BHS_SIZE - Pdu->Bytes) , Remaining ); 123 | RtlCopyMemory ( Pdu->Buffer + ( Pdu->Bytes - BHS_SIZE ) , PTsdu , Copied); 124 | 125 | PTsdu += Copied; 126 | Remaining -= Copied; 127 | Pdu->Bytes += Copied; 128 | *BytesTaken += Copied; 129 | 130 | if (Pdu->Bytes < Pdu->BufferSize + BHS_SIZE ) 131 | { 132 | Ctx->PendingPDU = Pdu; //Pending for AHS and DATA 133 | continue; 134 | } 135 | // 136 | // Complete a PDU , queue it 137 | // 138 | QueuePdu: 139 | 140 | if ( Pdu->Bhs->GENERICBHS.TotalAHSLength ) 141 | Pdu->Ahs = Pdu->Buffer; 142 | 143 | REVERSE_3BYTES ( &DataSegmentLength , &Pdu->Bhs->GENERICBHS.DataSegmentLength); 144 | if ( DataSegmentLength.AsULong ) 145 | Pdu->Data = Pdu->Buffer + Pdu->Bhs->GENERICBHS.TotalAHSLength; 146 | 147 | Ctx->PendingPDU = NULL; 148 | // 149 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT, 150 | "%s Pdu 0x%X Opcode 0x%X BufferSize 0x%X\n" , 151 | __FUNCTION__, 152 | Pdu, 153 | Pdu->Bhs->GENERICBHS.Opcode, 154 | Pdu->BufferSize)); 155 | 156 | KeQuerySystemTime ( &Pdu->ReceivingTime ); 157 | 158 | if ( TiVerifyDataDigest ( Ctx , Pdu) ) 159 | TpQueueInPDU ( Ctx , Pdu ); 160 | } 161 | } 162 | } 163 | 164 | // 165 | // 166 | // 167 | 168 | UCHAR TiDataPadding[4]; 169 | ULONG TiAllocateMdl (IN PPDU Pdu , IN PIRP Irp) 170 | { 171 | PMDL Mdl; 172 | ULONG SendLen = 0 , DataSize = 0; 173 | UCHAR PaddingLen; 174 | THREE_BYTE DataSegmentLength; 175 | 176 | Mdl = IoAllocateMdl ( Pdu->Bhs, BHS_SIZE, FALSE , FALSE , Irp ); 177 | __try 178 | { 179 | MmProbeAndLockPages ( Mdl , KernelMode , IoReadAccess ); 180 | } 181 | __except (EXCEPTION_EXECUTE_HANDLER) 182 | { 183 | DbgPrint("%s Exception 0x%X" , __FUNCTION__ , GetExceptionCode() ); 184 | } 185 | 186 | SendLen += BHS_SIZE; 187 | 188 | if ( Pdu->Ahs ) 189 | { 190 | Mdl = IoAllocateMdl ( Pdu->Ahs , 191 | Pdu->Bhs->GENERICBHS.TotalAHSLength << 2, 192 | TRUE , 193 | FALSE , 194 | Irp ); 195 | __try 196 | { 197 | MmProbeAndLockPages ( Mdl , KernelMode , IoReadAccess ); 198 | } 199 | __except (EXCEPTION_EXECUTE_HANDLER) 200 | { 201 | DbgPrint("%s Exception 0x%X" , __FUNCTION__ , GetExceptionCode()); 202 | } 203 | 204 | SendLen += Pdu->Bhs->GENERICBHS.TotalAHSLength << 2; 205 | } 206 | 207 | if ( Pdu->Data ) 208 | { 209 | REVERSE_3BYTES ( &DataSegmentLength , &Pdu->Bhs->GENERICBHS.DataSegmentLength ); 210 | 211 | Mdl = IoAllocateMdl ( Pdu->Data , 212 | DataSegmentLength.AsULong , 213 | TRUE , 214 | FALSE , 215 | Irp ); 216 | __try 217 | { 218 | MmProbeAndLockPages ( Mdl , KernelMode , IoReadAccess ); 219 | } 220 | __except (EXCEPTION_EXECUTE_HANDLER) 221 | { 222 | DbgPrint("%s Exception 0x%X" , __FUNCTION__ , GetExceptionCode()); 223 | } 224 | // 225 | // Padding if needed 226 | // 227 | if ( PaddingLen = DataSegmentLength.AsULong & 0x3 ) 228 | { 229 | DataSegmentLength.AsULong &= ~0x3; 230 | DataSegmentLength.AsULong += 0x4; 231 | 232 | Mdl = IoAllocateMdl ( TiDataPadding , 233 | 4 - PaddingLen , 234 | TRUE , 235 | FALSE , 236 | Irp ); 237 | __try 238 | { 239 | MmProbeAndLockPages ( Mdl , KernelMode , IoReadAccess ); 240 | } 241 | __except (EXCEPTION_EXECUTE_HANDLER) 242 | { 243 | DbgPrint("%s Exception 0x%X" , __FUNCTION__ , GetExceptionCode() ); 244 | } 245 | } 246 | 247 | SendLen += DataSegmentLength.AsULong; 248 | } 249 | 250 | return SendLen; 251 | } 252 | 253 | // 254 | // 255 | // 256 | 257 | VOID TiCheckCmdWindow (IN PSESSION Session) 258 | { 259 | KIRQL OldIrql; 260 | 261 | KeAcquireSpinLock ( &Session->WindowLock , &OldIrql ); 262 | 263 | if ( Session->CmdSN > Session->MaxCmdSN) 264 | { 265 | KeReleaseSpinLock ( &Session->WindowLock , OldIrql ); 266 | KeWaitForSingleObject ( &Session->WindowEvent , Executive , KernelMode , FALSE , NULL ); 267 | KeClearEvent ( &Session->WindowEvent ); 268 | } 269 | 270 | KeReleaseSpinLock ( &Session->WindowLock , OldIrql ); 271 | } 272 | 273 | // 274 | // Sender for outgoing PDUs 275 | // 276 | 277 | VOID TiSender(IN PVOID Parameter) 278 | { 279 | NTSTATUS Status; 280 | PWORKER_THREAD_CTX Tctx; 281 | PuCONINFO ConInfo; 282 | PuCON_CTX Ctx; 283 | PPDU Pdu; 284 | PIRP Irp; 285 | PDEVICE_OBJECT DevObj; 286 | PLIST_ENTRY Entry; 287 | LONG DataSize = 0 , SendLen = 0; 288 | ULONG CmdSN , ExpStatSN; 289 | FOUR_BYTE ITT; 290 | KEVENT Event; 291 | IO_STATUS_BLOCK IoStatus; 292 | 293 | Status = STATUS_SUCCESS; 294 | Tctx = (PWORKER_THREAD_CTX)Parameter; 295 | Ctx = Tctx->Ctx; 296 | ConInfo = Ctx->ConInfo; 297 | 298 | DevObj = IoGetRelatedDeviceObject (ConInfo->ConEpObj); 299 | 300 | while ( TRUE ) 301 | { 302 | Status = KeWaitForSingleObject ( &Ctx->OutEvent , 303 | Executive , 304 | KernelMode , 305 | FALSE , 306 | NULL ); 307 | 308 | KeClearEvent ( &Ctx->OutEvent ); 309 | 310 | while ( Entry = ExInterlockedRemoveHeadList ( &Ctx->OutPDU , &Ctx->OutLock ) ) 311 | { 312 | Pdu = CONTAINING_RECORD ( Entry , PDU , PDUList ); 313 | InitializeListHead ( &Pdu->PDUList ); 314 | 315 | KeInitializeEvent ( &Event , NotificationEvent , FALSE); 316 | 317 | Irp = TdiBuildInternalDeviceControlIrp( TDI_SEND, DevObj, NULL, &Event, &IoStatus ); 318 | if (!Irp ) 319 | { 320 | DbgPrint("%s Fail to allocate Irp\n" ,__FUNCTION__ ); 321 | Ctx->FailedIrpAllocation++; 322 | //Retry later 323 | TpQueueOutPDU( Ctx , Pdu ); 324 | continue; 325 | } 326 | // CmdSN 327 | if (Pdu->Bhs->GENERICBHS.Opcode != OP_DATA_OUT && 328 | Pdu->Bhs->GENERICBHS.Opcode != OP_SNACK_REQ ) 329 | { 330 | TiCheckCmdWindow (ConInfo->Session); 331 | 332 | if (Pdu->Bhs->GENERICBHS.Immediate) 333 | CmdSN = ConInfo->Session->CmdSN; 334 | else 335 | CmdSN = InterlockedIncrement ( (PLONG)&ConInfo->Session->CmdSN ); 336 | 337 | REVERSE_BYTES ( &Pdu->Bhs->STAT_FIELDS.CmdSN , &CmdSN); 338 | } 339 | // State Sync 340 | ExpStatSN = ConInfo->ExpStatSN; 341 | REVERSE_BYTES ( &Pdu->Bhs->STAT_FIELDS.ExpStatSN ,&ExpStatSN); 342 | 343 | SendLen = TiAllocateMdl ( Pdu , Irp ); 344 | 345 | REVERSE_BYTES ( &ITT , &Pdu->Bhs->STAT_FIELDS.InitiatorTaskTag ); 346 | /* 347 | DbgPrint( 348 | "%s Pdu=0x%X ITT=0x%X Opcode=0x%X ScsiOpcode=0x%X Irp=0x%p SendLen=0x%X Bhs=0x%p Ahs=0x%p Data=0x%p CmdSN=0x%X\n" , 349 | __FUNCTION__, 350 | Pdu, 351 | ITT.AsULong, 352 | Pdu->Bhs->GENERICBHS.Opcode, 353 | Pdu->Bhs->GENERICBHS.Opcode == OP_SCSI_CMD?Pdu->Bhs->SCSI_CMD.Cdb[0]:0xFF, 354 | Irp , 355 | SendLen , 356 | Pdu->Bhs, 357 | Pdu->Ahs, 358 | Pdu->Data, 359 | CmdSN );*/ 360 | 361 | TdiBuildSend( 362 | Irp, DevObj, ConInfo->ConEpObj, PiCompletePDU, Pdu, Irp->MdlAddress, 0, SendLen); 363 | 364 | Status = IoCallDriver ( DevObj , Irp ); 365 | if ( STATUS_PENDING == Status ) 366 | KeWaitForSingleObject ( &Event , Executive , KernelMode , FALSE , NULL ); 367 | } 368 | } 369 | 370 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT,"Sender(0x%x) Exited\n" , Tctx->No )); 371 | 372 | ExFreePoolWithTag ( Tctx , USCSI_TAG ); 373 | 374 | } 375 | 376 | PSN TiAllocateSN ( IN PuCONINFO ConInfo ); 377 | 378 | VOID TiFreeSN ( IN PuCONINFO ConInfo , IN PSN StatSN); 379 | 380 | 381 | __inline BOOLEAN TiUpdateStatSN(IN PuCONINFO ConInfo, IN ULONG Num ) 382 | { 383 | PSESSION Session; 384 | PSINGLE_LIST_ENTRY StatSNEnt , StatSNEnt2 = NULL; 385 | PSN StatSN; 386 | BOOLEAN ResetTimer = FALSE; 387 | 388 | if ( !ConInfo->ExpStatSNInited ) 389 | { 390 | ConInfo->ExpStatSN = Num + 1; 391 | ConInfo->ExpStatSNInited = TRUE; 392 | ResetTimer = TRUE; 393 | } 394 | // Exactly what we want 395 | else if ( ConInfo->ExpStatSN == Num ) 396 | { 397 | // Expect next one 398 | ConInfo->ExpStatSN++; 399 | // Maybe the others can be acked 400 | while ( StatSNEnt = ConInfo->StatSN.Next ) 401 | { 402 | StatSN = CONTAINING_RECORD ( StatSNEnt , SN , Next ); 403 | if ( StatSN->SN == ConInfo->ExpStatSN ) 404 | { 405 | ConInfo->ExpStatSN++; 406 | PopEntryList( &ConInfo->StatSN ); 407 | TiFreeSN ( ConInfo , StatSN); 408 | ResetTimer = TRUE; 409 | continue; 410 | } 411 | break; 412 | } 413 | } 414 | // Out of order 415 | else if ( ConInfo->ExpStatSN < Num ) 416 | { 417 | // 418 | // Insert , sort by StatSN 419 | // 420 | StatSNEnt = &ConInfo->StatSN; 421 | 422 | while ( StatSNEnt = StatSNEnt->Next ) 423 | { 424 | StatSN = CONTAINING_RECORD ( StatSNEnt , SN , Next ); 425 | 426 | StatSNEnt2 = StatSNEnt; 427 | 428 | if ( StatSN->SN < Num ) 429 | break; 430 | else if ( StatSN->SN == Num ) 431 | { 432 | // Duplicated Stat 433 | // Maybe Target timeouted and auto retransmit it 434 | // rarely happen 435 | ConInfo->DupStatSN++; 436 | return FALSE; 437 | } 438 | } 439 | 440 | if (!StatSNEnt2) 441 | StatSNEnt2 = &ConInfo->StatSN; 442 | 443 | StatSN = TiAllocateSN ( ConInfo ); 444 | StatSN->SN = Num; 445 | 446 | PushEntryList ( StatSNEnt2 , &StatSN->Next); 447 | } 448 | 449 | else if ( ConInfo->ExpStatSN > Num ) 450 | { 451 | // This is a retransmit of R2T 452 | } 453 | 454 | if (ResetTimer) 455 | { 456 | //TODO 457 | } 458 | 459 | return TRUE; 460 | } 461 | 462 | 463 | __inline VOID TiUpdateCmdWindow ( PuCONINFO ConInfo , ULONG ExpCmdSN , ULONG MaxCmdSN) 464 | { 465 | KIRQL OldIrql; 466 | PSESSION Session; 467 | 468 | Session = ConInfo->Session; 469 | 470 | KeAcquireSpinLock (&Session->WindowLock , &OldIrql ); 471 | 472 | if ( ExpCmdSN > Session->ExpCmdSN ) 473 | Session->ExpCmdSN = ExpCmdSN; 474 | 475 | if ( MaxCmdSN > Session->MaxCmdSN) 476 | Session->MaxCmdSN = MaxCmdSN ; 477 | 478 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT, 479 | "%s ExpCmdSN 0x%X MaxCmdSN 0x%X Window %d\n" , 480 | __FUNCTION__ , 481 | Session->ExpCmdSN, 482 | Session->MaxCmdSN, 483 | Session->MaxCmdSN - Session->ExpCmdSN )); 484 | 485 | KeReleaseSpinLock (&Session->WindowLock , OldIrql ); 486 | // 487 | // TiCheckCmdWindow wait on WindowEvent 488 | // 489 | if ( Session->MaxCmdSN > Session->CmdSN) 490 | KeSetEvent ( &Session->WindowEvent , IO_NO_INCREMENT , FALSE); 491 | } 492 | 493 | // 494 | // Dispatcher for incoming PDUs 495 | // 496 | 497 | VOID TiDispatcher( IN PVOID Parameter ) 498 | { 499 | NTSTATUS Status; 500 | PSESSION Session; 501 | PWORKER_THREAD_CTX Tctx; 502 | PuCONINFO ConInfo; 503 | PuCON_CTX Ctx; 504 | PLIST_ENTRY PduEnt; 505 | PPDU Pdu; 506 | FOUR_BYTE StatSN , ExpCmdSN , MaxCmdSN; 507 | 508 | Tctx = (PWORKER_THREAD_CTX)Parameter; 509 | Ctx = Tctx->Ctx; 510 | ConInfo = Ctx->ConInfo; 511 | Session = ConInfo->Session; 512 | 513 | while ( TRUE ) 514 | { 515 | Status = KeWaitForSingleObject ( &Ctx->DispatchEvent , 516 | Executive , 517 | KernelMode , 518 | FALSE , 519 | NULL ); 520 | 521 | KeClearEvent( &Ctx->DispatchEvent ); 522 | 523 | while ( PduEnt = ExInterlockedRemoveHeadList ( &Ctx->InPDU , &Ctx->InLock ) ) 524 | { 525 | Pdu = (PPDU)CONTAINING_RECORD ( PduEnt , PDU , PDUList ); 526 | InitializeListHead ( &Pdu->PDUList ); 527 | // Command Window 528 | REVERSE_BYTES ( &ExpCmdSN , &Pdu->Bhs->STAT_FIELDS.ExpCmdSN); 529 | REVERSE_BYTES ( &MaxCmdSN , &Pdu->Bhs->STAT_FIELDS.MaxCmdSN); 530 | TiUpdateCmdWindow (ConInfo , ExpCmdSN.AsULong , MaxCmdSN.AsULong); 531 | /* 532 | The presence of status 533 | (and of a residual count) is signaled though the S flag bit. 534 | p137 535 | */ 536 | if ( Pdu->Bhs->GENERICBHS.Opcode == OP_DATA_IN && Pdu->Bhs->SCSI_DATA_IN.S || 537 | Pdu->Bhs->GENERICBHS.Opcode != OP_DATA_IN) 538 | { 539 | REVERSE_BYTES ( &StatSN , &Pdu->Bhs->STAT_FIELDS.StatSN); 540 | TiUpdateStatSN ( ConInfo , StatSN.AsULong ); 541 | } 542 | 543 | //DbgPrint("%s Opcode=0x%X\n" , __FUNCTION__ , Pdu->Bhs->GENERICBHS.Opcode ); 544 | switch ( Pdu->Bhs->GENERICBHS.Opcode ) 545 | { 546 | case OP_SCSI_REP: 547 | PtProcessResponse ( Pdu , ConInfo ); 548 | break; 549 | 550 | case OP_DATA_IN: 551 | PtProcessDataIn ( Pdu , ConInfo ); 552 | break; 553 | 554 | case OP_R2T: 555 | PtProcessR2T ( Pdu , ConInfo ); 556 | break; 557 | 558 | case OP_SCSI_TASK_REP: 559 | PtProcessTask ( Pdu , ConInfo ); 560 | break; 561 | 562 | case OP_TXT_REP: 563 | PtProcessText ( Pdu , ConInfo); 564 | break; 565 | 566 | case OP_ASYNC_MSG: 567 | PtProcessAsyncMsg (Pdu , ConInfo); 568 | break; 569 | 570 | case OP_LOGIN_REP: 571 | PtProcessLogin( Pdu , ConInfo); 572 | break; 573 | 574 | case OP_LOGOUT_REP: 575 | PtProcessLogout ( Pdu , ConInfo ); 576 | break; 577 | 578 | case OP_REJECT: 579 | PtProcessReject ( Pdu , ConInfo ); 580 | break; 581 | 582 | case OP_NOP_IN: 583 | PtProcessNopIn ( Pdu , ConInfo ); 584 | break; 585 | 586 | default: 587 | //PtProcessError ( Pdu , ConInfo ); 588 | break; 589 | } 590 | } 591 | } 592 | 593 | KdPrintEx((DPFLTR_USCSI,DBG_TRANSPORT,"Dispatcher(0x%x) Exited\n" , Tctx->No)); 594 | ExFreePoolWithTag( Tctx , USCSI_TAG); 595 | //Ctx->WorkItemFlags |= CONCTX_INWORKER_SAFE; 596 | } -------------------------------------------------------------------------------- /uSCSIPort/Transport.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRANSPORT_H 2 | #define _TRANSPORT_H 3 | 4 | // 5 | // An iSCSI Session 6 | // 7 | 8 | typedef struct _SESSION 9 | { 10 | LIST_ENTRY List; 11 | // 12 | // iSCSI Command Sequence 13 | // 14 | ULONG CmdSN; 15 | // 16 | // Outstanding R2T Counter 17 | // 18 | ULONG R2TCount; 19 | // 20 | // Command Window 21 | // 22 | ULONG ExpCmdSN; 23 | ULONG MaxCmdSN; 24 | KSPIN_LOCK WindowLock; 25 | KEVENT WindowEvent; 26 | 27 | ULONG TaskTag; 28 | 29 | UCHAR ISID[6]; 30 | UCHAR TSIH[2]; 31 | // 32 | BOOLEAN NormalSession; 33 | // 34 | // CID Counter 35 | // 36 | USHORT CID; 37 | // 38 | // Session State 39 | // 40 | UCHAR State; 41 | // 42 | // Target 43 | // 44 | PITGT Tgt; 45 | // 46 | // Session Connections 47 | // 48 | KSPIN_LOCK ConLock; 49 | LIST_ENTRY Connections; 50 | PuCONINFO LeadingCon; 51 | PuCONINFO BestCon; 52 | // 53 | // Pdu Release Thread 54 | // 55 | LIST_ENTRY PduRelease; 56 | HANDLE PduReleaser; 57 | KEVENT PduReleaseEvent; 58 | KSPIN_LOCK PduReleaseLock; 59 | // 60 | // 61 | // 62 | HANDLE TdiPnP; 63 | // 64 | // 65 | // 66 | USCSI_CALL_BACK CallBack; 67 | // 68 | // For traffic stats 69 | // 70 | ULONG Ns100Unit; 71 | // 72 | // Session Wide Parameters 73 | // 74 | PKEY_VALUE Parameter[KEY_MaxSessionKey]; 75 | }SESSION , *PSESSION; 76 | 77 | // Session States 78 | 79 | #define SESSION_STAT_FREE 0x01 80 | #define SESSION_STAT_LOGGED_IN 0x03 81 | #define SESSION_STAT_FAILED 0x04 82 | 83 | // Connection 84 | #define CTX_MAX_DISPATCH_WORKERS 1 85 | #define CTX_MAX_SEND_WORKERS 1 86 | #define CTX_MAX_R2T_WORKERS 1 87 | #define CTX_MAX_DATAIN_WORKERS 1 88 | #define CTX_MAX_PENDING_CMD_WORKERS 1 89 | 90 | typedef struct _uCON_CTX 91 | { 92 | // 93 | // in PDU queue 94 | // 95 | LIST_ENTRY InPDU; 96 | KSPIN_LOCK InLock; 97 | KEVENT DispatchEvent; 98 | HANDLE Dispatchers[CTX_MAX_DISPATCH_WORKERS]; 99 | ULONG DispatcherCount; 100 | // 101 | // out PDU queue 102 | // 103 | LIST_ENTRY OutPDU; 104 | KSPIN_LOCK OutLock; 105 | KEVENT OutEvent; 106 | HANDLE Senders[CTX_MAX_SEND_WORKERS]; 107 | ULONG SenderCount; 108 | // 109 | // Outstanding R2Ts 110 | // 111 | LIST_ENTRY R2Ts; 112 | KSPIN_LOCK R2TLock; 113 | KEVENT R2TEvent; 114 | HANDLE R2TWorkers[CTX_MAX_R2T_WORKERS]; 115 | ULONG R2TWorkerCount; 116 | // 117 | // Incoming Data-In 118 | // 119 | LIST_ENTRY DataIns; 120 | KSPIN_LOCK DataInLock; 121 | KEVENT DataInEvent; 122 | HANDLE DataInWorkers[CTX_MAX_DATAIN_WORKERS]; 123 | ULONG DataInWorkerCount; 124 | // 125 | // Command that has been acked 126 | // 127 | LIST_ENTRY PendingCompleteCmds; 128 | KSPIN_LOCK PendingCompleteCmdLock; 129 | KEVENT PendingCompleteCmdEvent; 130 | HANDLE PendingCompleteCmdWorkers[CTX_MAX_PENDING_CMD_WORKERS]; 131 | ULONG PendingCompleteCmdCount; 132 | // 133 | // Pools 134 | // 135 | NPAGED_LOOKASIDE_LIST PDULookAside; 136 | NPAGED_LOOKASIDE_LIST BHSLookAside; 137 | // 138 | // For PDU assembling 139 | // 140 | PPDU PendingPDU; 141 | // 142 | // Outstanding Continue PDU 143 | // 144 | PPDU LinkPDU; 145 | // 146 | // Performance Counter 147 | // 148 | ULONG FailedIrpAllocation; 149 | // 150 | // back to 151 | // 152 | PuCONINFO ConInfo; 153 | }uCON_CTX , *PuCON_CTX; 154 | 155 | // 156 | // Worker context 157 | // 158 | 159 | typedef struct _WORKER_THREAD_CTX 160 | { 161 | PuCON_CTX Ctx; 162 | UCHAR No; 163 | }WORKER_THREAD_CTX , *PWORKER_THREAD_CTX; 164 | 165 | #define WI_PDU_IN 0x01 166 | #define WI_PDU_OUT 0x02 167 | #define WI_R2T 0x03 168 | #define WI_DATAIN 0x04 169 | #define WI_PENDING_COMPLETE_CMD 0x05 170 | #define WI_PDU_RELEASE 0x06 171 | 172 | // 173 | // Traffic stats 174 | // 175 | 176 | typedef enum _CON_STATS 177 | { 178 | CON_STATS_MIN = 0, 179 | CON_STATS_MAX, 180 | CON_STATS_AVG, 181 | CON_STATS_TIMEOUT, 182 | 183 | CON_STATS_LAST 184 | }CON_STATS; 185 | 186 | #define PENDING_TASK_HASH_SIZE 0x100 187 | 188 | // 189 | // iSCSI connection 190 | // 191 | 192 | typedef struct _uCONINFO 193 | { 194 | // 195 | // On Session list 196 | // 197 | LIST_ENTRY ConList; 198 | // 199 | // Status Numbering 200 | // 201 | ULONG ExpStatSN; 202 | // 203 | // TRUE if ExpStatSN has been initied with first StatSN 204 | // 205 | BOOLEAN ExpStatSNInited; 206 | // 207 | // StatSN Queue sorted by StatSN 208 | // 209 | SINGLE_LIST_ENTRY StatSN; 210 | // 211 | // 212 | // 213 | NPAGED_LOOKASIDE_LIST SNLookAside; 214 | // 215 | // Connection State 216 | // 217 | UCHAR State; 218 | // 219 | // ID 220 | // 221 | USHORT CID; 222 | 223 | ULONG DupResponseCount; 224 | ULONG DupStatSN; 225 | // 226 | // Transport address , endpoint , control channel 227 | // 228 | HANDLE Addr , Ep , Ctl; 229 | PFILE_OBJECT AddrObj , ConEpObj , CtlObj; 230 | // 231 | // Context 232 | // 233 | PuCON_CTX ConCtx; 234 | // 235 | // Session belonging 236 | // 237 | PSESSION Session; 238 | // 239 | // State of Text Conversation 240 | // 241 | UCHAR TextState; 242 | // 243 | // State of Login Conversation 244 | // 245 | UCHAR LoginState; 246 | UCHAR SkipOperationalNegotiation:1; 247 | UCHAR SSG:2; 248 | UCHAR CSG:2; 249 | UCHAR NSG:2; 250 | KEVENT LoginEvent; 251 | // 252 | // For Login and Text 253 | // 254 | BOOLEAN MoreWorks; 255 | PPDU Answer; 256 | // 257 | // Used by 258 | // 1. Login/Text to hold the head of a logical PDU 259 | // 2. and Data-In to hold the head of a data sequence 260 | // 261 | PPDU Logical; 262 | // 263 | // 264 | // 265 | UCHAR LogoutState; 266 | // 267 | // Hashed By InitiatorTaskTag 268 | // 269 | LIST_ENTRY PendingTasks[PENDING_TASK_HASH_SIZE]; 270 | KSPIN_LOCK PendingTasksLock; 271 | // 272 | // Connection Stats 273 | // 274 | ULONG Stats[CON_STATS_LAST]; 275 | // 276 | // Connection Wide Parameters 277 | // 278 | PKEY_VALUE Parameter[ KEY_LastKey - KEY_MaxSessionKey - 1]; 279 | // 280 | // Transport Parameters 281 | // 282 | TDI_CONNECTION_INFO ConnectionInfo; 283 | TDI_PROVIDER_INFO ProviderInfo; 284 | 285 | }uCONINFO , *PuCONINFO; 286 | 287 | #define IsLeadingCon(c) ( (c)->Session->LeadingCon == (c) ) 288 | 289 | #define KEY_CON_IDX(k) ( k - KEY_MaxSessionKey - 1 ) 290 | 291 | // Text Exchange State 292 | #define TXT_STAT_IDLE 0x00 293 | #define TXT_STAT_STARTED 0x01 294 | #define TXT_STAT_PENDING_COMPLETE 0x02 295 | 296 | // Login Stages 297 | #define LOGIN_STAGE_SecurityNegotiation 0x00 298 | #define LOGIN_STAGE_LoginOperationalNegotiation 0x01 299 | #define LOGIN_STAGE_FullFeaturePhase 0x03 300 | 301 | // Login Stage State 302 | #define LOGIN_STAT_IDLE 0x00 303 | #define LOGIN_STAT_STARTED 0x01 // A new stage has started 304 | #define LOGIN_STAT_PENDING_TRANSIT 0x02 // A transit has been requested 305 | 306 | // 307 | #define CON_STAT_UNINITIALIZED 0x00 308 | #define CON_STAT_INITIALIZED 0x01 309 | #define CON_STAT_CONNECTED 0x02 310 | 311 | // RFC 3720 Connection States 312 | 313 | #define CON_STAT_FREE 0x11 314 | #define CON_STAT_XPT_WAIT 0x12 315 | #define CON_STAT_IN_LOGIN 0x14 //Login , Initialization 316 | #define CON_STAT_LOGGED_IN 0x15 //Full Feature 317 | #define CON_STAT_LOGOUT_REQED 0x16 318 | #define CON_STAT_CLEANUP_WAIT 0x17 319 | 320 | // 321 | // Protocol Data Unit 322 | // 323 | typedef struct _PDU 324 | { 325 | // 326 | // On In\Out\R2Ts\DataIns Queue or in Pending Cmd Hash 327 | // 328 | LIST_ENTRY PDUList; 329 | // 330 | // On Continue List or PendingCompleteCmds Queue 331 | // 332 | union 333 | { 334 | LIST_ENTRY Continue; 335 | LIST_ENTRY PendingCmd; 336 | }; 337 | // 338 | // Referenced SCSI Cmd PDU 339 | // 340 | struct _PDU* Cmd; 341 | // 342 | // 343 | // 344 | ULONG Flags; 345 | // 346 | // Response of this command 347 | // 348 | LIST_ENTRY Resps; 349 | // 350 | // Data ins or R2Ts 351 | // 352 | LIST_ENTRY DataInOrR2T; 353 | ULONG DupDataInCount; 354 | KSPIN_LOCK DataInOrR2TLock; 355 | // 356 | // Data outs of SCSI Command / R2T 357 | // 358 | LIST_ENTRY DataOut; 359 | // 360 | // For SCSI Command and R2T 361 | // Numbering Data-Out(s) within a SCSI Command (Unsolicited Do) 362 | // or within a R2T (Solicited Do) 363 | ULONG DataSN; 364 | // 365 | // Data Offset in Data-Out buffer 366 | // 367 | ULONG DoOffset; 368 | // 369 | // SCSI staff 370 | // 371 | PIRP OrigIrp; 372 | PSCSI_REQUEST_BLOCK Srb; 373 | PUCHAR DataBuffer; 374 | // 375 | // Sending/Receiving Time of PDU 376 | // 377 | union 378 | { 379 | LARGE_INTEGER SendingTime; 380 | LARGE_INTEGER ReceivingTime; 381 | }; 382 | // 383 | // SCSI_RESPONSE ExpDataSN 384 | // 385 | ULONG ExpDataSN; 386 | // 387 | // Back to 388 | // 389 | PuCON_CTX ConCtx; 390 | // 391 | PBHS Bhs; 392 | // 393 | // Buffer for both Ahs and Data 394 | // 395 | PUCHAR Buffer; 396 | PUCHAR Ahs; 397 | PUCHAR Data; 398 | // TiReceive stuffs 399 | ULONG BufferSize; 400 | ULONG Bytes; 401 | }PDU , *PPDU; 402 | 403 | #define PDU_F_DATA_IN_PROCESSED 0x00000001 404 | #define PDU_F_DATA_IN_RETRANSMIT 0x00000002 405 | #define PDU_F_CMD_CAN_COMPLETE 0x00000004 406 | // 407 | // 408 | // 409 | typedef struct _SN 410 | { 411 | SINGLE_LIST_ENTRY Next; 412 | ULONG SN; 413 | }SN , *PSN; 414 | 415 | // 416 | // Public Functions 417 | // 418 | 419 | // TransUtils.c 420 | 421 | VOID TpRegisterPnPHandlers( PSESSION Session); 422 | 423 | PPDU TpAllocatePDU(PuCON_CTX Ctx , UCHAR Opcode , UCHAR AHSLen , ULONG DataLen); 424 | 425 | VOID TpFreePDU(PuCON_CTX Ctx , PPDU Pdu ,ULONG Level); 426 | 427 | VOID TpQueuePDU(PuCON_CTX Ctx , PPDU Pdu , UCHAR Which ); 428 | 429 | PuCONINFO TpBestConnection(PSESSION Session); 430 | 431 | #define TpQueueInPDU(Ctx , Pdu ) TpQueuePDU( Ctx , Pdu , WI_PDU_IN ) 432 | #define TpQueueOutPDU(Ctx , Pdu ) TpQueuePDU( Ctx , Pdu , WI_PDU_OUT ) 433 | #define TpQueueR2TPDU(Ctx , Pdu ) TpQueuePDU( Ctx , Pdu , WI_R2T ) 434 | #define TpQueueDataInPDU(Ctx , Pdu ) TpQueuePDU( Ctx , Pdu , WI_DATAIN ) 435 | #define TpQueuePendingCompleteCmd(Ctx , Pdu ) TpQueuePDU( Ctx , Pdu , WI_PENDING_COMPLETE_CMD ) 436 | #define TpQueuePduRelease(Ctx , Pdu) TpQueuePDU( Ctx , Pdu , WI_PDU_RELEASE) 437 | 438 | PuCONINFO TpAddConInfo(PSESSION Session ); 439 | 440 | NTSTATUS TpConnectTo( PuCONINFO Coninfo , ULONG Address , USHORT Port); 441 | 442 | PSESSION TpAllocateSession (); 443 | 444 | VOID TpFreeSession (PSESSION Session); 445 | 446 | NTSTATUS TpTest(); 447 | 448 | 449 | 450 | 451 | #endif -------------------------------------------------------------------------------- /uSCSIPort/precomp.h: -------------------------------------------------------------------------------- 1 | #ifndef PRECOMP_H 2 | #define PRECOMP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "uSCSIPortPublic.h" 14 | #include "uSCSIPort.h" 15 | #include "uSCSI.h" 16 | #include "Protocol.h" 17 | #include "Transport.h" 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /uSCSIPort/uSCSI.c: -------------------------------------------------------------------------------- 1 | 2 | #include "precomp.h" 3 | 4 | NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) 5 | { 6 | return STATUS_SUCCESS; 7 | } 8 | 9 | // 10 | // Global 11 | // 12 | 13 | BOOLEAN uSCSIPortInitialized = FALSE; 14 | KSPIN_LOCK SessionListLock; 15 | LIST_ENTRY SessionList; 16 | 17 | KSPIN_LOCK TgtsLock; 18 | LIST_ENTRY Targets; 19 | ULONG TgtCount; 20 | 21 | // 22 | // Exported function 23 | // 24 | 25 | ULONG TiCountTargetSize() 26 | { 27 | PITGT Itgt; 28 | ULONG Size = 0; 29 | PLIST_ENTRY TgtEnt , TgtEnt2; 30 | 31 | TgtEnt = &Targets; 32 | TgtEnt2 = TgtEnt->Flink; 33 | 34 | while ( TgtEnt2 != TgtEnt) 35 | { 36 | Itgt = CONTAINING_RECORD ( TgtEnt2 , ITGT , List ); 37 | Size += strlen (Itgt->Tgt.TargetName) + 1; 38 | Size += sizeof ( TGT ); 39 | TgtEnt2 = TgtEnt2->Flink; 40 | } 41 | 42 | if (!Size) 43 | return Size; 44 | 45 | return Size + sizeof (TGTS); 46 | } 47 | 48 | // 49 | // 50 | // 51 | 52 | ULONG uSCSIGetTargetsSize() 53 | { 54 | KIRQL OldIrql; 55 | ULONG Size = 0; 56 | 57 | ExAcquireSpinLock ( &TgtsLock , &OldIrql ); 58 | Size = TiCountTargetSize(); 59 | ExReleaseSpinLock ( &TgtsLock , OldIrql ); 60 | 61 | return Size; 62 | } 63 | 64 | // 65 | // 66 | // 67 | 68 | NTSTATUS uSCSIPopTargets( PTGTS Tgts) 69 | { 70 | KIRQL OldIrql; 71 | PTGT Tgt; 72 | PITGT Itgt; 73 | PUCHAR NameBuf; 74 | ULONG Len , NameOffset; 75 | PLIST_ENTRY TgtEnt , TgtEnt2; 76 | 77 | ExAcquireSpinLock ( &TgtsLock , &OldIrql ); 78 | 79 | if ( Tgts->Size != TiCountTargetSize() ) 80 | return STATUS_INVALID_PARAMETER; 81 | 82 | Tgts->Count = TgtCount; 83 | Tgt = (PTGT)(Tgts + 1); 84 | NameBuf = (PUCHAR)(Tgt + Tgts->Count); 85 | NameOffset = NameBuf - (PUCHAR)Tgts; 86 | 87 | TgtEnt = &Targets; 88 | TgtEnt2 = TgtEnt->Flink; 89 | 90 | while ( TgtEnt2 != TgtEnt) 91 | { 92 | Itgt = CONTAINING_RECORD ( TgtEnt2 , ITGT , List ); 93 | 94 | Len = strlen ( Itgt->Tgt.TargetName ) + 1 ; 95 | 96 | RtlCopyMemory ( NameBuf , Itgt->Tgt.TargetName , Len); 97 | 98 | Tgt->NameOffset = NameOffset; 99 | Tgt->Addr = Itgt->Tgt.Addr; 100 | Tgt->Port = Itgt->Tgt.Port; 101 | 102 | Tgt++; 103 | NameBuf += Len; 104 | NameOffset += Len; 105 | 106 | TgtEnt2 = TgtEnt2->Flink; 107 | } 108 | 109 | ExReleaseSpinLock ( &TgtsLock , OldIrql ); 110 | 111 | return STATUS_SUCCESS; 112 | } 113 | 114 | // 115 | // 116 | // 117 | 118 | VOID uSCSIAddTarget(PUCHAR TgtName , ULONG Addr , USHORT Port) 119 | { 120 | ULONG Len; 121 | PITGT Itgt; 122 | KIRQL OldIrql; 123 | 124 | Len = strlen (TgtName); 125 | 126 | if ( Len ) 127 | { 128 | Itgt = ExAllocatePoolWithTag ( PagedPool , sizeof (ITGT) + Len + 1, USCSI_TAG); 129 | Itgt->Tgt.TargetName = (PUCHAR)( Itgt + 1); 130 | RtlCopyMemory ( Itgt->Tgt.TargetName , TgtName , Len + 1); 131 | Itgt->Tgt.Addr =Addr; 132 | Itgt->Tgt.Port = Port; 133 | 134 | ExAcquireSpinLock ( &TgtsLock , &OldIrql ); 135 | InsertTailList ( &Targets , &Itgt->List); 136 | TgtCount++; 137 | ExReleaseSpinLock ( &TgtsLock , OldIrql ); 138 | } 139 | } 140 | 141 | // 142 | // 143 | // 144 | 145 | VOID uSCSIInitialize() 146 | { 147 | if ( uSCSIPortInitialized ) 148 | return; 149 | 150 | PtInit(); 151 | InitializeListHead ( &SessionList ); 152 | InitializeListHead ( &Targets ); 153 | KeInitializeSpinLock ( &SessionListLock); 154 | KeInitializeSpinLock ( &TgtsLock); 155 | 156 | uSCSIPortInitialized = TRUE; 157 | TgtCount = 0; 158 | //uSCSIAddTarget ( "iqn.com.yushang:vdisk.0001" , INETADDR (192,168,1,100) , HTONS(3260) ); 159 | } 160 | 161 | // 162 | // 163 | // 164 | 165 | PVOID uSCSICreateSession( PUCHAR Target , PUSCSI_CALL_BACK CallBack ) 166 | { 167 | NTSTATUS Status; 168 | PITGT Itgt; 169 | PLIST_ENTRY TgtEnt , TgtEnt2; 170 | PSESSION Session; 171 | PuCONINFO ConInfo; 172 | ULONG Cons; 173 | 174 | if (!uSCSIPortInitialized) 175 | return NULL; 176 | 177 | TgtEnt = &Targets; 178 | TgtEnt2 = TgtEnt->Flink; 179 | 180 | while ( TgtEnt != TgtEnt2 ) 181 | { 182 | Itgt = CONTAINING_RECORD ( TgtEnt2 , ITGT , List); 183 | if ( !strcmp( Itgt->Tgt.TargetName , Target )) 184 | goto Found; 185 | TgtEnt2 = TgtEnt2->Flink; 186 | } 187 | 188 | return NULL; 189 | 190 | Found: 191 | 192 | if ( !CallBack->CompleteCmd ) 193 | return NULL; 194 | 195 | Session = TpAllocateSession( Itgt ); 196 | Session->CallBack = *CallBack; 197 | 198 | TpAddConInfo (Session); 199 | 200 | Status = PtLogin (Session->LeadingCon); 201 | if (NT_SUCCESS (Status) ) 202 | { 203 | ExInterlockedInsertTailList ( &SessionList , &Session->List , &SessionListLock); 204 | return Session; 205 | } 206 | else 207 | { 208 | TpFreeSession (Session); 209 | return NULL; 210 | } 211 | } 212 | 213 | VOID PiSendDataOut(PuCON_CTX Ctx , PPDU CmdOrR2T ); 214 | 215 | // 216 | // 217 | // 218 | 219 | VOID uSCSIProcessSCSICmd ( PVOID Session , 220 | PUCHAR Cdb , 221 | ULONG CdbLength , 222 | PUCHAR DataBuf , 223 | ULONG WriteSize, 224 | ULONG ReadSize , 225 | UCHAR TaskAttr, 226 | UCHAR Dir , 227 | PULONG Handle) 228 | { 229 | PuCONINFO ConInfo; 230 | PPDU Cmd; 231 | 232 | ConInfo = TpBestConnection (Session); 233 | 234 | Cmd = PtAssembleSCSICmd ( ConInfo , 235 | Cdb , 236 | CdbLength , 237 | DataBuf , 238 | WriteSize , 239 | ReadSize , 240 | TaskAttr , 241 | Dir ); 242 | if ( Handle ) 243 | REVERSE_BYTES ( Handle , &Cmd->Bhs->STAT_FIELDS.InitiatorTaskTag); 244 | 245 | TpQueueOutPDU ( ConInfo->ConCtx , Cmd ); 246 | PiSendDataOut ( ConInfo->ConCtx , Cmd ); 247 | 248 | } 249 | 250 | // 251 | // 252 | // 253 | 254 | VOID uSCSIDiscover( ULONG Portal , USHORT Port) 255 | { 256 | 257 | } 258 | 259 | // 260 | // 261 | // 262 | 263 | PLIST_ENTRY uSCSIGetTargets() 264 | { 265 | PLIST_ENTRY Tgts = NULL; 266 | 267 | return Tgts; 268 | } 269 | 270 | // 271 | // 272 | // 273 | 274 | PLIST_ENTRY uSCSIGetSessions() 275 | { 276 | PLIST_ENTRY Sessions = NULL; 277 | 278 | return Sessions; 279 | } 280 | 281 | // 282 | // 283 | // 284 | 285 | PUSCSI_SESSION_INFO uSCSIGetSessionInfo ( PVOID Session ) 286 | { 287 | PUSCSI_SESSION_INFO Info = NULL; 288 | 289 | return Info; 290 | } 291 | // 292 | // For Extension Module 293 | // 294 | 295 | -------------------------------------------------------------------------------- /uSCSIPort/uSCSI.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef USCSI_H 3 | #define USCSI_H 4 | 5 | #define USCSI_TAG (ULONG)'uSCI' 6 | 7 | #define INETADDR(a, b, c, d) (a | (b<<8) | (c<<16) | (d<<24)) 8 | #define HTONL(a) (((a&0xFF)<<24) | ((a&0xFF00)<<8) | ((a&0xFF0000)>>8) | ((a&0xFF000000)>>24)) 9 | #define HTONS(a) (((0xFF&a)<<8) | ((0xFF00&a)>>8)) 10 | 11 | // 12 | // debug module 13 | // 14 | //#define DBG_USCSI 15 | //#define DBG_XPT 16 | //#define DBG_PROT 17 | 18 | ULONG DbgSeq; 19 | 20 | #define TMP_Debug( c ) 21 | /* 22 | #define TMP_Debug( c ) \ 23 | { \ 24 | DbgPrint("%08X ",DbgSeq++); \ 25 | DbgPrint c; \ 26 | }*/ 27 | 28 | #ifdef DBG_USCSI 29 | #define USCSI_Debug( c ) \ 30 | DbgPrint c 31 | #else 32 | #define USCSI_Debug( c ) 33 | #endif 34 | 35 | #ifdef DBG_XPT 36 | #define XPT_Debug( c ) \ 37 | DbgPrint c 38 | #else 39 | #define XPT_Debug( c ) 40 | #endif 41 | 42 | #ifdef DBG_PROT 43 | #define PROT_Debug( c ) \ 44 | DbgPrint c 45 | #else 46 | #define PROT_Debug( c ) 47 | #endif 48 | 49 | #define DPFLTR_USCSI DPFLTR_IHVDRIVER_ID 50 | 51 | #define DBG_TRANSPORT 0x4 52 | #define DBG_TDI 0x5 53 | #define DBG_PROTOCOL 0x6 54 | 55 | typedef struct _ITGT 56 | { 57 | LIST_ENTRY List; 58 | TGT Tgt; 59 | }ITGT , *PITGT; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /uSCSIPort/uSCSIPort.def: -------------------------------------------------------------------------------- 1 | LIBRARY uSCSIPort.sys 2 | EXPORTS 3 | uSCSIInitialize @1 4 | uSCSICreateSession @2 5 | uSCSIProcessSCSICmd @3 6 | uSCSIAddTarget @4 7 | uSCSIGetTargetsSize @5 8 | uSCSIPopTargets @6 -------------------------------------------------------------------------------- /uSCSIPort/uSCSIPort.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _USCSI_PORT_H 3 | #define _USCSI_PORT_H 4 | 5 | #define SCSI_TASK_UNTAGGED 0x00 6 | #define SCSI_TASK_SIMPLE 0x01 7 | #define SCSI_TASK_ORDERED 0x02 8 | #define SCSI_TASK_HOQ 0x03 9 | #define SCSI_TASK_ACA 0x04 10 | 11 | // OP_SCSI_CMD direction 12 | #define SCSI_CMD_DIR_READ 0x01 13 | #define SCSI_CMD_DIR_WRITE 0x02 14 | #define SCSI_CMD_DIR_BOTH 0x03 15 | #define SCSI_CMD_DIR_NONE 0x04 16 | 17 | typedef VOID (*uSCSIComplete)( ULONG Handle , 18 | PVOID Context , 19 | UCHAR ScsiStatus , 20 | PUCHAR SenseData, 21 | ULONG SenseLength ); 22 | 23 | typedef struct _USCSI_CALL_BACK 24 | { 25 | uSCSIComplete CompleteCmd; 26 | PVOID CompleteCtx; 27 | 28 | }USCSI_CALL_BACK , *PUSCSI_CALL_BACK; 29 | 30 | typedef struct _USCSI_SESSION_INFO 31 | { 32 | PVOID PlaceHolder; 33 | }USCSI_SESSION_INFO , *PUSCSI_SESSION_INFO; 34 | 35 | VOID uSCSIInitialize(); 36 | 37 | VOID uSCSIAddTarget(PUCHAR Tgt , ULONG Addr , USHORT Port); 38 | 39 | ULONG uSCSIGetTargetsSize(); 40 | 41 | NTSTATUS uSCSIPopTargets( PTGTS Tgts); 42 | 43 | PVOID uSCSICreateSession( PUCHAR Target , PUSCSI_CALL_BACK CallBack ); 44 | 45 | VOID uSCSIProcessSCSICmd ( PVOID Session , 46 | PUCHAR Cdb , 47 | ULONG CdbLength , 48 | PUCHAR DataBuf , 49 | ULONG WriteSize, 50 | ULONG ReadSize , 51 | UCHAR TaskAttr, 52 | UCHAR Dir , 53 | PULONG Handle); 54 | 55 | #endif -------------------------------------------------------------------------------- /uSCSIPort/uSCSIPortPublic.h: -------------------------------------------------------------------------------- 1 | #ifndef _USCSI_PORT_PUBLIC_H 2 | #define _USCSI_PORT_PUBLIC_H 3 | 4 | typedef struct _TGT 5 | { 6 | union 7 | { 8 | PUCHAR TargetName; 9 | ULONG NameOffset; 10 | }; 11 | ULONG Addr; 12 | USHORT Port; 13 | }TGT , *PTGT; 14 | 15 | typedef struct _TGTS 16 | { 17 | ULONG Count; 18 | ULONG Size; 19 | }TGTS , *PTGTS; 20 | 21 | 22 | //STATUS CODE 23 | #define STATUS_USCSI_SESSION_FAILED 0xE 24 | #endif -------------------------------------------------------------------------------- /uSCSI设计文档.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldoldman/iSCSI_drv/e118622fa43c4756038bc72a2ef4492f807a10f8/uSCSI设计文档.pdf --------------------------------------------------------------------------------