diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ia64/hp/sim/simscsi.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ia64/hp/sim/simscsi.c')
-rw-r--r-- | arch/ia64/hp/sim/simscsi.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c new file mode 100644 index 000000000000..56405dbfd739 --- /dev/null +++ b/arch/ia64/hp/sim/simscsi.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * Simulated SCSI driver. | ||
3 | * | ||
4 | * Copyright (C) 1999, 2001-2003 Hewlett-Packard Co | ||
5 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
6 | * Stephane Eranian <eranian@hpl.hp.com> | ||
7 | * | ||
8 | * 02/01/15 David Mosberger Updated for v2.5.1 | ||
9 | * 99/12/18 David Mosberger Added support for READ10/WRITE10 needed by linux v2.3.33 | ||
10 | */ | ||
11 | #include <linux/blkdev.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/timer.h> | ||
16 | #include <asm/irq.h> | ||
17 | |||
18 | #include <scsi/scsi.h> | ||
19 | #include <scsi/scsi_cmnd.h> | ||
20 | #include <scsi/scsi_device.h> | ||
21 | #include <scsi/scsi_host.h> | ||
22 | |||
23 | #define DEBUG_SIMSCSI 0 | ||
24 | |||
25 | #define SIMSCSI_REQ_QUEUE_LEN 64 | ||
26 | #define DEFAULT_SIMSCSI_ROOT "/var/ski-disks/sd" | ||
27 | |||
28 | /* Simulator system calls: */ | ||
29 | |||
30 | #define SSC_OPEN 50 | ||
31 | #define SSC_CLOSE 51 | ||
32 | #define SSC_READ 52 | ||
33 | #define SSC_WRITE 53 | ||
34 | #define SSC_GET_COMPLETION 54 | ||
35 | #define SSC_WAIT_COMPLETION 55 | ||
36 | |||
37 | #define SSC_WRITE_ACCESS 2 | ||
38 | #define SSC_READ_ACCESS 1 | ||
39 | |||
40 | #if DEBUG_SIMSCSI | ||
41 | int simscsi_debug; | ||
42 | # define DBG simscsi_debug | ||
43 | #else | ||
44 | # define DBG 0 | ||
45 | #endif | ||
46 | |||
47 | static struct Scsi_Host *host; | ||
48 | |||
49 | static void simscsi_interrupt (unsigned long val); | ||
50 | static DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0); | ||
51 | |||
52 | struct disk_req { | ||
53 | unsigned long addr; | ||
54 | unsigned len; | ||
55 | }; | ||
56 | |||
57 | struct disk_stat { | ||
58 | int fd; | ||
59 | unsigned count; | ||
60 | }; | ||
61 | |||
62 | extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr); | ||
63 | |||
64 | static int desc[16] = { | ||
65 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 | ||
66 | }; | ||
67 | |||
68 | static struct queue_entry { | ||
69 | struct scsi_cmnd *sc; | ||
70 | } queue[SIMSCSI_REQ_QUEUE_LEN]; | ||
71 | |||
72 | static int rd, wr; | ||
73 | static atomic_t num_reqs = ATOMIC_INIT(0); | ||
74 | |||
75 | /* base name for default disks */ | ||
76 | static char *simscsi_root = DEFAULT_SIMSCSI_ROOT; | ||
77 | |||
78 | #define MAX_ROOT_LEN 128 | ||
79 | |||
80 | /* | ||
81 | * used to setup a new base for disk images | ||
82 | * to use /foo/bar/disk[a-z] as disk images | ||
83 | * you have to specify simscsi=/foo/bar/disk on the command line | ||
84 | */ | ||
85 | static int __init | ||
86 | simscsi_setup (char *s) | ||
87 | { | ||
88 | /* XXX Fix me we may need to strcpy() ? */ | ||
89 | if (strlen(s) > MAX_ROOT_LEN) { | ||
90 | printk(KERN_ERR "simscsi_setup: prefix too long---using default %s\n", | ||
91 | simscsi_root); | ||
92 | } | ||
93 | simscsi_root = s; | ||
94 | return 1; | ||
95 | } | ||
96 | |||
97 | __setup("simscsi=", simscsi_setup); | ||
98 | |||
99 | static void | ||
100 | simscsi_interrupt (unsigned long val) | ||
101 | { | ||
102 | struct scsi_cmnd *sc; | ||
103 | |||
104 | while ((sc = queue[rd].sc) != 0) { | ||
105 | atomic_dec(&num_reqs); | ||
106 | queue[rd].sc = 0; | ||
107 | if (DBG) | ||
108 | printk("simscsi_interrupt: done with %ld\n", sc->serial_number); | ||
109 | (*sc->scsi_done)(sc); | ||
110 | rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static int | ||
115 | simscsi_biosparam (struct scsi_device *sdev, struct block_device *n, | ||
116 | sector_t capacity, int ip[]) | ||
117 | { | ||
118 | ip[0] = 64; /* heads */ | ||
119 | ip[1] = 32; /* sectors */ | ||
120 | ip[2] = capacity >> 11; /* cylinders */ | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | simscsi_readwrite (struct scsi_cmnd *sc, int mode, unsigned long offset, unsigned long len) | ||
126 | { | ||
127 | struct disk_stat stat; | ||
128 | struct disk_req req; | ||
129 | |||
130 | req.addr = __pa(sc->request_buffer); | ||
131 | req.len = len; /* # of bytes to transfer */ | ||
132 | |||
133 | if (sc->request_bufflen < req.len) | ||
134 | return; | ||
135 | |||
136 | stat.fd = desc[sc->device->id]; | ||
137 | if (DBG) | ||
138 | printk("simscsi_%s @ %lx (off %lx)\n", | ||
139 | mode == SSC_READ ? "read":"write", req.addr, offset); | ||
140 | ia64_ssc(stat.fd, 1, __pa(&req), offset, mode); | ||
141 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
142 | |||
143 | if (stat.count == req.len) { | ||
144 | sc->result = GOOD; | ||
145 | } else { | ||
146 | sc->result = DID_ERROR << 16; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static void | ||
151 | simscsi_sg_readwrite (struct scsi_cmnd *sc, int mode, unsigned long offset) | ||
152 | { | ||
153 | int list_len = sc->use_sg; | ||
154 | struct scatterlist *sl = (struct scatterlist *)sc->buffer; | ||
155 | struct disk_stat stat; | ||
156 | struct disk_req req; | ||
157 | |||
158 | stat.fd = desc[sc->device->id]; | ||
159 | |||
160 | while (list_len) { | ||
161 | req.addr = __pa(page_address(sl->page) + sl->offset); | ||
162 | req.len = sl->length; | ||
163 | if (DBG) | ||
164 | printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n", | ||
165 | mode == SSC_READ ? "read":"write", req.addr, offset, | ||
166 | list_len, sl->length); | ||
167 | ia64_ssc(stat.fd, 1, __pa(&req), offset, mode); | ||
168 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
169 | |||
170 | /* should not happen in our case */ | ||
171 | if (stat.count != req.len) { | ||
172 | sc->result = DID_ERROR << 16; | ||
173 | return; | ||
174 | } | ||
175 | offset += sl->length; | ||
176 | sl++; | ||
177 | list_len--; | ||
178 | } | ||
179 | sc->result = GOOD; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * function handling both READ_6/WRITE_6 (non-scatter/gather mode) | ||
184 | * commands. | ||
185 | * Added 02/26/99 S.Eranian | ||
186 | */ | ||
187 | static void | ||
188 | simscsi_readwrite6 (struct scsi_cmnd *sc, int mode) | ||
189 | { | ||
190 | unsigned long offset; | ||
191 | |||
192 | offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512; | ||
193 | if (sc->use_sg > 0) | ||
194 | simscsi_sg_readwrite(sc, mode, offset); | ||
195 | else | ||
196 | simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512); | ||
197 | } | ||
198 | |||
199 | static size_t | ||
200 | simscsi_get_disk_size (int fd) | ||
201 | { | ||
202 | struct disk_stat stat; | ||
203 | size_t bit, sectors = 0; | ||
204 | struct disk_req req; | ||
205 | char buf[512]; | ||
206 | |||
207 | /* | ||
208 | * This is a bit kludgey: the simulator doesn't provide a direct way of determining | ||
209 | * the disk size, so we do a binary search, assuming a maximum disk size of 4GB. | ||
210 | */ | ||
211 | for (bit = (4UL << 30)/512; bit != 0; bit >>= 1) { | ||
212 | req.addr = __pa(&buf); | ||
213 | req.len = sizeof(buf); | ||
214 | ia64_ssc(fd, 1, __pa(&req), ((sectors | bit) - 1)*512, SSC_READ); | ||
215 | stat.fd = fd; | ||
216 | ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); | ||
217 | if (stat.count == sizeof(buf)) | ||
218 | sectors |= bit; | ||
219 | } | ||
220 | return sectors - 1; /* return last valid sector number */ | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | simscsi_readwrite10 (struct scsi_cmnd *sc, int mode) | ||
225 | { | ||
226 | unsigned long offset; | ||
227 | |||
228 | offset = ( (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16) | ||
229 | | (sc->cmnd[4] << 8) | (sc->cmnd[5] << 0))*512; | ||
230 | if (sc->use_sg > 0) | ||
231 | simscsi_sg_readwrite(sc, mode, offset); | ||
232 | else | ||
233 | simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512); | ||
234 | } | ||
235 | |||
236 | static int | ||
237 | simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) | ||
238 | { | ||
239 | unsigned int target_id = sc->device->id; | ||
240 | char fname[MAX_ROOT_LEN+16]; | ||
241 | size_t disk_size; | ||
242 | char *buf; | ||
243 | #if DEBUG_SIMSCSI | ||
244 | register long sp asm ("sp"); | ||
245 | |||
246 | if (DBG) | ||
247 | printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n", | ||
248 | target_id, sc->cmnd[0], sc->serial_number, sp, done); | ||
249 | #endif | ||
250 | |||
251 | sc->result = DID_BAD_TARGET << 16; | ||
252 | sc->scsi_done = done; | ||
253 | if (target_id <= 15 && sc->device->lun == 0) { | ||
254 | switch (sc->cmnd[0]) { | ||
255 | case INQUIRY: | ||
256 | if (sc->request_bufflen < 35) { | ||
257 | break; | ||
258 | } | ||
259 | sprintf (fname, "%s%c", simscsi_root, 'a' + target_id); | ||
260 | desc[target_id] = ia64_ssc(__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS, | ||
261 | 0, 0, SSC_OPEN); | ||
262 | if (desc[target_id] < 0) { | ||
263 | /* disk doesn't exist... */ | ||
264 | break; | ||
265 | } | ||
266 | buf = sc->request_buffer; | ||
267 | buf[0] = 0; /* magnetic disk */ | ||
268 | buf[1] = 0; /* not a removable medium */ | ||
269 | buf[2] = 2; /* SCSI-2 compliant device */ | ||
270 | buf[3] = 2; /* SCSI-2 response data format */ | ||
271 | buf[4] = 31; /* additional length (bytes) */ | ||
272 | buf[5] = 0; /* reserved */ | ||
273 | buf[6] = 0; /* reserved */ | ||
274 | buf[7] = 0; /* various flags */ | ||
275 | memcpy(buf + 8, "HP SIMULATED DISK 0.00", 28); | ||
276 | sc->result = GOOD; | ||
277 | break; | ||
278 | |||
279 | case TEST_UNIT_READY: | ||
280 | sc->result = GOOD; | ||
281 | break; | ||
282 | |||
283 | case READ_6: | ||
284 | if (desc[target_id] < 0 ) | ||
285 | break; | ||
286 | simscsi_readwrite6(sc, SSC_READ); | ||
287 | break; | ||
288 | |||
289 | case READ_10: | ||
290 | if (desc[target_id] < 0 ) | ||
291 | break; | ||
292 | simscsi_readwrite10(sc, SSC_READ); | ||
293 | break; | ||
294 | |||
295 | case WRITE_6: | ||
296 | if (desc[target_id] < 0) | ||
297 | break; | ||
298 | simscsi_readwrite6(sc, SSC_WRITE); | ||
299 | break; | ||
300 | |||
301 | case WRITE_10: | ||
302 | if (desc[target_id] < 0) | ||
303 | break; | ||
304 | simscsi_readwrite10(sc, SSC_WRITE); | ||
305 | break; | ||
306 | |||
307 | |||
308 | case READ_CAPACITY: | ||
309 | if (desc[target_id] < 0 || sc->request_bufflen < 8) { | ||
310 | break; | ||
311 | } | ||
312 | buf = sc->request_buffer; | ||
313 | |||
314 | disk_size = simscsi_get_disk_size(desc[target_id]); | ||
315 | |||
316 | /* pretend to be a 1GB disk (partition table contains real stuff): */ | ||
317 | buf[0] = (disk_size >> 24) & 0xff; | ||
318 | buf[1] = (disk_size >> 16) & 0xff; | ||
319 | buf[2] = (disk_size >> 8) & 0xff; | ||
320 | buf[3] = (disk_size >> 0) & 0xff; | ||
321 | /* set block size of 512 bytes: */ | ||
322 | buf[4] = 0; | ||
323 | buf[5] = 0; | ||
324 | buf[6] = 2; | ||
325 | buf[7] = 0; | ||
326 | sc->result = GOOD; | ||
327 | break; | ||
328 | |||
329 | case MODE_SENSE: | ||
330 | case MODE_SENSE_10: | ||
331 | /* sd.c uses this to determine whether disk does write-caching. */ | ||
332 | memset(sc->request_buffer, 0, 128); | ||
333 | sc->result = GOOD; | ||
334 | break; | ||
335 | |||
336 | case START_STOP: | ||
337 | printk(KERN_ERR "START_STOP\n"); | ||
338 | break; | ||
339 | |||
340 | default: | ||
341 | panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]); | ||
342 | } | ||
343 | } | ||
344 | if (sc->result == DID_BAD_TARGET) { | ||
345 | sc->result |= DRIVER_SENSE << 24; | ||
346 | sc->sense_buffer[0] = 0x70; | ||
347 | sc->sense_buffer[2] = 0x00; | ||
348 | } | ||
349 | if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) { | ||
350 | panic("Attempt to queue command while command is pending!!"); | ||
351 | } | ||
352 | atomic_inc(&num_reqs); | ||
353 | queue[wr].sc = sc; | ||
354 | wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN; | ||
355 | |||
356 | tasklet_schedule(&simscsi_tasklet); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | simscsi_host_reset (struct scsi_cmnd *sc) | ||
362 | { | ||
363 | printk(KERN_ERR "simscsi_host_reset: not implemented\n"); | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static struct scsi_host_template driver_template = { | ||
368 | .name = "simulated SCSI host adapter", | ||
369 | .proc_name = "simscsi", | ||
370 | .queuecommand = simscsi_queuecommand, | ||
371 | .eh_host_reset_handler = simscsi_host_reset, | ||
372 | .bios_param = simscsi_biosparam, | ||
373 | .can_queue = SIMSCSI_REQ_QUEUE_LEN, | ||
374 | .this_id = -1, | ||
375 | .sg_tablesize = SG_ALL, | ||
376 | .max_sectors = 1024, | ||
377 | .cmd_per_lun = SIMSCSI_REQ_QUEUE_LEN, | ||
378 | .use_clustering = DISABLE_CLUSTERING, | ||
379 | }; | ||
380 | |||
381 | static int __init | ||
382 | simscsi_init(void) | ||
383 | { | ||
384 | int error; | ||
385 | |||
386 | host = scsi_host_alloc(&driver_template, 0); | ||
387 | if (!host) | ||
388 | return -ENOMEM; | ||
389 | |||
390 | error = scsi_add_host(host, NULL); | ||
391 | if (!error) | ||
392 | scsi_scan_host(host); | ||
393 | return error; | ||
394 | } | ||
395 | |||
396 | static void __exit | ||
397 | simscsi_exit(void) | ||
398 | { | ||
399 | scsi_remove_host(host); | ||
400 | scsi_host_put(host); | ||
401 | } | ||
402 | |||
403 | module_init(simscsi_init); | ||
404 | module_exit(simscsi_exit); | ||