summaryrefslogtreecommitdiffstats
path: root/uClinux-2.4.20-uc1/drivers/scsi/hosts.c
blob: 72e87c62fa9061f3cacbd86fe90a1cb2a7c435de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*
 *  hosts.c Copyright (C) 1992 Drew Eckhardt
 *          Copyright (C) 1993, 1994, 1995 Eric Youngdale
 *
 *  mid to lowlevel SCSI driver interface
 *      Initial versions: Drew Eckhardt
 *      Subsequent revisions: Eric Youngdale
 *
 *  <drew@colorado.edu>
 *
 *  Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
 *  Added QLOGIC QLA1280 SCSI controller kernel host support. 
 *     August 4, 1999 Fred Lewis, Intel DuPont
 *
 *  Updated to reflect the new initialization scheme for the higher 
 *  level of scsi drivers (sd/sr/st)
 *  September 17, 2000 Torben Mathiasen <tmm@image.dk>
 */


/*
 *  This file contains the medium level SCSI
 *  host interface initialization, as well as the scsi_hosts array of SCSI
 *  hosts currently present in the system.
 */

#define __NO_VERSION__
#include <linux/module.h>
#include <linux/blk.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>

#define __KERNEL_SYSCALLS__

#include <linux/unistd.h>

#include "scsi.h"
#include "hosts.h"

/*
static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
*/

/*
 *  The scsi host entries should be in the order you wish the
 *  cards to be detected.  A driver may appear more than once IFF
 *  it can deal with being detected (and therefore initialized)
 *  with more than one simultaneous host number, can handle being
 *  reentrant, etc.
 *
 *  They may appear in any order, as each SCSI host is told which host 
 *  number it is during detection.
 */

/* This is a placeholder for controllers that are not configured into
 * the system - we do this to ensure that the controller numbering is
 * always consistent, no matter how the kernel is configured. */

#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
			   NULL, NULL, 0, 0, 0, 0, 0, 0}

/*
 *  When figure is run, we don't want to link to any object code.  Since
 *  the macro for each host will contain function pointers, we cannot
 *  use it and instead must use a "blank" that does no such
 *  idiocy.
 */

Scsi_Host_Template * scsi_hosts;


/*
 *  Our semaphores and timeout counters, where size depends on 
 *      MAX_SCSI_HOSTS here.
 */

Scsi_Host_Name * scsi_host_no_list;
struct Scsi_Host * scsi_hostlist;
struct Scsi_Device_Template * scsi_devicelist;

int max_scsi_hosts;
int next_scsi_host;

void
scsi_unregister(struct Scsi_Host * sh){
    struct Scsi_Host * shpnt;
    Scsi_Host_Name *shn;
        
    if(scsi_hostlist == sh)
	scsi_hostlist = sh->next;
    else {
	shpnt = scsi_hostlist;
	while(shpnt->next != sh) shpnt = shpnt->next;
	shpnt->next = shpnt->next->next;
    }

    /*
     * We have to unregister the host from the scsi_host_no_list as well.
     * Decide by the host_no not by the name because most host drivers are
     * able to handle more than one adapters from the same kind (or family).
     */
    for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no);
	  shn=shn->next);
    if (shn) shn->host_registered = 0;
    /* else {} : This should not happen, we should panic here... */
    
    /* If we are removing the last host registered, it is safe to reuse
     * its host number (this avoids "holes" at boot time) (DB) 
     * It is also safe to reuse those of numbers directly below which have
     * been released earlier (to avoid some holes in numbering).
     */
    if(sh->host_no == max_scsi_hosts - 1) {
	while(--max_scsi_hosts >= next_scsi_host) {
	    shpnt = scsi_hostlist;
	    while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
		shpnt = shpnt->next;
	    if(shpnt)
		break;
	}
    }
    next_scsi_host--;
    kfree((char *) sh);
}

/* We call this when we come across a new host adapter. We only do this
 * once we are 100% sure that we want to use this host adapter -  it is a
 * pain to reverse this, so we try to avoid it 
 */
