Import 2.3.18pre1
[davej-history.git] / drivers / scsi / scsi_debug.c
blob310b289ec524e85ad1b5ee5d89205098dae1a2fa
1 /* $Id: scsi_debug.c,v 1.1 1992/07/24 06:27:38 root Exp root $
2 * linux/kernel/scsi_debug.c
4 * Copyright (C) 1992 Eric Youngdale
5 * Simulate a host adapter with 2 disks attached. Do a lot of checking
6 * to make sure that we are not getting blocks mixed up, and panic if
7 * anything out of the ordinary is seen.
8 */
10 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/errno.h>
15 #include <linux/timer.h>
16 #include <linux/types.h>
17 #include <linux/string.h>
18 #include <linux/genhd.h>
19 #include <linux/fs.h>
20 #include <linux/proc_fs.h>
22 #include <asm/system.h>
23 #include <asm/io.h>
25 #include <linux/blk.h>
26 #include "scsi.h"
27 #include "hosts.h"
29 #include "sd.h"
31 #include<linux/stat.h>
33 struct proc_dir_entry proc_scsi_scsi_debug =
35 PROC_SCSI_SCSI_DEBUG, 10, "scsi_debug",
36 S_IFDIR | S_IRUGO | S_IXUGO, 2
40 /* A few options that we want selected */
42 #define NR_HOSTS_PRESENT 20
43 #define NR_FAKE_DISKS 6
44 #define N_HEAD 32
45 #define N_SECTOR 64
46 #define DISK_READONLY(TGT) (1)
47 #define DISK_REMOVEABLE(TGT) (1)
49 /* Do not attempt to use a timer to simulate a real disk with latency */
50 /* Only use this in the actual kernel, not in the simulator. */
51 /* #define IMMEDIATE */
53 /* Skip some consistency checking. Good for benchmarking */
54 #define SPEEDY
55 /* Read return zeros. Undefine for benchmarking */
56 #define CLEAR
58 /* Number of real scsi disks that will be detected ahead of time */
59 static int NR_REAL = -1;
61 #define NR_BLK_DEV 12
62 #ifndef MAJOR_NR
63 #define MAJOR_NR 8
64 #endif
65 #define START_PARTITION 4
67 /* Number of jiffies to wait before completing a command */
68 #define DISK_SPEED 10
69 #define CAPACITY (0x80000)
71 static int starts[] =
72 {N_HEAD, N_HEAD * N_SECTOR, 50000, CAPACITY, 0};
73 static int npart = 0;
75 #include "scsi_debug.h"
76 #ifdef DEBUG
77 #define DEB(x) x
78 #else
79 #define DEB(x)
80 #endif
82 #ifdef SPEEDY
83 #define VERIFY1_DEBUG(RW)
84 #define VERIFY_DEBUG(RW)
85 #else
87 #define VERIFY1_DEBUG(RW) \
88 if (bufflen != 1024) {printk("%d", bufflen); panic("(1)Bad bufflen");}; \
89 start = 0; \
90 if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1]; \
91 if (bh){ \
92 if (bh->b_size != 1024) panic ("Wrong bh size"); \
93 if ((bh->b_blocknr << 1) + start != block) \
94 { printk("Wrong bh block# %d %d ",bh->b_blocknr, block); \
95 panic ("Wrong bh block#"); \
96 }; \
97 if (bh->b_dev != SCpnt->request.rq_dev) \
98 panic ("Bad bh target"); \
101 #define VERIFY_DEBUG(RW) \
102 if (bufflen != 1024 && (!SCpnt->use_sg)) {printk("%x %d\n ",bufflen, SCpnt->use_sg); panic("Bad bufflen");}; \
103 start = 0; \
104 if ((MINOR(SCpnt->request.rq_dev) & 0xf) > npart) panic ("Bad partition"); \
105 if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1]; \
106 if (SCpnt->request.cmd != RW) panic ("Wrong operation"); \
107 if (SCpnt->request.sector + start != block) panic("Wrong block."); \
108 if (SCpnt->request.current_nr_sectors != 2 && (!SCpnt->use_sg)) panic ("Wrong # blocks"); \
109 if (SCpnt->request.bh){ \
110 if (SCpnt->request.bh->b_size != 1024) panic ("Wrong bh size"); \
111 if ((SCpnt->request.bh->b_blocknr << 1) + start != block) \
112 { printk("Wrong bh block# %d %d ",SCpnt->request.bh->b_blocknr, block); \
113 panic ("Wrong bh block#"); \
114 }; \
115 if (SCpnt->request.bh->b_dev != SCpnt->request.rq_dev) \
116 panic ("Bad bh target");\
118 #endif
120 typedef void (*done_fct_t) (Scsi_Cmnd *);
122 static volatile done_fct_t do_done[SCSI_DEBUG_MAILBOXES] = {NULL,};
124 static void scsi_debug_intr_handle(unsigned long);
126 static struct timer_list timeout[SCSI_DEBUG_MAILBOXES];
128 Scsi_Cmnd *SCint[SCSI_DEBUG_MAILBOXES] = {NULL,};
129 static char SCrst[SCSI_DEBUG_MAILBOXES] = {0,};
132 * Semaphore used to simulate bus lockups.
134 static int scsi_debug_lockup = 0;
136 static char sense_buffer[128] = {0,};
138 static void scsi_dump(Scsi_Cmnd * SCpnt, int flag)
140 int i;
141 #if 0
142 unsigned char *pnt;
143 #endif
144 unsigned int *lpnt;
145 struct scatterlist *sgpnt = NULL;
146 printk("use_sg: %d", SCpnt->use_sg);
147 if (SCpnt->use_sg) {
148 sgpnt = (struct scatterlist *) SCpnt->buffer;
149 for (i = 0; i < SCpnt->use_sg; i++) {
150 lpnt = (int *) sgpnt[i].alt_address;
151 printk(":%p %p %d\n", sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
152 if (lpnt)
153 printk(" (Alt %x) ", lpnt[15]);
155 } else {
156 printk("nosg: %p %p %d\n", SCpnt->request.buffer, SCpnt->buffer,
157 SCpnt->bufflen);
158 lpnt = (int *) SCpnt->request.buffer;
159 if (lpnt)
160 printk(" (Alt %x) ", lpnt[15]);
162 lpnt = (unsigned int *) SCpnt;
163 for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
164 if ((i & 7) == 0)
165 printk("\n");
166 printk("%x ", *lpnt++);
168 printk("\n");
169 if (flag == 0)
170 return;
171 lpnt = (unsigned int *) sgpnt[0].alt_address;
172 for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
173 if ((i & 7) == 0)
174 printk("\n");
175 printk("%x ", *lpnt++);
177 #if 0
178 printk("\n");
179 lpnt = (unsigned int *) sgpnt[0].address;
180 for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
181 if ((i & 7) == 0)
182 printk("\n");
183 printk("%x ", *lpnt++);
185 printk("\n");
186 #endif
187 printk("DMA free %d sectors.\n", scsi_dma_free_sectors);
190 int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
192 unchar *cmd = (unchar *) SCpnt->cmnd;
193 struct partition *p;
194 int block;
195 struct buffer_head *bh = NULL;
196 unsigned char *buff;
197 int nbytes, sgcount;
198 int scsi_debug_errsts;
199 struct scatterlist *sgpnt;
200 int target = SCpnt->target;
201 int bufflen = SCpnt->request_bufflen;
202 unsigned long flags;
203 int i;
204 sgcount = 0;
205 sgpnt = NULL;
208 * If we are being notified of the mid-level reposessing a command due to timeout,
209 * just return.
211 if (done == NULL) {
212 return 0;
214 DEB(if (target >= NR_FAKE_DISKS) {
215 SCpnt->result = DID_TIME_OUT << 16; done(SCpnt); return 0;
219 buff = (unsigned char *) SCpnt->request_buffer;
221 if (target >= NR_FAKE_DISKS || SCpnt->lun != 0) {
222 SCpnt->result = DID_NO_CONNECT << 16;
223 done(SCpnt);
224 return 0;
226 if (SCrst[target] != 0 && !scsi_debug_lockup) {
227 SCrst[target] = 0;
228 memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
229 SCpnt->sense_buffer[0] = 0x70;
230 SCpnt->sense_buffer[2] = UNIT_ATTENTION;
231 SCpnt->result = (CHECK_CONDITION << 1);
232 done(SCpnt);
234 switch (*cmd) {
235 case REQUEST_SENSE:
236 SCSI_LOG_LLQUEUE(3, printk("Request sense...\n"));
237 #ifndef DEBUG
239 int i;
240 printk("scsi_debug: Requesting sense buffer (%p %p %p %d):", SCpnt, buff, done, bufflen);
241 for (i = 0; i < 12; i++)
242 printk("%d ", sense_buffer[i]);
243 printk("\n");
245 #endif
246 memset(buff, 0, bufflen);
247 memcpy(buff, sense_buffer, bufflen);
248 memset(sense_buffer, 0, sizeof(sense_buffer));
249 SCpnt->result = 0;
250 done(SCpnt);
251 return 0;
252 case ALLOW_MEDIUM_REMOVAL:
253 if (cmd[4]) {
254 SCSI_LOG_LLQUEUE(2, printk("Medium removal inhibited..."));
255 } else {
256 SCSI_LOG_LLQUEUE(2, printk("Medium removal enabled..."));
258 scsi_debug_errsts = 0;
259 break;
260 case INQUIRY:
261 SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
262 memset(buff, 0, bufflen);
263 buff[0] = TYPE_DISK;
264 buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
265 buff[2] = 1;
266 buff[4] = 33 - 5;
267 memcpy(&buff[8], "Foo Inc", 7);
268 memcpy(&buff[16], "XYZZY", 5);
269 memcpy(&buff[32], "1", 1);
270 scsi_debug_errsts = 0;
271 break;
272 case TEST_UNIT_READY:
273 SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", buff, bufflen));
274 if (buff)
275 memset(buff, 0, bufflen);
276 scsi_debug_errsts = 0;
277 break;
278 case READ_CAPACITY:
279 SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n"));
280 if (NR_REAL < 0)
281 NR_REAL = (MINOR(SCpnt->request.rq_dev) >> 4) & 0x0f;
282 memset(buff, 0, bufflen);
283 buff[0] = (CAPACITY >> 24);
284 buff[1] = (CAPACITY >> 16) & 0xff;
285 buff[2] = (CAPACITY >> 8) & 0xff;
286 buff[3] = CAPACITY & 0xff;
287 buff[6] = 2; /* 512 byte sectors */
288 scsi_debug_errsts = 0;
289 break;
290 case READ_10:
291 case READ_6:
292 #ifdef DEBUG
293 printk("Read...");
294 #endif
295 if ((*cmd) == READ_10)
296 block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
297 else
298 block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
299 VERIFY_DEBUG(READ);
300 #if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
302 int delay = SCSI_SETUP_LATENCY;
304 delay += SCpnt->request.nr_sectors * SCSI_DATARATE;
305 if (delay)
306 usleep(delay);
308 #endif
310 #ifdef DEBUG
311 printk("(r%d)", SCpnt->request.nr_sectors);
312 #endif
313 nbytes = bufflen;
314 if (SCpnt->use_sg) {
315 sgcount = 0;
316 sgpnt = (struct scatterlist *) buff;
317 buff = sgpnt[sgcount].address;
318 bufflen = sgpnt[sgcount].length;
319 bh = SCpnt->request.bh;
321 scsi_debug_errsts = 0;
322 do {
323 VERIFY1_DEBUG(READ);
324 /* For the speedy test, we do not even want to fill the buffer with anything */
325 #ifdef CLEAR
326 memset(buff, 0, bufflen);
327 #endif
328 /* If this is block 0, then we want to read the partition table for this
329 * device. Let's make one up */
330 if (block == 0) {
331 int i;
332 memset(buff, 0, bufflen);
333 *((unsigned short *) (buff + 510)) = 0xAA55;
334 p = (struct partition *) (buff + 0x1be);
335 i = 0;
336 while (starts[i + 1]) {
337 p->start_sect = starts[i];
338 p->nr_sects = starts[i + 1] - starts[i];
339 p->sys_ind = 0x81; /* Linux partition */
340 p->head = (i == 0 ? 1 : 0);
341 p->sector = 1;
342 p->cyl = starts[i] / N_HEAD / N_SECTOR;
343 p->end_head = N_HEAD - 1;
344 p->end_sector = N_SECTOR;
345 p->end_cyl = starts[i + 1] / N_HEAD / N_SECTOR;
346 p++;
347 i++;
349 if (!npart)
350 npart = i;
351 scsi_debug_errsts = 0;
352 break;
354 #ifdef DEBUG
355 if (SCpnt->use_sg)
356 printk("Block %x (%d %d)\n", block, SCpnt->request.nr_sectors,
357 SCpnt->request.current_nr_sectors);
358 #endif
360 #if 0
361 /* Simulate a disk change */
362 if (block == 0xfff0) {
363 sense_buffer[0] = 0x70;
364 sense_buffer[2] = UNIT_ATTENTION;
365 starts[0] += 10;
366 starts[1] += 10;
367 starts[2] += 10;
369 #ifdef DEBUG
371 int i;
372 printk("scsi_debug: Filling sense buffer:");
373 for (i = 0; i < 12; i++)
374 printk("%d ", sense_buffer[i]);
375 printk("\n");
377 #endif
378 scsi_debug_errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
379 break;
380 } /* End phony disk change code */
383 #ifdef CLEAR
384 memcpy(buff, &target, sizeof(target));
385 memcpy(buff + sizeof(target), cmd, 24);
386 memcpy(buff + 60, &block, sizeof(block));
387 memcpy(buff + 64, SCpnt, sizeof(Scsi_Cmnd));
388 #endif
389 nbytes -= bufflen;
390 if (SCpnt->use_sg) {
391 #ifdef CLEAR
392 memcpy(buff + 128, bh, sizeof(struct buffer_head));
393 #endif
394 block += bufflen >> 9;
395 bh = bh->b_reqnext;
396 sgcount++;
397 if (nbytes) {
398 if (!bh)
399 panic("Too few blocks for linked request.");
400 buff = sgpnt[sgcount].address;
401 bufflen = sgpnt[sgcount].length;
404 } while (nbytes);
406 SCpnt->result = 0;
407 (done) (SCpnt);
408 return 0;
410 if (SCpnt->use_sg && !scsi_debug_errsts)
411 if (bh)
412 scsi_dump(SCpnt, 0);
413 break;
414 case WRITE_10:
415 case WRITE_6:
416 #ifdef DEBUG
417 printk("Write\n");
418 #endif
419 if ((*cmd) == WRITE_10)
420 block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
421 else
422 block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
423 VERIFY_DEBUG(WRITE);
424 /* printk("(w%d)",SCpnt->request.nr_sectors); */
425 if (SCpnt->use_sg) {
426 if ((bufflen >> 9) != SCpnt->request.nr_sectors)
427 panic("Trying to write wrong number of blocks\n");
428 sgpnt = (struct scatterlist *) buff;
429 buff = sgpnt[sgcount].address;
431 #if 0
432 if (block != *((unsigned long *) (buff + 60))) {
433 printk("%x %x :", block, *((unsigned long *) (buff + 60)));
434 scsi_dump(SCpnt, 1);
435 panic("Bad block written.\n");
437 #endif
438 scsi_debug_errsts = 0;
439 break;
440 case MODE_SENSE:
442 * Used to detect write protected status.
444 scsi_debug_errsts = 0;
445 memset(buff, 0, 6);
446 break;
447 default:
448 SCSI_LOG_LLQUEUE(3, printk("Unknown command %d\n", *cmd));
449 SCpnt->result = DID_NO_CONNECT << 16;
450 done(SCpnt);
451 return 0;
454 save_flags(flags);
455 cli();
456 for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
457 if (timeout[i].function == NULL)
458 break;
462 * If all of the slots are full, just return 1. The new error handling scheme
463 * allows this, and the mid-level should queue things.
465 if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) {
466 SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n"));
467 restore_flags(flags);
468 return 1;
470 SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i));
472 #ifdef IMMEDIATE
473 if (!scsi_debug_lockup) {
474 SCpnt->result = scsi_debug_errsts;
475 scsi_debug_intr_handle(i); /* No timer - do this one right away */
477 restore_flags(flags);
478 #else
480 SCpnt->result = scsi_debug_errsts;
481 timeout[i].function = scsi_debug_intr_handle;
482 timeout[i].data = i;
483 timeout[i].expires = jiffies + DISK_SPEED;
484 SCint[i] = SCpnt;
485 do_done[i] = done;
487 restore_flags(flags);
488 add_timer(&timeout[i]);
489 if (!done)
490 panic("scsi_debug_queuecommand: done can't be NULL\n");
492 #if 0
493 printk("Sending command (%d %x %d %d)...", i, done, timeout[i].expires, jiffies);
494 #endif
495 #endif
497 return 0;
500 volatile static int internal_done_flag = 0;
501 volatile static int internal_done_errcode = 0;
502 static void internal_done(Scsi_Cmnd * SCpnt)
504 internal_done_errcode = SCpnt->result;
505 ++internal_done_flag;
508 int scsi_debug_command(Scsi_Cmnd * SCpnt)
510 DEB(printk("scsi_debug_command: ..calling scsi_debug_queuecommand\n"));
511 scsi_debug_queuecommand(SCpnt, internal_done);
513 while (!internal_done_flag);
514 internal_done_flag = 0;
515 return internal_done_errcode;
518 /* A "high" level interrupt handler. This should be called once per jiffy
519 * to simulate a regular scsi disk. We use a timer to do this. */
521 static void scsi_debug_intr_handle(unsigned long indx)
523 Scsi_Cmnd *SCtmp;
524 void (*my_done) (Scsi_Cmnd *);
525 #ifdef DEBUG
526 int to;
527 #endif
529 #if 0
530 del_timer(&timeout[indx]);
531 #endif
533 SCtmp = (Scsi_Cmnd *) SCint[indx];
534 my_done = do_done[indx];
535 do_done[indx] = NULL;
536 timeout[indx].function = NULL;
537 SCint[indx] = NULL;
539 if (!my_done) {
540 printk("scsi_debug_intr_handle: Unexpected interrupt\n");
541 return;
543 #ifdef DEBUG
544 printk("In intr_handle...");
545 printk("...done %d %x %d %d\n", i, my_done, to, jiffies);
546 printk("In intr_handle: %d %x %x\n", i, SCtmp, my_done);
547 #endif
549 my_done(SCtmp);
550 #ifdef DEBUG
551 printk("Called done.\n");
552 #endif
556 int scsi_debug_detect(Scsi_Host_Template * tpnt)
558 int i;
560 for (i = 0; i < NR_HOSTS_PRESENT; i++) {
561 tpnt->proc_dir = &proc_scsi_scsi_debug;
562 scsi_register(tpnt, 0);
564 return NR_HOSTS_PRESENT;
567 int scsi_debug_abort(Scsi_Cmnd * SCpnt)
569 #if 0
570 int j;
571 void (*my_done) (Scsi_Cmnd *);
572 unsigned long flags;
573 #endif
575 DEB(printk("scsi_debug_abort\n"));
576 #if 0
577 SCpnt->result = SCpnt->abort_reason << 16;
578 for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) {
579 if (SCpnt == SCint[j]) {
580 my_done = do_done[j];
581 my_done(SCpnt);
582 save_flags(flags);
583 cli();
584 timeout[j] = 0;
585 SCint[j] = NULL;
586 do_done[j] = NULL;
587 restore_flags(flags);
590 #endif
591 return SCSI_ABORT_SNOOZE;
594 int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info)
596 int size = disk->capacity;
597 info[0] = N_HEAD;
598 info[1] = N_SECTOR;
599 info[2] = (size + 2047) >> 11;
600 if (info[2] >= 1024)
601 info[2] = 1024;
602 return 0;
605 int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why)
607 int i;
608 unsigned long flags;
610 void (*my_done) (Scsi_Cmnd *);
611 printk("Bus unlocked by reset - %d\n", why);
612 scsi_debug_lockup = 0;
613 DEB(printk("scsi_debug_reset called\n"));
614 for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
615 if (SCint[i] == NULL)
616 continue;
617 SCint[i]->result = DID_RESET << 16;
618 my_done = do_done[i];
619 my_done(SCint[i]);
620 save_flags(flags);
621 cli();
622 SCint[i] = NULL;
623 do_done[i] = NULL;
624 timeout[i].function = NULL;
625 restore_flags(flags);
627 return SCSI_RESET_SUCCESS;
630 const char *scsi_debug_info(void)
632 static char buffer[] = " "; /* looks nicer without anything here */
633 return buffer;
636 /* scsi_debug_proc_info
637 * Used if the driver currently has no own support for /proc/scsi
639 int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
640 int length, int inode, int inout)
642 int len, pos, begin;
643 int orig_length;
645 orig_length = length;
647 if (inout == 1) {
648 /* First check for the Signature */
649 if (length >= 10 && strncmp(buffer, "scsi_debug", 10) == 0) {
650 buffer += 11;
651 length -= 11;
653 if (buffer[length - 1] == '\n') {
654 buffer[length - 1] = '\0';
655 length--;
658 * OK, we are getting some kind of command. Figure out
659 * what we are supposed to do here. Simulate bus lockups
660 * to test our reset capability.
662 if (length == 6 && strncmp(buffer, "lockup", length) == 0) {
663 scsi_debug_lockup = 1;
664 return orig_length;
666 if (length == 6 && strncmp(buffer, "unlock", length) == 0) {
667 scsi_debug_lockup = 0;
668 return orig_length;
670 printk("Unknown command:%s (%d)\n", buffer, length);
671 } else
672 printk("Wrong Signature:%10s\n", (char *) buffer);
674 return -EINVAL;
677 begin = 0;
678 pos = len = sprintf(buffer,
679 "This driver is not a real scsi driver, but it plays one on TV.\n"
680 "It is very handy for debugging specific problems because you\n"
681 "can simulate a variety of error conditions\n");
682 if (pos < offset) {
683 len = 0;
684 begin = pos;
686 *start = buffer + (offset - begin); /* Start of wanted data */
687 len -= (offset - begin);
688 if (len > length)
689 len = length;
691 return (len);
694 #ifdef MODULE
695 /* Eventually this will go into an include file, but this will be later */
696 Scsi_Host_Template driver_template = SCSI_DEBUG;
698 #include "scsi_module.c"
699 #endif
702 * Overrides for Emacs so that we almost follow Linus's tabbing style.
703 * Emacs will notice this stuff at the end of the file and automatically
704 * adjust the settings for this buffer only. This must remain at the end
705 * of the file.
706 * ---------------------------------------------------------------------------
707 * Local variables:
708 * c-indent-level: 4
709 * c-brace-imaginary-offset: 0
710 * c-brace-offset: -4
711 * c-argdecl-indent: 4
712 * c-label-offset: -4
713 * c-continued-statement-offset: 4
714 * c-continued-brace-offset: 0
715 * indent-tabs-mode: nil
716 * tab-width: 8
717 * End: