*********************** NOTES FOR PEOPLE PORTING LOW LEVEL DRIVERS ******* This file explains some of the internals of the new scsi drivers, and explains some of the things that need to be done to add new features to the low-level code. I will start with a general wish list that explains what it is that would need to be done, and then I will a general comment. There are several things I would like you to do. 1) Test the drivers as is, and make sure that they still work. 2) Use the request_irq and request_dma (if appropriate) to register the irq number and dma channel with the rest of linux (I think that only the 7000fasst driver does not register the irq with the rest of the kernel). The Ultrastor and 7000fasst drivers should register their dma channels. See the Adaptec code for an example of how this is done. 3) Add scatter-gather. The flag SCpnt->use_sg is an integer which indicates whether or not scatter-gather is in use. If this is zero, then no scatter-gather is being used, and the request should be treated as before. If this is non-zero, then SCpnt->buffer points to an array of structures. The structure definition is in scsi.h, and looks like: struct scatterlist { char * address; /* Location data is to be transferred to */ char * alt_address; /* Location of real data if address is a dma indirect buffer */ unsigned short length; }; The low level driver need only be concerned with the "address" and "length" field. The upper level driver uses the other field. SCpnt->use_sg is a count of how many of these structures there are. All you have to do is walk the list, and handle "length" bytes. If your host adapter uses unchecked DMA, then the "address" fields are guaranteed to be < 16Mb. The header file for the device needs to be told the maximum number of chains that can be used for a given device. Host adapters that do it in software will not have any limit, the Adaptec has a limit of 16. There is a field in the various .h files which indicates exactly how many chains can be used. I filled in SG_NONE in this field for all of the drivers. There is a field in SCpnt, SCpnt->host_scribble which is a char*. If you need to allocate memory, you can do so, and store the pointer here. The interrupt routine for your host adapter *must* also free the memory. It is generally wise to avoid the use of malloc/free in disk drivers, because this can lead to race conditions where the malloc would need to allocate a new page, which could lead to an attempt to write out a dirty page to disk, which would lead to another disk write request.... There are a pair of functions, scsi_malloc and scsi_free, which can be used for memory allocation. They are simpleminded, in that they can only accept requests for 512, 1024, 2048, or 4096 bytes, but they are guaranteed to be at addresses < 16Mb (suitable for DMA). The function scsi_free needs to know the size of the block you are freeing. There is only a limited pool of memory that scsi_malloc can call upon, so if you forget to scsi_free, you will soon get a panic when the kernel runs out. The scsi_malloc pool size is determined at boot time from the number of actual devices located while scanning all of the devices. Since these functions do not try and expand the pool size, they are safe to use within a disk driver for general purposes. You just have to allocate in multiples of 512 bytes. The pool is not allocated until after the various detect routines have been executed for the various host adapters, and after we have done an INQUIRY to look for devices on each host adapter. 4) Add multiple outstanding requests. All of the old host adapters have variables SCint, and do_done, which are used to store the parameters for the completion function once an interrupt is received. These need to be turned into arrays. The interrupt routine must know that it should be handling either just one request, or however many requests are done, and you must have a clear sense of what the actual card is going to do if a second request finishes while a first one is still being serviced. It is easy to lose interrupts here, so you need to make sure that the first thing that you do once you find out which request you are servicing is to set/clear something so that a subsequent interrupt does not try and handle the completion for the same request. You should use cli()/sti() pairs when you are searching for a request that has been finished to make sure that nothing changes while you are working on it. You should try and have the interrupts turned off for the absolute minimum amount of time. You need to modify the field in the relevant header file, which fits into the can_queue field of scsi_hosts. This should be set to the maximum number of requests that a given host adapter can handle simultaneously. 5) Decide if your host adapter can make good use of having more than one command for a given lun. With the seagate, you can take the second one, and "link" it to the current one, and improve performance (or so Drew tells me). With the Adaptec, you can not link commands once the first one is started, so it is hard to make use of this there. If you can make use of command linking (or having more than one command per lun outstanding), you can change the constant in your relevant .h file. A value of 1 allows one command per lun, a value of 2 allows 2, etc. 6) Add a biosparam function to your host adapter. This is a fairly simple function which returns the number of heads, sectors and cylinders for a hard disk that will be connected to your controller. For many controllers these are fixed numbers, so you just return constants. Other controllers can be strapped for several possibilities. In these cases it would be best if you could read it off of the board somehow. This function is needed so that LILO can boot from the disk without the user having to add the SCSI disk to the disktab. ************************************************************************ GENERAL COMMENTS In general you should not change any of the fields that are pointed to by SCpnt, but there are a few exceptions. SCpnt->result is where you should store the completion code before calling the done function. SCpnt->host_scribble is a char* that is designed to be used by host interfaces that might need to use scsi_malloc to allocate memory to process the command (remember to use scsi_free to deallocate it once you are done). The field SCpnt->SCp points to a structure which can be used by any host interface for any purpose whatsoever - I added it for Drew, because he wanted a place to store various pointers and counters for the seagate, and it will probably come in handy with the Future Domain. If you are porting a different host interface, and one of these does not fit the bill for you, let me know what you need, and I can add it to the Scsi_Cmnd structure. As I understand it, the extended filesystem can generate allocation patterns where files end up being stored backwards on the disk (i.e. the next block of a file will be at the previous block on the disk). This will be a real disaster as far as performance is concerned, because the linux kernel combines I/O requests into scatter-gather blocks if subsequent requests are for blocks that come just *after* the blocks being requested for the current request. We should be able to modify the higher level code to handle this situation properly, but you should be aware that this may be throttling your disk access times.