extern int blk_nohighio;
struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
    struct Scsi_Host * retval, *shpnt, *o_shp;
    Scsi_Host_Name *shn, *shn2;
    int flag_new = 1;
    const char * hname;
    size_t hname_len;
    retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
					 (tpnt->unchecked_isa_dma && j ? 
					  GFP_DMA : 0) | GFP_ATOMIC);
    if(retval == NULL)
    {
        printk("scsi: out of memory in scsi_register.\n");
    	return NULL;
    }
    	
    memset(retval, 0, sizeof(struct Scsi_Host) + j);

    /* trying to find a reserved entry (host_no) */
    hname = (tpnt->proc_name) ?  tpnt->proc_name : "";
    hname_len = strlen(hname);
    for (shn = scsi_host_no_list;shn;shn = shn->next) {
	if (!(shn->host_registered) && 
	    (hname_len > 0) && (0 == strncmp(hname, shn->name, hname_len))) {
	    flag_new = 0;
	    retval->host_no = shn->host_no;
	    shn->host_registered = 1;
	    shn->loaded_as_module = 1;
	    break;
	}
    }
    atomic_set(&retval->host_active,0);
    retval->host_busy = 0;
    retval->host_failed = 0;
    if(j > 0xffff) panic("Too many extra bytes requested\n");
    retval->extra_bytes = j;
    retval->loaded_as_module = 1;
    if (flag_new) {
	shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC);
        if (!shn) {
                kfree(retval);
                printk(KERN_ERR "scsi: out of memory(2) in scsi_register.\n");
                return NULL;
        }
	shn->name = kmalloc(hname_len + 1, GFP_ATOMIC);
	if (hname_len > 0)
	    strncpy(shn->name, hname, hname_len);
	shn->name[hname_len] = 0;
	shn->host_no = max_scsi_hosts++;
	shn->host_registered = 1;
	shn->loaded_as_module = 1;
	shn->next = NULL;
	if (scsi_host_no_list) {
	    for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
		;
	    shn2->next = shn;
	}
	else
	    scsi_host_no_list = shn;
	retval->host_no = shn->host_no;
    }
    next_scsi_host++;
    retval->host_queue = NULL;
    init_waitqueue_head(&retval->host_wait);
    retval->resetting = 0;
    retval->last_reset = 0;
    retval->irq = 0;
    retval->dma_channel = 0xff;

    /* These three are default values which can be overridden */
    retval->max_channel = 0; 
    retval->max_id = 8;      
    retval->max_lun = 8;

    /*
     * All drivers right now should be able to handle 12 byte commands.
     * Every so often there are requests for 16 byte commands, but individual
     * low-level drivers need to certify that they actually do something
     * sensible with such commands.
     */
    retval->max_cmd_len = 12;

    retval->unique_id = 0;
    retval->io_port = 0;
    retval->hostt = tpnt;
    retval->next = NULL;
    retval->in_recovery = 0;
    retval->ehandler = NULL;    /* Initial value until the thing starts up. */
    retval->eh_notify   = NULL;    /* Who we notify when we exit. */


    retval->host_blocked = FALSE;
    retval->host_self_blocked = FALSE;

#ifdef DEBUG
    printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
#endif

    /* The next six are the default values which can be overridden
     * if need be */
    retval->this_id = tpnt->this_id;
    retval->can_queue = tpnt->can_queue;
    retval->sg_tablesize = tpnt->sg_tablesize;
    retval->cmd_per_lun = tpnt->cmd_per_lun;
    retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
    retval->use_clustering = tpnt->use_clustering;   
    if (!blk_nohighio)
	retval->highmem_io = tpnt->highmem_io;

    retval->select_queue_depths = tpnt->select_queue_depths;
    retval->max_sectors = tpnt->max_sectors;

    if(!scsi_hostlist)
	scsi_hostlist = retval;
    else {
	shpnt = scsi_hostlist;
	if (retval->host_no < shpnt->host_no) {
	    retval->next = shpnt;
	    wmb(); /* want all to see these writes in this order */
	    scsi_hostlist = retval;
	}
	else {
	    for (o_shp = shpnt, shpnt = shpnt->next; shpnt; 
		 o_shp = shpnt, shpnt = shpnt->next) {
		if (retval->host_no < shpnt->host_no) {
		    retval->next = shpnt;
		    wmb();
		    o_shp->next = retval;
		    break;
		}
	    }
	    if (! shpnt)
		o_shp->next = retval;
        }
    }
    
    return retval;
}

int
scsi_register_device(struct Scsi_Device_Template * sdpnt)
{
    if(sdpnt->next) panic("Device already registered");
    sdpnt->next = scsi_devicelist;
    scsi_devicelist = sdpnt;
    return 0;
}

void
scsi_deregister_device(struct Scsi_Device_Template * tpnt)
{
    struct Scsi_Device_Template *spnt;
    struct Scsi_Device_Template *prev_spnt;

    spnt = scsi_devicelist;
    prev_spnt = NULL;
    while (spnt != tpnt) {
	prev_spnt = spnt;
	spnt = spnt->next;
    }
    if (prev_spnt == NULL)
        scsi_devicelist = tpnt->next;
    else
        prev_spnt->next = spnt->next;
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-indent-level: 4
 * c-brace-imaginary-offset: 0
 * c-brace-offset: -4
 * c-argdecl-indent: 4
 * c-label-offset: -4
 * c-continued-statement-offset: 4
 * c-continued-brace-offset: 0
 * indent-tabs-mode: nil
 * tab-width: 8
 * End:
 */