diff --git a/debian/patches-debian/amd64-int3-fix.patch b/debian/patches-debian/amd64-int3-fix.patch new file mode 100644 index 000000000..e02fbb948 --- /dev/null +++ b/debian/patches-debian/amd64-int3-fix.patch @@ -0,0 +1,18 @@ +## amd64-int3-fix.dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Revert int3 handling +## DP: Patch author: Andi Kleen +## DP: Upstream status: not yet committed to BK +# +--- a/arch/x86_64/kernel/kprobes.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/arch/x86_64/kernel/kprobes.c 2005-06-15 21:17:14.000000000 -0400 +@@ -447,6 +447,8 @@ + struct die_args *args = (struct die_args *)data; + switch (val) { + case DIE_INT3: ++ if (args->regs->cs & 3) ++ return NOTIFY_DONE; + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + break; diff --git a/debian/patches-debian/amd64-outs.patch b/debian/patches-debian/amd64-outs.patch new file mode 100644 index 000000000..3380ffab3 --- /dev/null +++ b/debian/patches-debian/amd64-outs.patch @@ -0,0 +1,24 @@ +#! /bin/sh -e +## .dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: [CAN-2005-0204]: AMD64, allows local users to write to privileged IO ports via OUTS instruction +## DP: Patch author: Suresh Siddha (suresh.b.siddha@intel.com) +## DP: Upstream status: unknown +## DP: URL: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=146244 +## DP: Patch source: Micah Anderson (debian-kernel) + +. $(dirname $0)/DPATCH + +@DPATCH@ +--- linux-2.6.9/include/asm-x86_64/desc.h 2005-01-30 20:08:12.799247944 -0800 ++++ linux-2.6.9/include/asm-x86_64/desc.h 2005-01-30 20:08:12.799247944 -0800 +@@ -128,7 +128,7 @@ + { + set_tssldt_descriptor(&cpu_gdt_table[cpu][GDT_ENTRY_TSS], (unsigned long)addr, + DESC_TSS, +- sizeof(struct tss_struct) - 1); ++ IO_BITMAP_OFFSET + IO_BITMAP_BYTES + 7); + } + + static inline void set_ldt_desc(unsigned cpu, void *addr, int size) diff --git a/debian/patches-debian/drivers-add-scsi_changer.patch b/debian/patches-debian/drivers-add-scsi_changer.patch new file mode 100644 index 000000000..5786a219c --- /dev/null +++ b/debian/patches-debian/drivers-add-scsi_changer.patch @@ -0,0 +1,1482 @@ +From: Gerd Knorr + +This patch adds a device driver for scsi media changer devices (tape +libraries for example). + +It exists for quite some time (since 2.2.x or so ...), is used by several +people and also is in some vendor kernels (debian, suse, maybe others as +well). People bug be from time to time to submit it ;) + +Recently I went over it and did some 2.6 cleanups, module parameters and +the like. And now, finally, here it is. + +Signed-off-by: Gerd Knorr +Signed-off-by: Andrew Morton +--- + + 25-akpm/Documentation/scsi-changer.txt | 180 +++++ + 25-akpm/drivers/scsi/Kconfig | 18 + 25-akpm/drivers/scsi/Makefile | 1 + 25-akpm/drivers/scsi/ch.c | 1020 +++++++++++++++++++++++++++++++++ + 25-akpm/include/linux/chio.h | 168 +++++ + 25-akpm/include/linux/major.h | 1 + 25-akpm/include/scsi/scsi.h | 3 + 7 files changed, 1391 insertions(+) + +diff -aurN a/Documentation/scsi-changer.txt b/Documentation/scsi-changer.txt +--- a/Documentation/scsi-changer.txt 1969-12-31 19:00:00.000000000 -0500 ++++ b/Documentation/scsi-changer.txt 2005-06-15 22:00:21.000000000 -0400 +@@ -0,0 +1,180 @@ ++ ++README for the SCSI media changer driver ++======================================== ++ ++This is a driver for SCSI Medium Changer devices, which are listed ++with "Type: Medium Changer" in /proc/scsi/scsi. ++ ++This is for *real* Jukeboxes. It is *not* supported to work with ++common small CD-ROM changers, neither one-lun-per-slot SCSI changers ++nor IDE drives. ++ ++Userland tools available from here: ++ http://linux.bytesex.org/misc/changer.html ++ ++ ++General Information ++------------------- ++ ++First some words about how changers work: A changer has 2 (possibly ++more) SCSI ID's. One for the changer device which controls the robot, ++and one for the device which actually reads and writes the data. The ++later may be anything, a MOD, a CD-ROM, a tape or whatever. For the ++changer device this is a "don't care", he *only* shuffles around the ++media, nothing else. ++ ++ ++The SCSI changer model is complex, compared to - for example - IDE-CD ++changers. But it allows to handle nearly all possible cases. It knows ++4 different types of changer elements: ++ ++ media transport - this one shuffles around the media, i.e. the ++ transport arm. Also known as "picker". ++ storage - a slot which can hold a media. ++ import/export - the same as above, but is accessable from outside, ++ i.e. there the operator (you !) can use this to ++ fill in and remove media from the changer. ++ Sometimes named "mailslot". ++ data transfer - this is the device which reads/writes, i.e. the ++ CD-ROM / Tape / whatever drive. ++ ++None of these is limited to one: A huge Jukebox could have slots for ++123 CD-ROM's, 5 CD-ROM readers (and therefore 6 SCSI ID's: the changer ++and each CD-ROM) and 2 transport arms. No problem to handle. ++ ++ ++How it is implemented ++--------------------- ++ ++I implemented the driver as character device driver with a NetBSD-like ++ioctl interface. Just grabbed NetBSD's header file and one of the ++other linux SCSI device drivers as starting point. The interface ++should be source code compatible with NetBSD. So if there is any ++software (anybody knows ???) which supports a BSDish changer driver, ++it should work with this driver too. ++ ++Over time a few more ioctls where added, volume tag support for example ++wasn't covered by the NetBSD ioctl API. ++ ++ ++Current State ++------------- ++ ++Support for more than one transport arm is not implemented yet (and ++nobody asked for it so far...). ++ ++I test and use the driver myself with a 35 slot cdrom jukebox from ++Grundig. I got some reports telling it works ok with tape autoloaders ++(Exabyte, HP and DEC). Some People use this driver with amanda. It ++works fine with small (11 slots) and a huge (4 MOs, 88 slots) ++magneto-optical Jukebox. Probably with lots of other changers too, most ++(but not all :-) people mail me only if it does *not* work... ++ ++I don't have any device lists, neither black-list nor white-list. Thus ++it is quite useless to ask me whenever a specific device is supported or ++not. In theory every changer device which supports the SCSI-2 media ++changer command set should work out-of-the-box with this driver. If it ++doesn't, it is a bug. Either within the driver or within the firmware ++of the changer device. ++ ++ ++Using it ++-------- ++ ++This is a character device with major number is 86, so use ++"mknod /dev/sch0 c 86 0" to create the special file for the driver. ++ ++If the module finds the changer, it prints some messages about the ++device [ try "dmesg" if you don't see anything ] and should show up in ++/proc/devices. If not.... some changers use ID ? / LUN 0 for the ++device and ID ? / LUN 1 for the robot mechanism. But Linux does *not* ++look for LUN's other than 0 as default, becauce there are to many ++broken devices. So you can try: ++ ++ 1) echo "scsi add-single-device 0 0 ID 1" > /proc/scsi/scsi ++ (replace ID with the SCSI-ID of the device) ++ 2) boot the kernel with "max_scsi_luns=1" on the command line ++ (append="max_scsi_luns=1" in lilo.conf should do the trick) ++ ++ ++Trouble? ++-------- ++ ++If you insmod the driver with "insmod debug=1", it will be verbose and ++prints a lot of stuff to the syslog. Compiling the kernel with ++CONFIG_SCSI_CONSTANTS=y improves the quality of the error messages alot ++because the kernel will translate the error codes into human-readable ++strings then. ++ ++You can display these messages with the dmesg command (or check the ++logfiles). If you email me some question becauce of a problem with the ++driver, please include these messages. ++ ++ ++Insmod options ++-------------- ++ ++debug=0/1 ++ Enable debug messages (see above, default: 0). ++ ++verbose=0/1 ++ Be verbose (default: 1). ++ ++init=0/1 ++ Send INITIALIZE ELEMENT STATUS command to the changer ++ at insmod time (default: 1). ++ ++timeout_init= ++ timeout for the INITIALIZE ELEMENT STATUS command ++ (default: 3600). ++ ++timeout_move= ++ timeout for all other commands (default: 120). ++ ++dt_id=,,... ++dt_lun=,,... ++ These two allow to specify the SCSI ID and LUN for the data ++ transfer elements. You likely don't need this as the jukebox ++ should provide this information. But some devices don't ... ++ ++vendor_firsts= ++vendor_counts= ++vendor_labels= ++ These insmod options can be used to tell the driver that there ++ are some vendor-specific element types. Grundig for example ++ does this. Some jukeboxes have a printer to label fresh burned ++ CDs, which is addressed as element 0xc000 (type 5). To tell the ++ driver about this vendor-specific element, use this: ++ $ insmod ch \ ++ vendor_firsts=0xc000 \ ++ vendor_counts=1 \ ++ vendor_labels=printer ++ All three insmod options accept up to four comma-separated ++ values, this way you can configure the element types 5-8. ++ You likely need the SCSI specs for the device in question to ++ find the correct values as they are not covered by the SCSI-2 ++ standard. ++ ++ ++Credits ++------- ++ ++I wrote this driver using the famous mailing-patches-around-the-world ++method. With (more or less) help from: ++ ++ Daniel Moehwald ++ Dane Jasper ++ R. Scott Bailey ++ Jonathan Corbet ++ ++Special thanks go to ++ Martin Kuehne ++for a old, second-hand (but full functional) cdrom jukebox which I use ++to develop/test driver and tools now. ++ ++Have fun, ++ ++ Gerd ++ ++-- ++Gerd Knorr +diff -aurN a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +--- a/drivers/scsi/Kconfig 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/scsi/Kconfig 2005-06-15 22:00:21.000000000 -0400 +@@ -137,6 +137,24 @@ + + If unsure, say N. + ++config CHR_DEV_SCH ++ tristate "SCSI media changer support" ++ depends on SCSI ++ ---help--- ++ This is a driver for SCSI media changers. Most common devices are ++ tape libraries and MOD/CDROM jukeboxes. *Real* jukeboxes, you ++ don't need this for those tiny 6-slot cdrom changers. Media ++ changers are listed as "Type: Medium Changer" in /proc/scsi/scsi. ++ If you have such hardware and want to use it with linux, say Y ++ here. Check for details. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read and ++ . The module will be called ch.o. ++ If unsure, say N. ++ ++ + comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs" + depends on SCSI + +diff -aurN a/drivers/scsi/Makefile b/drivers/scsi/Makefile +--- a/drivers/scsi/Makefile 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/scsi/Makefile 2005-06-15 22:00:21.000000000 -0400 +@@ -142,6 +142,7 @@ + obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o + obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o + obj-$(CONFIG_CHR_DEV_SG) += sg.o ++obj-$(CONFIG_CHR_DEV_SCH) += ch.o + + scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ + scsicam.o scsi_error.o scsi_lib.o \ +diff -aurN a/drivers/scsi/ch.c b/drivers/scsi/ch.c +--- a/drivers/scsi/ch.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/drivers/scsi/ch.c 2005-06-15 22:00:21.000000000 -0400 +@@ -0,0 +1,1020 @@ ++/* ++ * SCSI Media Changer device driver for Linux 2.6 ++ * ++ * (c) 1996-2003 Gerd Knorr ++ * ++ */ ++ ++#define VERSION "0.24" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* here are all the ioctls */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CH_DT_MAX 16 ++#define CH_TYPES 8 ++ ++MODULE_DESCRIPTION("device driver for scsi media changer devices"); ++MODULE_AUTHOR("Gerd Knorr "); ++MODULE_LICENSE("GPL"); ++ ++static int init = 1; ++module_param(init, int, 0444); ++MODULE_PARM_DESC(init, \ ++ "initialize element status on driver load (default: on)"); ++ ++static int timeout_move = 300; ++module_param(timeout_move, int, 0644); ++MODULE_PARM_DESC(timeout_move,"timeout for move commands " ++ "(default: 300 seconds)"); ++ ++static int timeout_init = 3600; ++module_param(timeout_init, int, 0644); ++MODULE_PARM_DESC(timeout_init,"timeout for INITIALIZE ELEMENT STATUS " ++ "(default: 3600 seconds)"); ++ ++static int verbose = 1; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose,"be verbose (default: on)"); ++ ++static int debug = 0; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more " ++ "detailed sense codes on scsi errors (default: off)"); ++ ++static int dt_id[CH_DT_MAX] = { [ 0 ... (CH_DT_MAX-1) ] = -1 }; ++static int dt_lun[CH_DT_MAX]; ++module_param_array(dt_id, int, NULL, 0444); ++module_param_array(dt_lun, int, NULL, 0444); ++ ++/* tell the driver about vendor-specific slots */ ++static int vendor_firsts[CH_TYPES-4]; ++static int vendor_counts[CH_TYPES-4]; ++module_param_array(vendor_firsts, int, NULL, 0444); ++module_param_array(vendor_counts, int, NULL, 0444); ++ ++static char *vendor_labels[CH_TYPES-4] = { ++ "v0", "v1", "v2", "v3" ++}; ++// module_param_string_array(vendor_labels, NULL, 0444); ++ ++#define dprintk(fmt, arg...) if (debug) \ ++ printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg) ++#define vprintk(fmt, arg...) if (verbose) \ ++ printk(KERN_INFO "%s: " fmt, ch->name, ##arg) ++ ++/* ------------------------------------------------------------------- */ ++ ++#define MAX_RETRIES 1 ++ ++static int ch_probe(struct device *); ++static int ch_remove(struct device *); ++static int ch_open(struct inode * inode, struct file * filp); ++static int ch_release(struct inode * inode, struct file * filp); ++static int ch_ioctl(struct inode * inode, struct file * filp, ++ unsigned int cmd, unsigned long arg); ++static long ch_ioctl_compat(struct file * filp, ++ unsigned int cmd, unsigned long arg); ++ ++typedef struct { ++ struct list_head list; ++ int minor; ++ char name[8]; ++ struct scsi_device *device; ++ struct scsi_device **dt; /* ptrs to data transfer elements */ ++ u_int firsts[CH_TYPES]; ++ u_int counts[CH_TYPES]; ++ u_int unit_attention; ++ u_int voltags; ++ struct semaphore lock; ++} scsi_changer; ++ ++static LIST_HEAD(ch_devlist); ++static spinlock_t ch_devlist_lock = SPIN_LOCK_UNLOCKED; ++static int ch_devcount; ++ ++struct scsi_driver ch_template = ++{ ++ .owner = THIS_MODULE, ++ .gendrv = { ++ .name = "ch", ++ .probe = ch_probe, ++ .remove = ch_remove, ++ }, ++}; ++ ++static struct file_operations changer_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = ch_open, ++ .release = ch_release, ++ .ioctl = ch_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = ch_ioctl_compat, ++#endif ++}; ++ ++static struct { ++ unsigned char sense; ++ unsigned char asc; ++ unsigned char ascq; ++ int errno; ++} err[] = { ++/* Just filled in what looks right. Hav'nt checked any standard paper for ++ these errno assignments, so they may be wrong... */ ++ { ++ .sense = ILLEGAL_REQUEST, ++ .asc = 0x21, ++ .ascq = 0x01, ++ .errno = EBADSLT, /* Invalid element address */ ++ },{ ++ .sense = ILLEGAL_REQUEST, ++ .asc = 0x28, ++ .ascq = 0x01, ++ .errno = EBADE, /* Import or export element accessed */ ++ },{ ++ .sense = ILLEGAL_REQUEST, ++ .asc = 0x3B, ++ .ascq = 0x0D, ++ .errno = EXFULL, /* Medium destination element full */ ++ },{ ++ .sense = ILLEGAL_REQUEST, ++ .asc = 0x3B, ++ .ascq = 0x0E, ++ .errno = EBADE, /* Medium source element empty */ ++ },{ ++ .sense = ILLEGAL_REQUEST, ++ .asc = 0x20, ++ .ascq = 0x00, ++ .errno = EBADRQC, /* Invalid command operation code */ ++ },{ ++ /* end of list */ ++ } ++}; ++ ++/* ------------------------------------------------------------------- */ ++ ++static int ch_find_errno(unsigned char *sense_buffer) ++{ ++ int i,errno = 0; ++ ++ /* Check to see if additional sense information is available */ ++ if (sense_buffer[7] > 5 && ++ sense_buffer[12] != 0) { ++ for (i = 0; err[i].errno != 0; i++) { ++ if (err[i].sense == sense_buffer[ 2] && ++ err[i].asc == sense_buffer[12] && ++ err[i].ascq == sense_buffer[13]) { ++ errno = -err[i].errno; ++ break; ++ } ++ } ++ } ++ if (errno == 0) ++ errno = -EIO; ++ return errno; ++} ++ ++static void ++ch_request_done(struct scsi_cmnd *sc) ++{ ++ sc->request->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ ++ if (sc->request->waiting != NULL) ++ complete(sc->request->waiting); ++} ++ ++static int ++ch_do_scsi(scsi_changer *ch, unsigned char *cmd, ++ void *buffer, unsigned buflength) ++{ ++ int errno, retries = 0, timeout; ++ DECLARE_COMPLETION(wait); ++ struct scsi_request *sr; ++ ++ sr = scsi_allocate_request(ch->device, GFP_KERNEL); ++ if (NULL == sr) ++ return -ENOMEM; ++ ++ retry: ++ errno = 0; ++ if (debug) { ++ dprintk("command: "); ++ __scsi_print_command(cmd); ++ } ++ ++ sr->sr_request->waiting = &wait; ++ timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS) ++ ? timeout_init : timeout_move; ++ scsi_do_req(sr, cmd, buffer, buflength, ch_request_done, ++ timeout * HZ, MAX_RETRIES); ++ wait_for_completion(&wait); ++ sr->sr_request->waiting = NULL; ++ ++ dprintk("result: 0x%x\n",sr->sr_result); ++ if (driver_byte(sr->sr_result) != 0) { ++ if (debug) ++ scsi_print_req_sense(ch->name, sr); ++ errno = ch_find_errno(sr->sr_sense_buffer); ++ ++ switch(sr->sr_sense_buffer[2] & 0xf) { ++ case UNIT_ATTENTION: ++ ch->unit_attention = 1; ++ if (retries++ < 3) ++ goto retry; ++ break; ++ } ++ } ++ scsi_release_request(sr); ++ return errno; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int ++ch_elem_to_typecode(scsi_changer *ch, u_int elem) ++{ ++ int i; ++ ++ for (i = 0; i < CH_TYPES; i++) { ++ if (elem >= ch->firsts[i] && ++ elem < ch->firsts[i] + ++ ch->counts[i]) ++ return i+1; ++ } ++ return 0; ++} ++ ++static int ++ch_read_element_status(scsi_changer *ch, u_int elem, char *data) ++{ ++ u_char cmd[12]; ++ u_char *buffer; ++ int result; ++ ++ buffer = kmalloc(512, GFP_KERNEL); ++ if(!buffer) ++ return -ENOMEM; ++ ++ retry: ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = READ_ELEMENT_STATUS; ++ cmd[1] = (ch->device->lun << 5) | ++ (ch->voltags ? 0x10 : 0) | ++ ch_elem_to_typecode(ch,elem); ++ cmd[2] = (elem >> 8) & 0xff; ++ cmd[3] = elem & 0xff; ++ cmd[5] = 1; ++ cmd[9] = 255; ++ if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256))) { ++ if (((buffer[16] << 8) | buffer[17]) != elem) { ++ dprintk("asked for element 0x%02x, got 0x%02x\n", ++ elem,(buffer[16] << 8) | buffer[17]); ++ kfree(buffer); ++ return -EIO; ++ } ++ memcpy(data,buffer+16,16); ++ } else { ++ if (ch->voltags) { ++ ch->voltags = 0; ++ vprintk("device has no volume tag support%s\n",""); ++ goto retry; ++ } ++ dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem); ++ } ++ kfree(buffer); ++ return result; ++} ++ ++static int ++ch_init_elem(scsi_changer *ch) ++{ ++ int err; ++ u_char cmd[6]; ++ ++ vprintk("INITIALIZE ELEMENT STATUS, may take some time ...%s\n",""); ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = INITIALIZE_ELEMENT_STATUS; ++ cmd[1] = ch->device->lun << 5; ++ err = ch_do_scsi(ch, cmd, NULL, 0); ++ vprintk("... finished%s\n",""); ++ return err; ++} ++ ++static int ++ch_readconfig(scsi_changer *ch) ++{ ++ u_char cmd[10], data[16]; ++ u_char *buffer; ++ int result,id,lun,i; ++ u_int elem; ++ ++ buffer = kmalloc(512, GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ memset(buffer,0,512); ++ ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = MODE_SENSE; ++ cmd[1] = ch->device->lun << 5; ++ cmd[2] = 0x1d; ++ cmd[4] = 255; ++ result = ch_do_scsi(ch, cmd, buffer, 255); ++ if (0 != result) { ++ cmd[1] |= (1<<3); ++ result = ch_do_scsi(ch, cmd, buffer, 255); ++ } ++ if (0 == result) { ++ ch->firsts[CHET_MT] = ++ (buffer[buffer[3]+ 6] << 8) | buffer[buffer[3]+ 7]; ++ ch->counts[CHET_MT] = ++ (buffer[buffer[3]+ 8] << 8) | buffer[buffer[3]+ 9]; ++ ch->firsts[CHET_ST] = ++ (buffer[buffer[3]+10] << 8) | buffer[buffer[3]+11]; ++ ch->counts[CHET_ST] = ++ (buffer[buffer[3]+12] << 8) | buffer[buffer[3]+13]; ++ ch->firsts[CHET_IE] = ++ (buffer[buffer[3]+14] << 8) | buffer[buffer[3]+15]; ++ ch->counts[CHET_IE] = ++ (buffer[buffer[3]+16] << 8) | buffer[buffer[3]+17]; ++ ch->firsts[CHET_DT] = ++ (buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19]; ++ ch->counts[CHET_DT] = ++ (buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21]; ++ vprintk("type #1 (mt): 0x%x+%d [medium transport]\n", ++ ch->firsts[CHET_MT], ++ ch->counts[CHET_MT]); ++ vprintk("type #2 (st): 0x%x+%d [storage]\n", ++ ch->firsts[CHET_ST], ++ ch->counts[CHET_ST]); ++ vprintk("type #3 (ie): 0x%x+%d [import/export]\n", ++ ch->firsts[CHET_IE], ++ ch->counts[CHET_IE]); ++ vprintk("type #4 (dt): 0x%x+%d [data transfer]\n", ++ ch->firsts[CHET_DT], ++ ch->counts[CHET_DT]); ++ } else { ++ vprintk("reading element address assigment page failed!%s\n", ++ ""); ++ } ++ ++ /* vendor specific element types */ ++ for (i = 0; i < 4; i++) { ++ if (0 == vendor_counts[i]) ++ continue; ++ if (NULL == vendor_labels[i]) ++ continue; ++ ch->firsts[CHET_V1+i] = vendor_firsts[i]; ++ ch->counts[CHET_V1+i] = vendor_counts[i]; ++ vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n", ++ i+5,i+1,vendor_firsts[i],vendor_counts[i], ++ vendor_labels[i]); ++ } ++ ++ /* look up the devices of the data transfer elements */ ++ ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device), ++ GFP_KERNEL); ++ for (elem = 0; elem < ch->counts[CHET_DT]; elem++) { ++ id = -1; ++ lun = 0; ++ if (-1 != dt_id[elem]) { ++ id = dt_id[elem]; ++ lun = dt_lun[elem]; ++ vprintk("dt 0x%x: [insmod option] ", ++ elem+ch->firsts[CHET_DT]); ++ } else if (0 != ch_read_element_status ++ (ch,elem+ch->firsts[CHET_DT],data)) { ++ vprintk("dt 0x%x: READ ELEMENT STATUS failed\n", ++ elem+ch->firsts[CHET_DT]); ++ } else { ++ vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]); ++ if (data[6] & 0x80) { ++ if (verbose) ++ printk("not this SCSI bus\n"); ++ ch->dt[elem] = NULL; ++ } else if (0 == (data[6] & 0x30)) { ++ if (verbose) ++ printk("ID/LUN unknown\n"); ++ ch->dt[elem] = NULL; ++ } else { ++ id = ch->device->id; ++ lun = 0; ++ if (data[6] & 0x20) id = data[7]; ++ if (data[6] & 0x10) lun = data[6] & 7; ++ } ++ } ++ if (-1 != id) { ++ if (verbose) ++ printk("ID %i, LUN %i, ",id,lun); ++ ch->dt[elem] = ++ scsi_device_lookup(ch->device->host, ++ ch->device->channel, ++ id,lun); ++ if (!ch->dt[elem]) { ++ /* should not happen */ ++ if (verbose) ++ printk("Huh? device not found!\n"); ++ } else { ++ if (verbose) ++ printk("name: %8.8s %16.16s %4.4s\n", ++ ch->dt[elem]->vendor, ++ ch->dt[elem]->model, ++ ch->dt[elem]->rev); ++ } ++ } ++ } ++ ch->voltags = 1; ++ kfree(buffer); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int ++ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate) ++{ ++ u_char cmd[10]; ++ ++ dprintk("position: 0x%x\n",elem); ++ if (0 == trans) ++ trans = ch->firsts[CHET_MT]; ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = POSITION_TO_ELEMENT; ++ cmd[1] = ch->device->lun << 5; ++ cmd[2] = (trans >> 8) & 0xff; ++ cmd[3] = trans & 0xff; ++ cmd[4] = (elem >> 8) & 0xff; ++ cmd[5] = elem & 0xff; ++ cmd[8] = rotate ? 1 : 0; ++ return ch_do_scsi(ch, cmd, NULL,0); ++} ++ ++static int ++ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate) ++{ ++ u_char cmd[12]; ++ ++ dprintk("move: 0x%x => 0x%x\n",src,dest); ++ if (0 == trans) ++ trans = ch->firsts[CHET_MT]; ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = MOVE_MEDIUM; ++ cmd[1] = ch->device->lun << 5; ++ cmd[2] = (trans >> 8) & 0xff; ++ cmd[3] = trans & 0xff; ++ cmd[4] = (src >> 8) & 0xff; ++ cmd[5] = src & 0xff; ++ cmd[6] = (dest >> 8) & 0xff; ++ cmd[7] = dest & 0xff; ++ cmd[10] = rotate ? 1 : 0; ++ return ch_do_scsi(ch, cmd, NULL,0); ++} ++ ++static int ++ch_exchange(scsi_changer *ch, u_int trans, u_int src, ++ u_int dest1, u_int dest2, int rotate1, int rotate2) ++{ ++ u_char cmd[12]; ++ ++ dprintk("exchange: 0x%x => 0x%x => 0x%x\n", ++ src,dest1,dest2); ++ if (0 == trans) ++ trans = ch->firsts[CHET_MT]; ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = EXCHANGE_MEDIUM; ++ cmd[1] = ch->device->lun << 5; ++ cmd[2] = (trans >> 8) & 0xff; ++ cmd[3] = trans & 0xff; ++ cmd[4] = (src >> 8) & 0xff; ++ cmd[5] = src & 0xff; ++ cmd[6] = (dest1 >> 8) & 0xff; ++ cmd[7] = dest1 & 0xff; ++ cmd[8] = (dest2 >> 8) & 0xff; ++ cmd[9] = dest2 & 0xff; ++ cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0); ++ ++ return ch_do_scsi(ch, cmd, NULL,0); ++} ++ ++static void ++ch_check_voltag(char *tag) ++{ ++ int i; ++ ++ for (i = 0; i < 32; i++) { ++ /* restrict to ascii */ ++ if (tag[i] >= 0x7f || tag[i] < 0x20) ++ tag[i] = ' '; ++ /* don't allow search wildcards */ ++ if (tag[i] == '?' || ++ tag[i] == '*') ++ tag[i] = ' '; ++ } ++} ++ ++static int ++ch_set_voltag(scsi_changer *ch, u_int elem, ++ int alternate, int clear, u_char *tag) ++{ ++ u_char cmd[12]; ++ u_char *buffer; ++ int result; ++ ++ buffer = kmalloc(512, GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ memset(buffer,0,512); ++ ++ dprintk("%s %s voltag: 0x%x => \"%s\"\n", ++ clear ? "clear" : "set", ++ alternate ? "alternate" : "primary", ++ elem, tag); ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = SEND_VOLUME_TAG; ++ cmd[1] = (ch->device->lun << 5) | ++ ch_elem_to_typecode(ch,elem); ++ cmd[2] = (elem >> 8) & 0xff; ++ cmd[3] = elem & 0xff; ++ cmd[5] = clear ++ ? (alternate ? 0x0d : 0x0c) ++ : (alternate ? 0x0b : 0x0a); ++ ++ cmd[9] = 255; ++ ++ memcpy(buffer,tag,32); ++ ch_check_voltag(buffer); ++ ++ result = ch_do_scsi(ch, cmd, buffer, 256); ++ kfree(buffer); ++ return result; ++} ++ ++static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest) ++{ ++ int retval = 0; ++ u_char data[16]; ++ unsigned int i; ++ ++ down(&ch->lock); ++ for (i = 0; i < ch->counts[type]; i++) { ++ if (0 != ch_read_element_status ++ (ch, ch->firsts[type]+i,data)) { ++ retval = -EIO; ++ break; ++ } ++ put_user(data[2], dest+i); ++ if (data[2] & CESTATUS_EXCEPT) ++ vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n", ++ ch->firsts[type]+i, ++ (int)data[4],(int)data[5]); ++ retval = ch_read_element_status ++ (ch, ch->firsts[type]+i,data); ++ if (0 != retval) ++ break; ++ } ++ up(&ch->lock); ++ return retval; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int ++ch_release(struct inode *inode, struct file *file) ++{ ++ scsi_changer *ch = file->private_data; ++ ++ scsi_device_put(ch->device); ++ file->private_data = NULL; ++ return 0; ++} ++ ++static int ++ch_open(struct inode *inode, struct file *file) ++{ ++ scsi_changer *tmp, *ch; ++ int minor = iminor(inode); ++ ++ spin_lock(&ch_devlist_lock); ++ ch = NULL; ++ list_for_each_entry(tmp,&ch_devlist,list) { ++ if (tmp->minor == minor) ++ ch = tmp; ++ } ++ if (NULL == ch || scsi_device_get(ch->device)) { ++ spin_unlock(&ch_devlist_lock); ++ return -ENXIO; ++ } ++ spin_unlock(&ch_devlist_lock); ++ ++ file->private_data = ch; ++ return 0; ++} ++ ++static int ++ch_checkrange(scsi_changer *ch, unsigned int type, unsigned int unit) ++{ ++ if (type >= CH_TYPES || unit >= ch->counts[type]) ++ return -1; ++ return 0; ++} ++ ++static int ch_ioctl(struct inode * inode, struct file * file, ++ unsigned int cmd, unsigned long arg) ++{ ++ scsi_changer *ch = file->private_data; ++ int retval; ++ ++ switch (cmd) { ++ case CHIOGPARAMS: ++ { ++ struct changer_params params; ++ ++ params.cp_curpicker = 0; ++ params.cp_npickers = ch->counts[CHET_MT]; ++ params.cp_nslots = ch->counts[CHET_ST]; ++ params.cp_nportals = ch->counts[CHET_IE]; ++ params.cp_ndrives = ch->counts[CHET_DT]; ++ ++ if (copy_to_user((void *) arg, ¶ms, sizeof(params))) ++ return -EFAULT; ++ return 0; ++ } ++ case CHIOGVPARAMS: ++ { ++ struct changer_vendor_params vparams; ++ ++ memset(&vparams,0,sizeof(vparams)); ++ if (ch->counts[CHET_V1]) { ++ vparams.cvp_n1 = ch->counts[CHET_V1]; ++ strncpy(vparams.cvp_label1,vendor_labels[0],16); ++ } ++ if (ch->counts[CHET_V2]) { ++ vparams.cvp_n2 = ch->counts[CHET_V2]; ++ strncpy(vparams.cvp_label2,vendor_labels[1],16); ++ } ++ if (ch->counts[CHET_V3]) { ++ vparams.cvp_n3 = ch->counts[CHET_V3]; ++ strncpy(vparams.cvp_label3,vendor_labels[2],16); ++ } ++ if (ch->counts[CHET_V4]) { ++ vparams.cvp_n4 = ch->counts[CHET_V4]; ++ strncpy(vparams.cvp_label4,vendor_labels[3],16); ++ } ++ if (copy_to_user((void *) arg, &vparams, sizeof(vparams))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ case CHIOPOSITION: ++ { ++ struct changer_position pos; ++ ++ if (copy_from_user(&pos, (void*)arg, sizeof (pos))) ++ return -EFAULT; ++ ++ if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) { ++ dprintk("CHIOPOSITION: invalid parameter%s\n",""); ++ return -EBADSLT; ++ } ++ down(&ch->lock); ++ retval = ch_position(ch,0, ++ ch->firsts[pos.cp_type] + pos.cp_unit, ++ pos.cp_flags & CP_INVERT); ++ up(&ch->lock); ++ return retval; ++ } ++ ++ case CHIOMOVE: ++ { ++ struct changer_move mv; ++ ++ if (copy_from_user(&mv, (void*)arg, sizeof (mv))) ++ return -EFAULT; ++ ++ if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) || ++ 0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) { ++ dprintk("CHIOMOVE: invalid parameter%s\n",""); ++ return -EBADSLT; ++ } ++ ++ down(&ch->lock); ++ retval = ch_move(ch,0, ++ ch->firsts[mv.cm_fromtype] + mv.cm_fromunit, ++ ch->firsts[mv.cm_totype] + mv.cm_tounit, ++ mv.cm_flags & CM_INVERT); ++ up(&ch->lock); ++ return retval; ++ } ++ ++ case CHIOEXCHANGE: ++ { ++ struct changer_exchange mv; ++ ++ if (copy_from_user(&mv, (void*)arg, sizeof (mv))) ++ return -EFAULT; ++ ++ if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) || ++ 0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) || ++ 0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) { ++ dprintk("CHIOEXCHANGE: invalid parameter%s\n",""); ++ return -EBADSLT; ++ } ++ ++ down(&ch->lock); ++ retval = ch_exchange ++ (ch,0, ++ ch->firsts[mv.ce_srctype] + mv.ce_srcunit, ++ ch->firsts[mv.ce_fdsttype] + mv.ce_fdstunit, ++ ch->firsts[mv.ce_sdsttype] + mv.ce_sdstunit, ++ mv.ce_flags & CE_INVERT1, mv.ce_flags & CE_INVERT2); ++ up(&ch->lock); ++ return retval; ++ } ++ ++ case CHIOGSTATUS: ++ { ++ struct changer_element_status ces; ++ ++ if (copy_from_user(&ces, (void*)arg, sizeof (ces))) ++ return -EFAULT; ++ if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES) ++ return -EINVAL; ++ ++ return ch_gstatus(ch, ces.ces_type, ces.ces_data); ++ } ++ ++ case CHIOGELEM: ++ { ++ struct changer_get_element cge; ++ u_char cmd[12]; ++ u_char *buffer; ++ unsigned int elem; ++ int result,i; ++ ++ if (copy_from_user(&cge, (void*)arg, sizeof (cge))) ++ return -EFAULT; ++ ++ if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit)) ++ return -EINVAL; ++ elem = ch->firsts[cge.cge_type] + cge.cge_unit; ++ ++ buffer = kmalloc(512, GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ down(&ch->lock); ++ ++ voltag_retry: ++ memset(cmd,0,sizeof(cmd)); ++ cmd[0] = READ_ELEMENT_STATUS; ++ cmd[1] = (ch->device->lun << 5) | ++ (ch->voltags ? 0x10 : 0) | ++ ch_elem_to_typecode(ch,elem); ++ cmd[2] = (elem >> 8) & 0xff; ++ cmd[3] = elem & 0xff; ++ cmd[5] = 1; ++ cmd[9] = 255; ++ ++ if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256))) { ++ cge.cge_status = buffer[18]; ++ cge.cge_flags = 0; ++ if (buffer[18] & CESTATUS_EXCEPT) { ++ /* FIXME: fill cge_errno */ ++ } ++ if (buffer[25] & 0x80) { ++ cge.cge_flags |= CGE_SRC; ++ if (buffer[25] & 0x40) ++ cge.cge_flags |= CGE_INVERT; ++ elem = (buffer[26]<<8) | buffer[27]; ++ for (i = 0; i < 4; i++) { ++ if (elem >= ch->firsts[i] && ++ elem < ch->firsts[i] + ch->counts[i]) { ++ cge.cge_srctype = i; ++ cge.cge_srcunit = elem-ch->firsts[i]; ++ } ++ } ++ } ++ if ((buffer[22] & 0x30) == 0x30) { ++ cge.cge_flags |= CGE_IDLUN; ++ cge.cge_id = buffer[23]; ++ cge.cge_lun = buffer[22] & 7; ++ } ++ if (buffer[9] & 0x80) { ++ cge.cge_flags |= CGE_PVOLTAG; ++ memcpy(cge.cge_pvoltag,buffer+28,36); ++ } ++ if (buffer[9] & 0x40) { ++ cge.cge_flags |= CGE_AVOLTAG; ++ memcpy(cge.cge_avoltag,buffer+64,36); ++ } ++ } else if (ch->voltags) { ++ ch->voltags = 0; ++ vprintk("device has no volume tag support%s\n",""); ++ goto voltag_retry; ++ } ++ kfree(buffer); ++ up(&ch->lock); ++ ++ if (copy_to_user((void*)arg, &cge, sizeof (cge))) ++ return -EFAULT; ++ return result; ++ } ++ ++ case CHIOINITELEM: ++ { ++ down(&ch->lock); ++ retval = ch_init_elem(ch); ++ up(&ch->lock); ++ return retval; ++ } ++ ++ case CHIOSVOLTAG: ++ { ++ struct changer_set_voltag csv; ++ int elem; ++ ++ if (copy_from_user(&csv, (void*)arg, sizeof(csv))) ++ return -EFAULT; ++ ++ if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) { ++ dprintk("CHIOSVOLTAG: invalid parameter%s\n",""); ++ return -EBADSLT; ++ } ++ elem = ch->firsts[csv.csv_type] + csv.csv_unit; ++ down(&ch->lock); ++ retval = ch_set_voltag(ch, elem, ++ csv.csv_flags & CSV_AVOLTAG, ++ csv.csv_flags & CSV_CLEARTAG, ++ csv.csv_voltag); ++ up(&ch->lock); ++ return retval; ++ } ++ ++ default: ++ return scsi_ioctl(ch->device, cmd, (void*)arg); ++ ++ } ++} ++ ++#ifdef CONFIG_COMPAT ++ ++struct changer_element_status32 { ++ int ces_type; ++ compat_uptr_t ces_data; ++}; ++#define CHIOGSTATUS32 _IOW('c', 8,struct changer_element_status32) ++ ++static long ch_ioctl_compat(struct file * file, ++ unsigned int cmd, unsigned long arg) ++{ ++ scsi_changer *ch = file->private_data; ++ ++ switch (cmd) { ++ case CHIOGPARAMS: ++ case CHIOGVPARAMS: ++ case CHIOPOSITION: ++ case CHIOMOVE: ++ case CHIOEXCHANGE: ++ case CHIOGELEM: ++ case CHIOINITELEM: ++ case CHIOSVOLTAG: ++ /* compatible */ ++ return ch_ioctl(NULL /* inode, unused */, ++ file, cmd, arg); ++ case CHIOGSTATUS32: ++ { ++ struct changer_element_status32 ces32; ++ unsigned char *data; ++ ++ if (copy_from_user(&ces32, (void*)arg, sizeof (ces32))) ++ return -EFAULT; ++ if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES) ++ return -EINVAL; ++ ++ data = compat_ptr(ces32.ces_data); ++ return ch_gstatus(ch, ces32.ces_type, data); ++ } ++ default: ++ // return scsi_ioctl_compat(ch->device, cmd, (void*)arg); ++ return -ENOIOCTLCMD; ++ ++ } ++} ++#endif ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int ch_probe(struct device *dev) ++{ ++ struct scsi_device *sd = to_scsi_device(dev); ++ scsi_changer *ch; ++ ++ if (sd->type != TYPE_MEDIUM_CHANGER) ++ return -ENODEV; ++ ++ ch = kmalloc(sizeof(*ch), GFP_KERNEL); ++ if (NULL == ch) ++ return -ENOMEM; ++ ++ memset(ch,0,sizeof(*ch)); ++ ch->minor = ch_devcount; ++ sprintf(ch->name,"ch%d",ch->minor); ++ init_MUTEX(&ch->lock); ++ ch->device = sd; ++ ch_readconfig(ch); ++ if (init) ++ ch_init_elem(ch); ++ ++ devfs_mk_cdev(MKDEV(SCSI_CHANGER_MAJOR,ch->minor), ++ S_IFCHR | S_IRUGO | S_IWUGO, ch->name); ++ ++ printk(KERN_INFO "Attached scsi changer %s " ++ "at scsi%d, channel %d, id %d, lun %d\n", ++ ch->name, sd->host->host_no, sd->channel, sd->id, sd->lun); ++ ++ spin_lock(&ch_devlist_lock); ++ list_add_tail(&ch->list,&ch_devlist); ++ ch_devcount++; ++ spin_unlock(&ch_devlist_lock); ++ return 0; ++} ++ ++static int ch_remove(struct device *dev) ++{ ++ struct scsi_device *sd = to_scsi_device(dev); ++ scsi_changer *tmp, *ch; ++ ++ spin_lock(&ch_devlist_lock); ++ ch = NULL; ++ list_for_each_entry(tmp,&ch_devlist,list) { ++ if (tmp->device == sd) ++ ch = tmp; ++ } ++ BUG_ON(NULL == ch); ++ list_del(&ch->list); ++ spin_unlock(&ch_devlist_lock); ++ ++ devfs_remove(ch->name); ++ kfree(ch->dt); ++ kfree(ch); ++ ch_devcount--; ++ return 0; ++} ++ ++static int __init init_ch_module(void) ++{ ++ int rc; ++ ++ printk(KERN_INFO "SCSI Media Changer driver v" VERSION " \n"); ++ rc = register_chrdev(SCSI_CHANGER_MAJOR,"ch",&changer_fops); ++ if (rc < 0) { ++ printk("Unable to get major %d for SCSI-Changer\n", ++ SCSI_CHANGER_MAJOR); ++ return rc; ++ } ++ rc = scsi_register_driver(&ch_template.gendrv); ++ if (rc < 0) ++ goto fail1; ++ return 0; ++ ++ fail1: ++ unregister_chrdev(SCSI_CHANGER_MAJOR, "ch"); ++ return rc; ++} ++ ++static void __exit exit_ch_module(void) ++{ ++ scsi_unregister_driver(&ch_template.gendrv); ++ unregister_chrdev(SCSI_CHANGER_MAJOR, "ch"); ++} ++ ++module_init(init_ch_module); ++module_exit(exit_ch_module); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -aurN a/include/linux/chio.h b/include/linux/chio.h +--- a/include/linux/chio.h 1969-12-31 19:00:00.000000000 -0500 ++++ b/include/linux/chio.h 2005-06-15 22:00:21.000000000 -0400 +@@ -0,0 +1,168 @@ ++/* ++ * ioctl interface for the scsi media changer driver ++ */ ++ ++/* changer element types */ ++#define CHET_MT 0 /* media transport element (robot) */ ++#define CHET_ST 1 /* storage element (media slots) */ ++#define CHET_IE 2 /* import/export element */ ++#define CHET_DT 3 /* data transfer element (tape/cdrom/whatever) */ ++#define CHET_V1 4 /* vendor specific #1 */ ++#define CHET_V2 5 /* vendor specific #2 */ ++#define CHET_V3 6 /* vendor specific #3 */ ++#define CHET_V4 7 /* vendor specific #4 */ ++ ++ ++/* ++ * CHIOGPARAMS ++ * query changer properties ++ * ++ * CHIOVGPARAMS ++ * query vendor-specific element types ++ * ++ * accessing elements works by specifing type and unit of the element. ++ * for eample, storage elements are addressed with type = CHET_ST and ++ * unit = 0 .. cp_nslots-1 ++ * ++ */ ++struct changer_params { ++ int cp_curpicker; /* current transport element */ ++ int cp_npickers; /* number of transport elements (CHET_MT) */ ++ int cp_nslots; /* number of storage elements (CHET_ST) */ ++ int cp_nportals; /* number of import/export elements (CHET_IE) */ ++ int cp_ndrives; /* number of data transfer elements (CHET_DT) */ ++}; ++struct changer_vendor_params { ++ int cvp_n1; /* number of vendor specific elems (CHET_V1) */ ++ char cvp_label1[16]; ++ int cvp_n2; /* number of vendor specific elems (CHET_V2) */ ++ char cvp_label2[16]; ++ int cvp_n3; /* number of vendor specific elems (CHET_V3) */ ++ char cvp_label3[16]; ++ int cvp_n4; /* number of vendor specific elems (CHET_V4) */ ++ char cvp_label4[16]; ++ int reserved[8]; ++}; ++ ++ ++/* ++ * CHIOMOVE ++ * move a medium from one element to another ++ */ ++struct changer_move { ++ int cm_fromtype; /* type/unit of source element */ ++ int cm_fromunit; ++ int cm_totype; /* type/unit of destination element */ ++ int cm_tounit; ++ int cm_flags; ++}; ++#define CM_INVERT 1 /* flag: rotate media (for double-sided like MOD) */ ++ ++ ++/* ++ * CHIOEXCHANGE ++ * move one medium from element #1 to element #2, ++ * and another one from element #2 to element #3. ++ * element #1 and #3 are allowed to be identical. ++ */ ++struct changer_exchange { ++ int ce_srctype; /* type/unit of element #1 */ ++ int ce_srcunit; ++ int ce_fdsttype; /* type/unit of element #2 */ ++ int ce_fdstunit; ++ int ce_sdsttype; /* type/unit of element #3 */ ++ int ce_sdstunit; ++ int ce_flags; ++}; ++#define CE_INVERT1 1 ++#define CE_INVERT2 2 ++ ++ ++/* ++ * CHIOPOSITION ++ * move the transport element (robot arm) to a specific element. ++ */ ++struct changer_position { ++ int cp_type; ++ int cp_unit; ++ int cp_flags; ++}; ++#define CP_INVERT 1 ++ ++ ++/* ++ * CHIOGSTATUS ++ * get element status for all elements of a specific type ++ */ ++struct changer_element_status { ++ int ces_type; ++ unsigned char *ces_data; ++}; ++#define CESTATUS_FULL 0x01 /* full */ ++#define CESTATUS_IMPEXP 0x02 /* media was imported (inserted by sysop) */ ++#define CESTATUS_EXCEPT 0x04 /* error condition */ ++#define CESTATUS_ACCESS 0x08 /* access allowed */ ++#define CESTATUS_EXENAB 0x10 /* element can export media */ ++#define CESTATUS_INENAB 0x20 /* element can import media */ ++ ++ ++/* ++ * CHIOGELEM ++ * get more detailed status informtion for a single element ++ */ ++struct changer_get_element { ++ int cge_type; /* type/unit */ ++ int cge_unit; ++ int cge_status; /* status */ ++ int cge_errno; /* errno */ ++ int cge_srctype; /* source element of the last move/exchange */ ++ int cge_srcunit; ++ int cge_id; /* scsi id (for data transfer elements) */ ++ int cge_lun; /* scsi lun (for data transfer elements) */ ++ char cge_pvoltag[36]; /* primary volume tag */ ++ char cge_avoltag[36]; /* alternate volume tag */ ++ int cge_flags; ++}; ++/* flags */ ++#define CGE_ERRNO 0x01 /* errno available */ ++#define CGE_INVERT 0x02 /* media inverted */ ++#define CGE_SRC 0x04 /* media src available */ ++#define CGE_IDLUN 0x08 /* ID+LUN available */ ++#define CGE_PVOLTAG 0x10 /* primary volume tag available */ ++#define CGE_AVOLTAG 0x20 /* alternate volume tag available */ ++ ++ ++/* ++ * CHIOSVOLTAG ++ * set volume tag ++ */ ++struct changer_set_voltag { ++ int csv_type; /* type/unit */ ++ int csv_unit; ++ char csv_voltag[36]; /* volume tag */ ++ int csv_flags; ++}; ++#define CSV_PVOLTAG 0x01 /* primary volume tag */ ++#define CSV_AVOLTAG 0x02 /* alternate volume tag */ ++#define CSV_CLEARTAG 0x04 /* clear volume tag */ ++ ++/* ioctls */ ++#define CHIOMOVE _IOW('c', 1,struct changer_move) ++#define CHIOEXCHANGE _IOW('c', 2,struct changer_exchange) ++#define CHIOPOSITION _IOW('c', 3,struct changer_position) ++#define CHIOGPICKER _IOR('c', 4,int) /* not impl. */ ++#define CHIOSPICKER _IOW('c', 5,int) /* not impl. */ ++#define CHIOGPARAMS _IOR('c', 6,struct changer_params) ++#define CHIOGSTATUS _IOW('c', 8,struct changer_element_status) ++#define CHIOGELEM _IOW('c',16,struct changer_get_element) ++#define CHIOINITELEM _IO('c',17) ++#define CHIOSVOLTAG _IOW('c',18,struct changer_set_voltag) ++#define CHIOGVPARAMS _IOR('c',19,struct changer_vendor_params) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff -aurN a/include/linux/major.h b/include/linux/major.h +--- a/include/linux/major.h 2005-06-06 11:22:29.000000000 -0400 ++++ b/include/linux/major.h 2005-06-15 22:00:21.000000000 -0400 +@@ -100,6 +100,7 @@ + #define I2O_MAJOR 80 /* 80->87 */ + + #define SHMIQ_MAJOR 85 /* Linux/mips, SGI /dev/shmiq */ ++#define SCSI_CHANGER_MAJOR 86 + + #define IDE6_MAJOR 88 + #define IDE7_MAJOR 89 +diff -aurN a/include/scsi/scsi.h b/include/scsi/scsi.h +--- a/include/scsi/scsi.h 2005-06-06 11:22:29.000000000 -0400 ++++ b/include/scsi/scsi.h 2005-06-15 22:00:21.000000000 -0400 +@@ -41,6 +41,7 @@ + #define FORMAT_UNIT 0x04 + #define READ_BLOCK_LIMITS 0x05 + #define REASSIGN_BLOCKS 0x07 ++#define INITIALIZE_ELEMENT_STATUS 0x07 + #define READ_6 0x08 + #define WRITE_6 0x0a + #define SEEK_6 0x0b +@@ -65,6 +66,7 @@ + #define READ_10 0x28 + #define WRITE_10 0x2a + #define SEEK_10 0x2b ++#define POSITION_TO_ELEMENT 0x2b + #define WRITE_VERIFY 0x2e + #define VERIFY 0x2f + #define SEARCH_HIGH 0x30 +@@ -97,6 +99,7 @@ + #define PERSISTENT_RESERVE_OUT 0x5f + #define REPORT_LUNS 0xa0 + #define MOVE_MEDIUM 0xa5 ++#define EXCHANGE_MEDIUM 0xa6 + #define READ_12 0xa8 + #define WRITE_12 0xaa + #define WRITE_VERIFY_12 0xae diff --git a/debian/patches-debian/drivers-ide-__devinit.patch b/debian/patches-debian/drivers-ide-__devinit.patch new file mode 100644 index 000000000..085cf80da --- /dev/null +++ b/debian/patches-debian/drivers-ide-__devinit.patch @@ -0,0 +1,339 @@ +diff -aurN a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c +--- a/drivers/ide/pci/alim15x3.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/alim15x3.c 2005-06-15 22:01:23.000000000 -0400 +@@ -583,7 +583,7 @@ + * appropriate also sets up the 1533 southbridge. + */ + +-static unsigned int __init init_chipset_ali15x3 (struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name) + { + unsigned long flags; + u8 tmpbyte; +@@ -677,7 +677,7 @@ + * FIXME: frobs bits that are not defined on newer ALi devicea + */ + +-static unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) ++static unsigned int __devinit ata66_ali15x3 (ide_hwif_t *hwif) + { + struct pci_dev *dev = hwif->pci_dev; + unsigned int ata66 = 0; +@@ -748,7 +748,7 @@ + * Initialize the IDE structure side of the ALi 15x3 driver. + */ + +-static void __init init_hwif_common_ali15x3 (ide_hwif_t *hwif) ++static void __devinit init_hwif_common_ali15x3 (ide_hwif_t *hwif) + { + hwif->autodma = 0; + hwif->tuneproc = &ali15x3_tune_drive; +@@ -794,7 +794,7 @@ + * Sparc systems + */ + +-static void __init init_hwif_ali15x3 (ide_hwif_t *hwif) ++static void __devinit init_hwif_ali15x3 (ide_hwif_t *hwif) + { + u8 ideic, inmir; + s8 irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, +@@ -847,7 +847,7 @@ + * the actual work. + */ + +-static void __init init_dma_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) ++static void __devinit init_dma_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) + { + if (m5229_revision < 0x20) + return; +diff -aurN a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c +--- a/drivers/ide/pci/amd74xx.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/amd74xx.c 2005-06-15 22:01:23.000000000 -0400 +@@ -309,7 +309,7 @@ + * and initialize its drive independent registers. + */ + +-static unsigned int __init init_chipset_amd74xx(struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, const char *name) + { + unsigned char t; + unsigned int u; +@@ -413,7 +413,7 @@ + return dev->irq; + } + +-static void __init init_hwif_amd74xx(ide_hwif_t *hwif) ++static void __devinit init_hwif_amd74xx(ide_hwif_t *hwif) + { + int i; + +diff -aurN a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c +--- a/drivers/ide/pci/cmd640.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/cmd640.c 2005-06-15 22:01:23.000000000 -0400 +@@ -278,7 +278,7 @@ + spin_unlock_irqrestore(&ide_lock, flags); + } + +-static int __init match_pci_cmd640_device (void) ++static int __devinit match_pci_cmd640_device (void) + { + const u8 ven_dev[4] = {0x95, 0x10, 0x40, 0x06}; + unsigned int i; +@@ -298,7 +298,7 @@ + /* + * Probe for CMD640x -- pci method 1 + */ +-static int __init probe_for_cmd640_pci1 (void) ++static int __devinit probe_for_cmd640_pci1 (void) + { + __get_cmd640_reg = get_cmd640_reg_pci1; + __put_cmd640_reg = put_cmd640_reg_pci1; +@@ -314,7 +314,7 @@ + /* + * Probe for CMD640x -- pci method 2 + */ +-static int __init probe_for_cmd640_pci2 (void) ++static int __devinit probe_for_cmd640_pci2 (void) + { + __get_cmd640_reg = get_cmd640_reg_pci2; + __put_cmd640_reg = put_cmd640_reg_pci2; +@@ -328,7 +328,7 @@ + /* + * Probe for CMD640x -- vlb + */ +-static int __init probe_for_cmd640_vlb (void) ++static int __devinit probe_for_cmd640_vlb (void) + { + u8 b; + +@@ -349,7 +349,7 @@ + * Returns 1 if an IDE interface/drive exists at 0x170, + * Returns 0 otherwise. + */ +-static int __init secondary_port_responding (void) ++static int __devinit secondary_port_responding (void) + { + unsigned long flags; + +@@ -392,7 +392,7 @@ + * Check whether prefetch is on for a drive, + * and initialize the unmask flags for safe operation. + */ +-static void __init check_prefetch (unsigned int index) ++static void __devinit check_prefetch (unsigned int index) + { + ide_drive_t *drive = cmd_drives[index]; + u8 b = get_cmd640_reg(prefetch_regs[index]); +@@ -413,7 +413,7 @@ + /* + * Figure out which devices we control + */ +-static void __init setup_device_ptrs (void) ++static void __devinit setup_device_ptrs (void) + { + unsigned int i; + +@@ -495,7 +495,7 @@ + /* + * This routine retrieves the initial drive timings from the chipset. + */ +-static void __init retrieve_drive_counts (unsigned int index) ++static void __devinit retrieve_drive_counts (unsigned int index) + { + u8 b; + +@@ -716,7 +716,7 @@ + /* + * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c + */ +-int __init ide_probe_for_cmd640x (void) ++int __devinit ide_probe_for_cmd640x (void) + { + #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + int second_port_toggled = 0; +diff -aurN a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c +--- a/drivers/ide/pci/cs5530.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/cs5530.c 2005-06-15 22:01:23.000000000 -0400 +@@ -217,7 +217,7 @@ + * Initialize the cs5530 bridge for reliable IDE DMA operation. + */ + +-static unsigned int __init init_chipset_cs5530 (struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name) + { + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + unsigned long flags; +@@ -308,7 +308,7 @@ + * performs channel-specific pre-initialization before drive probing. + */ + +-static void __init init_hwif_cs5530 (ide_hwif_t *hwif) ++static void __devinit init_hwif_cs5530 (ide_hwif_t *hwif) + { + unsigned long basereg; + u32 d0_timings; +diff -aurN a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c +--- a/drivers/ide/pci/cy82c693.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/cy82c693.c 2005-06-15 22:01:23.000000000 -0400 +@@ -391,7 +391,7 @@ + /* + * this function is called during init and is used to setup the cy82c693 chip + */ +-static unsigned int __init init_chipset_cy82c693(struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name) + { + if (PCI_FUNC(dev->devfn) != 1) + return 0; +@@ -443,7 +443,7 @@ + /* + * the init function - called for each ide channel once + */ +-static void __init init_hwif_cy82c693(ide_hwif_t *hwif) ++static void __devinit init_hwif_cy82c693(ide_hwif_t *hwif) + { + hwif->autodma = 0; + +@@ -467,9 +467,9 @@ + hwif->drives[1].autodma = hwif->autodma; + } + +-static __initdata ide_hwif_t *primary; ++static __devinitdata ide_hwif_t *primary; + +-void __init init_iops_cy82c693(ide_hwif_t *hwif) ++void __devinit init_iops_cy82c693(ide_hwif_t *hwif) + { + if (PCI_FUNC(hwif->pci_dev->devfn) == 1) + primary = hwif; +diff -aurN a/drivers/ide/pci/it8172.c b/drivers/ide/pci/it8172.c +--- a/drivers/ide/pci/it8172.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/it8172.c 2005-06-15 22:01:23.000000000 -0400 +@@ -216,7 +216,7 @@ + return 0; + } + +-static unsigned int __init init_chipset_it8172 (struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_it8172 (struct pci_dev *dev, const char *name) + { + unsigned char progif; + +@@ -230,7 +230,7 @@ + } + + +-static void __init init_hwif_it8172 (ide_hwif_t *hwif) ++static void __devinit init_hwif_it8172 (ide_hwif_t *hwif) + { + struct pci_dev* dev = hwif->pci_dev; + unsigned long cmdBase, ctrlBase; +diff -aurN a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c +--- a/drivers/ide/pci/ns87415.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/ns87415.c 2005-06-15 22:01:23.000000000 -0400 +@@ -195,7 +195,7 @@ + return __ide_dma_check(drive); + } + +-static void __init init_hwif_ns87415 (ide_hwif_t *hwif) ++static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) + { + struct pci_dev *dev = hwif->pci_dev; + unsigned int ctrl, using_inta; +diff -aurN a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c +--- a/drivers/ide/pci/opti621.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/opti621.c 2005-06-15 22:01:23.000000000 -0400 +@@ -326,7 +326,7 @@ + /* + * init_hwif_opti621() is called once for each hwif found at boot. + */ +-static void __init init_hwif_opti621 (ide_hwif_t *hwif) ++static void __devinit init_hwif_opti621 (ide_hwif_t *hwif) + { + hwif->autodma = 0; + hwif->drives[0].drive_data = PIO_DONT_KNOW; +diff -aurN a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c +--- a/drivers/ide/pci/sc1200.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/sc1200.c 2005-06-15 22:01:23.000000000 -0400 +@@ -459,7 +459,7 @@ + * This gets invoked by the IDE driver once for each channel, + * and performs channel-specific pre-initialization before drive probing. + */ +-static void __init init_hwif_sc1200 (ide_hwif_t *hwif) ++static void __devinit init_hwif_sc1200 (ide_hwif_t *hwif) + { + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; +diff -aurN a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c +--- a/drivers/ide/pci/sl82c105.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/sl82c105.c 2005-06-15 22:01:23.000000000 -0400 +@@ -386,7 +386,7 @@ + * channel 0 here at least, but channel 1 has to be enabled by + * firmware or arch code. We still set both to 16 bits mode. + */ +-static unsigned int __init init_chipset_sl82c105(struct pci_dev *dev, const char *msg) ++static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg) + { + u32 val; + +@@ -399,7 +399,7 @@ + return dev->irq; + } + +-static void __init init_dma_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) ++static void __devinit init_dma_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) + { + unsigned int rev; + u8 dma_state; +@@ -431,7 +431,7 @@ + * Initialise the chip + */ + +-static void __init init_hwif_sl82c105(ide_hwif_t *hwif) ++static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) + { + struct pci_dev *dev = hwif->pci_dev; + u32 val; +diff -aurN a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c +--- a/drivers/ide/pci/slc90e66.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/slc90e66.c 2005-06-15 22:01:23.000000000 -0400 +@@ -196,7 +196,7 @@ + } + #endif /* CONFIG_BLK_DEV_IDEDMA */ + +-static void __init init_hwif_slc90e66 (ide_hwif_t *hwif) ++static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif) + { + u8 reg47 = 0; + u8 mask = hwif->channel ? 0x01 : 0x02; /* bit0:Primary */ +diff -aurN a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c +--- a/drivers/ide/pci/triflex.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/triflex.c 2005-06-15 22:01:23.000000000 -0400 +@@ -130,7 +130,7 @@ + return hwif->ide_dma_off_quietly(drive); + } + +-static void __init init_hwif_triflex(ide_hwif_t *hwif) ++static void __devinit init_hwif_triflex(ide_hwif_t *hwif) + { + hwif->tuneproc = &triflex_tune_drive; + hwif->speedproc = &triflex_tune_chipset; +diff -aurN a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c +--- a/drivers/ide/pci/via82cxxx.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/pci/via82cxxx.c 2005-06-15 22:01:23.000000000 -0400 +@@ -415,7 +415,7 @@ + * and initialize its drive independent registers. + */ + +-static unsigned int __init init_chipset_via82cxxx(struct pci_dev *dev, const char *name) ++static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name) + { + struct pci_dev *isa = NULL; + u8 t, v; +@@ -576,7 +576,7 @@ + return 0; + } + +-static void __init init_hwif_via82cxxx(ide_hwif_t *hwif) ++static void __devinit init_hwif_via82cxxx(ide_hwif_t *hwif) + { + int i; + diff --git a/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch b/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch new file mode 100644 index 000000000..465adbb8a --- /dev/null +++ b/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch @@ -0,0 +1,21 @@ +#! /bin/sh -e +## .dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: blacklist "TOSHIBA CD-ROM XM-1702BC" in drivers/ide/ide-dma.c +## DP: Patch author: unknown +## DP: Upstream status: not submitted + +. $(dirname $0)/DPATCH + +@DPATCH@ +--- kernel-source-2.6.6/drivers/ide/ide-dma.c 2004-04-05 19:49:28.000000000 +1000 ++++ kernel-source-2.6.6-1/drivers/ide/ide-dma.c 2004-04-05 20:54:34.000000000 +1000 +@@ -125,6 +125,7 @@ + { "HITACHI CDR-8335" , "ALL" }, + { "HITACHI CDR-8435" , "ALL" }, + { "Toshiba CD-ROM XM-6202B" , "ALL" }, ++ { "TOSHIBA CD-ROM XM-1702BC" , "ALL" }, + { "CD-532E-A" , "ALL" }, + { "E-IDE CD-ROM CR-840", "ALL" }, + { "CD-ROM Drive/F5A", "ALL" }, diff --git a/debian/patches-debian/drivers-scsi-megaraid_splitup.patch b/debian/patches-debian/drivers-scsi-megaraid_splitup.patch new file mode 100644 index 000000000..60af03e21 --- /dev/null +++ b/debian/patches-debian/drivers-scsi-megaraid_splitup.patch @@ -0,0 +1,110 @@ +# From Fabio M. Di Nitto +# Date 27th May 2005 +# Source Ubuntu kernel-team baz-archive +# http://people.u.c/~lamont/Archives/kernel-team@ubuntu.com--2005/ +# kernel-debian--pre1,2--2.6.11.93 +# +# * The megaraid legacy driver is around only to support AMI megaraid 1 and 2. +# All the other controllers are supported (according to the code) by the +# new megaraid driver: +# - Add patch drivers-scsi-megaraid_spiltup.dpatch: +# . Split PCI ID's properly between the 2 drivers. +# . Allow compilation of both drivers at the same time. +# . Update Kconfig.megaraid to reflect the new changes in the help. +# . Rename a few things in the old megaraid driver to avoid possible +# conflicts with the new drivers (NOTE: there might be more that needs +# to be changed given that now the 2 modules can be loaded at the same +# time). + +diff -urNad linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid/Kconfig.megaraid /usr/src/dpatchtemp/dpep.eRg2wK/linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid/Kconfig.megaraid +--- linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid/Kconfig.megaraid 2005-03-02 08:37:49.000000000 +0100 ++++ /usr/src/dpatchtemp/dpep.eRg2wK/linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid/Kconfig.megaraid 2005-04-19 08:28:09.521239400 +0200 +@@ -64,15 +64,11 @@ + To compile this driver as a module, choose M here: the + module will be called megaraid_mbox + +-if MEGARAID_NEWGEN=n + config MEGARAID_LEGACY + tristate "LSI Logic Legacy MegaRAID Driver" + depends on PCI && SCSI + help +- This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490 +- and 467 SCSI host adapters. This driver also support the all U320 +- RAID controllers ++ This driver supports the AMI MEGARAID 1 and 2. + + To compile this driver as a module, choose M here: the + module will be called megaraid +-endif +diff -urNad linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid.c /usr/src/dpatchtemp/dpep.eRg2wK/linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid.c +--- linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid.c 2005-03-02 08:37:30.000000000 +0100 ++++ /usr/src/dpatchtemp/dpep.eRg2wK/linux-source-2.6.12-2.6.11.90/drivers/scsi/megaraid.c 2005-04-19 08:35:13.889725584 +0200 +@@ -4565,7 +4565,7 @@ + }; + + static int __devinit +-megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ++megaraid_legacy_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) + { + struct Scsi_Host *host; + adapter_t *adapter; +@@ -5033,37 +5033,25 @@ + } + + static struct pci_device_id megaraid_pci_tbl[] = { +- {PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DISCOVERY, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +- {PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_PERC4_DI, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, BOARD_64BIT}, +- {PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_PERC4_QC_VERDE, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, BOARD_64BIT}, + {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +- {PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID3, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +- {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_AMI_MEGARAID3, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +- {PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_AMI_MEGARAID3, +- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} + }; + MODULE_DEVICE_TABLE(pci, megaraid_pci_tbl); + + static struct pci_driver megaraid_pci_driver = { +- .name = "megaraid", ++ .name = "megaraidlegacy", + .id_table = megaraid_pci_tbl, +- .probe = megaraid_probe_one, ++ .probe = megaraid_legacy_probe_one, + .remove = __devexit_p(megaraid_remove_one), + .driver = { + .shutdown = megaraid_shutdown, + }, + }; + +-static int __init megaraid_init(void) ++static int __init megaraid_legacy_init(void) + { + int error; + +@@ -5102,7 +5090,7 @@ + return 0; + } + +-static void __exit megaraid_exit(void) ++static void __exit megaraid_legacy_exit(void) + { + /* + * Unregister the character device interface to the driver. +@@ -5116,7 +5104,7 @@ + #endif + } + +-module_init(megaraid_init); +-module_exit(megaraid_exit); ++module_init(megaraid_legacy_init); ++module_exit(megaraid_legacy_exit); + + /* vi: set ts=8 sw=8 tw=78: */ diff --git a/debian/patches-debian/fbdev-radeon-noaccel.patch b/debian/patches-debian/fbdev-radeon-noaccel.patch new file mode 100644 index 000000000..85950b0d8 --- /dev/null +++ b/debian/patches-debian/fbdev-radeon-noaccel.patch @@ -0,0 +1,17 @@ +diff -aurN a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c +--- a/drivers/video/aty/radeon_base.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/video/aty/radeon_base.c 2005-06-15 21:55:56.000000000 -0400 +@@ -1907,8 +1907,12 @@ + + fb_alloc_cmap(&info->cmap, 256, 0); + +- if (noaccel) ++ if (noaccel) { + info->flags |= FBINFO_HWACCEL_DISABLED; ++ info->fix.accel = FB_ACCEL_NONE; ++ } else { ++ info->fix.accel = FB_ACCEL_ATI_RADEON; ++ } + + return 0; + } diff --git a/debian/patches-debian/fs-asfs-2.patch b/debian/patches-debian/fs-asfs-2.patch new file mode 100644 index 000000000..e3d37697f --- /dev/null +++ b/debian/patches-debian/fs-asfs-2.patch @@ -0,0 +1,5262 @@ +## fs-asfs.dpatch by Sven Luther +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: add support for the Amiga SmartFilesystem (asfs), 1.0beta9 +## DP: Patch author: Marek Szyprowski +## DP: Upstream status: submitted but no reply. Submitted again on 2005.03.22. +## DP: Reference: http://home.elka.pw.edu.pl/~mszyprow/programy/asfs/ + +diff -aurN a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX +--- a/Documentation/filesystems/00-INDEX 2005-06-06 11:22:29.000000000 -0400 ++++ b/Documentation/filesystems/00-INDEX 2005-06-15 22:05:09.000000000 -0400 +@@ -6,6 +6,8 @@ + - info and mount options for the Acorn Advanced Disc Filing System. + affs.txt + - info and mount options for the Amiga Fast File System. ++asfs.txt ++ - info and mount options for the Amiga Smart File System. + bfs.txt + - info for the SCO UnixWare Boot Filesystem (BFS). + cifs.txt +diff -aurN a/Documentation/filesystems/asfs.txt b/Documentation/filesystems/asfs.txt +--- a/Documentation/filesystems/asfs.txt 1969-12-31 19:00:00.000000000 -0500 ++++ b/Documentation/filesystems/asfs.txt 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,161 @@ ++ ++Amiga SmartFileSystem, Linux implementation ++=========================================== ++ ++ASFS is a Amiga Smart FileSystem driver for Linux. It supports reading ++files and directories. From version 1.0 there is also an experimental ++(almost full) write support. Experimental means that it hasn't been ++tested enough yet, so use it with care. Symbolic links (in AmigaOS ++called soft links) are also supported read/write. Read notes below ++about symlinks support. ++ ++ ++Unsupported features of Amiga SFS ++================================ ++ ++ASFS currently does not support safe-delete feature of Amiga SFS ++filesystem. It simply deletes files instead of moving them to ++".recycled" directory. It also doesn't remove files from ".recycled" ++directory, when there is no space left on drive. ++ ++If there is no space left, you need to manually remove files from ++".recycled" directory. Also if you want to delete a file in a safe ++way, you need to move it to ".recycled" directory by hand. ++ ++Because of all of above, the amount of free space on disk does not ++include space used by all files from ".recycled" directory. ++ ++ ++Limitations ++=========== ++ ++There is no Amiga protection bits into Linux permission bits tranlation ++and vice versa. If you need this feature, mail me. ++ ++ASFS will always keep some amount of blocks free. This means that you ++cannot fill the drive completely. It is because Amiga SFS uses some ++special methods of writing data (called safe write), which needs some ++additional free space. ++ ++File systems with unfinished transactions (this happens when system crashed ++during writing data to disk on AmigaOS/MorphOS) will be mounted read-only ++to protect data. The only way to fix such filesystem is to mount it under ++AmigaOS or MorphOS. ++ ++Do not try to mount and write to filesystem with errors. Bad things will ++happen. ++ ++ ++Mount options for the ASFS ++========================== ++ ++setuid=uid ++ This sets the owner of all files and directories in the file ++ system to uid. ++ ++setgid=gid ++ Same as above, but for gid. ++ ++mode=mode ++ Sets the mode flags to the given (octal) value. Directories ++ will get an x permission if the corresponding r bit is set. ++ The default mode is 0644, which means that everybody are allowed ++ to read files, but only root can write to them. ++ (for directories this means also that search bits are set). ++ ++prefix=path ++ Path will be prefixed to every absolute path name of symbolic ++ links on an ASFS/AFFS partition. Default = "/". (See below.) ++ ++volume=name ++ When symbolic links with an absolute path are created ++ on an ASFS/AFFS partition, name will be prepended as the ++ volume name. Default = "" (empty string). (See below.) ++ ++lowercasevol ++ Translate all volume names in symlinks to lower case. ++ Disabled by default. (See below.) ++ ++iocharset=name ++ Character set to use for converting file names. Specifies ++ character set used by your Linux system. ++codepage=name ++ Set the codepage number for converting file names. Specifies ++ character set used by your Amiga. Use full name (for example ++ 'cp1251' instead of '1251') here, this allows to specify any ++ character set, not only numbered one (like 'iso8859-2'). ++ Use special name 'none' to disable the NLS file name ++ translation. ++ ++Symbolic links ++============== ++ ++Although the Amiga and Linux file systems resemble each other, there ++are some, not always subtle, differences. One of them becomes apparent ++with symbolic links. While Linux has a file system with exactly one ++root directory, the Amiga has a separate root directory for each ++file system (for example, partition, floppy disk, ...). With the Amiga, ++these entities are called "volumes". They have symbolic names which ++can be used to access them. Thus, symbolic links can point to a ++different volume. ASFS turns the volume name into a directory name ++and prepends the prefix path (see prefix option) to it. When option ++"lowercasevol" is set, it also translates volume names to lower case. ++If the volume name is the same as a name given in "volume" option, ++it will be ignored and an absolute path will be created. ++ ++Example: ++You mount all your Amiga partitions under /amiga/ (where ++ is the name of the volume), and you give options ++`prefix="/amiga/",volume="Linux",lowercasevol' when mounting all your ++ASFS partitions. (They might be "User", "WB" and "Graphics", the mount ++points /amiga/user, /amiga/wb and /amiga/graphics). ++ ++A symbolic link referring to "USER:sc/include/dos/dos.h" will be ++translated to "/amiga/user/sc/include/dos/dos.h". ++A symbolic link referring to "Linux:etc/fstab" will be translated to ++"/etc/fstab". ++If you create a symlink referring to "/amiga/graphics/data/pict.jpg", ++it will be saved as "graphics:data/pict.jpg". ++If you create a symlink referring to "/boot/System.map", it will be ++saved as "Linux:boot/System.map". ++ ++ ++Other information ++================= ++ ++Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks ++speed up almost everything at the expense of wasted disk space. The speed ++gain above 4K seems not really worth the price, so you don't lose too ++much here, either. ++ ++This file system has been tested on Motorola PPC and 68k, as well as ++Intel x86 systems. I don't know, if it works on other Linux systems. ++ ++This filesystem is in BETA STAGE. This means that driver MIGHT corrupt ++or damage data on your disk. Remember! YOU USE IT ON YOUR OWN RISK! ++ ++I made almost all I could to minimalize this risk. On my systems several ++gigabytes has been succesfully copied from and to SFS disks. I would also ++appreciate any infomation if this filesystem works on your system or not. ++See next paragraph for my email. ++ ++Some parts of this documentation has been adapted from AFFS driver docs. ++ ++ ++Author, contact and copyright infos ++=================================== ++ ++ASFS has been written by Marek 'March' Szyprowski . ++Mail me if you have any suggestions or found a bug. ++ ++Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski ++ ++Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts ++of original amiga version of SmartFilesystem source code. ++ ++SmartFilesystem is copyrighted (C) 2003,2004 by: John Hendrikx, ++Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek ++ ++The ASFS driver is realased under the terms of of the GNU General ++Public License. See source code for more details. ++ +diff -aurN a/fs/Kconfig b/fs/Kconfig +--- a/fs/Kconfig 2005-06-06 11:22:29.000000000 -0400 ++++ b/fs/Kconfig 2005-06-15 22:05:09.000000000 -0400 +@@ -921,6 +921,53 @@ + To compile this file system support as a module, choose M here: the + module will be called affs. If unsure, say N. + ++config ASFS_FS ++ tristate "Amiga SFS file system support (EXPERIMENTAL)" ++ select NLS ++ depends on EXPERIMENTAL ++ help ++ ++ The Amiga Smart FileSystem (SFS) is the file system used on hard ++ disks by Amiga(tm) and MorphOS(tm) systems. Say Y if you want ++ to be able to read files from an Amiga SFS partition on your hard ++ drive. ++ ++ For more information read ++ ++ To compile this file system support as a module, choose M here: the ++ module will be called asfs. ++ ++ If unsure, say N. ++ ++config ASFS_DEFAULT_CODEPAGE ++ string "Default codepage for SFS" ++ depends on ASFS_FS ++ default "" ++ help ++ This option should be set to the codepage of your SFS filesystems. ++ It can be overridden with the 'codepage' mount option. Leave it blank ++ or enter 'none' to disable filename converting. ++ ++ Use full codepage name (for example 'cp1251' instead of '1251') here, ++ this allows to specify any character set, not only numbered one (like ++ 'iso8859-2'). ++ ++ If unsure, leave it blank. ++ ++config ASFS_RW ++ bool "Amiga SFS write support (DANGEROUS)" ++ depends on ASFS_FS ++ help ++ ++ If you say Y here, you will be able to write to ASFS file ++ systems as well as read from them. The read-write support in ASFS ++ is in beta stage. This means that useing it to write files to SFS ++ partitions is DANGEROUS and COULD corrupt the filesystem. ++ ++ For more information read ++ ++ If unsure, say N. ++ + config HFS_FS + tristate "Apple Macintosh file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL +diff -aurN a/fs/Makefile b/fs/Makefile +--- a/fs/Makefile 2005-06-06 11:22:29.000000000 -0400 ++++ b/fs/Makefile 2005-06-15 22:05:09.000000000 -0400 +@@ -81,6 +81,7 @@ + obj-$(CONFIG_JFFS_FS) += jffs/ + obj-$(CONFIG_JFFS2_FS) += jffs2/ + obj-$(CONFIG_AFFS_FS) += affs/ ++obj-$(CONFIG_ASFS_FS) += asfs/ + obj-$(CONFIG_ROMFS_FS) += romfs/ + obj-$(CONFIG_QNX4FS_FS) += qnx4/ + obj-$(CONFIG_AUTOFS_FS) += autofs/ +diff -aurN a/fs/asfs/Changes b/fs/asfs/Changes +--- a/fs/asfs/Changes 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/Changes 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,108 @@ ++ ++Amiga Smart File System, Linux implementation ++ ++Please direct bug reports to: marek@amiga.pl ++ ++History: ++ ++v1.0beta9 (17.03.2005) ++- added NLS support (thanks to Pavel Fedin!) ++ ++v1.0beta8 (07.01.2005) ++- adapted to 2.6.10 kenrel VFS changes ++- added workaround for buggy Mandrake kernel headers ++ ++v1.0beta7 (25.06.2004) ++- small changes in documentation ++- code clean up: bitfuncs.c, super.c, inode.c, *.h, Makefile, added ++ asfs_ prefix to function names, made some functions static ++ (big thanks to Christoph Hellwig for advice!) ++- fixed minor bugs (inode leak in super.c, not-realesed buffer during ++ object renaming in inode.c) ++- now files/dirs are created with global ownership/permission bits ++ ++v1.0beta6 (04.06.2004) ++- fixed: ASFS_SB(sb)->flags was always zero in 2.6.x code ++ ++v1.0beta5 (07.05.2004) ++- finally fixed a problem with file size attrib. not being written ++ to disk ++- fixed some problems with GCC 3.x and debug enabled ++ ++v1.0beta4 (12.04.2004) ++- removed dummy asfs_notify_change (this fixes major bug introduced ++ in 1.0beta3 - file size wasn't written to disk) until it will ++ be implemented completely ++ ++v1.0beta3 (22.03.2004) - still beta ++- updated for 2.6.x kernels VFS changes ++- code clean-up ++- added dummy asfs_notify_change (chmod now returns no errors) ++- added symlinks write support ++- fixed: ASFS_SB(sb)->flags was always zero ++ ++v1.0beta2 (11.01.2004) - special version for Pegasos][ kernel ++- separated read and write functions, can be compiled also ++ as read-only fs ++ ++v1.0beta1 (02.12.2003) - first public beta with write support ++- added dentry hashing/comparing routines ++- code clean-up ++ ++v1.0aplha4 (30.11.2003) - preparing for first public beta ++- fixed some problems with renaming/moving files ++- fixed two major bugs, which didn't occur when fs was mounted ++ on loopback device (newly allocated blocks were not written to ++ disk and state bits were not set correctly on newly mapped file ++ blocks) ++- fixed many small bugs in io code (some buffers were not freed) ++- added/modified sb locks in asfs_lookup and asfs_getblock ++- fixed serious bug in file block allocation routines ++ ++v1.0aplha3 (23.11.2003) ++- added (hopefully) all byteswap code, should now work again on ++ little-endian systems (also with write support!) ++- updated documentation ++ ++v1.0alpha2 (13.11.2003) ++- now alocates file blocks in chunks during one request ++- fixed some dead-locks, other fixes ++ ++v1.0alpha (02.11.2003) - first working version with full write support ++- too much to list it here ;) ++ ++... (working on write support) ++ ++v0.7 (12.10.2003) - internal realase ++- added asfs_breadcheck, modified asfs_get_node, asfs_search_BTree, ++ no more from_be32/16 macros, other... ++- code splitted into several files ++ ++v0.6 (04.09.2003) - final read-only version ++- added support for HashTables, directory scaning should be ++ MUCH faster now ++- added checking of block IDs before reading any data from block ++ ++v0.5 (19.07.2003) ++- added simple but effective extent cache - real speed-up ++ in reading large files ++- added read support for symlinks - based on AFFS symlinks ++ ++v0.4 (10.07.2003) ++- third code clean-up (thanks to Roman Zippel for advice) ++- now uses generic readpage and readinode routines ++ ++v0.3beta (17.06.2003) ++- second code clean-up ++ ++v0.2beta2 (15.06.2003) ++- fixed yet another stupid bug - driver can't read root block on little-endian systems ++v0.2beta (15.06.2003) ++- fixed stupid bug - now files have 'file' flag (S_IFREG) set... ++- added mount options to set uid, gid and mode of files and dirs ++- made hidden files & dirs really hidden (= not listed in directories) ++- code clean-up ++ ++v0.1beta (11.06.2003) ++- after many kernel crashes, finally got it! ++- first working read-only filesystem driver +diff -aurN a/fs/asfs/Makefile b/fs/asfs/Makefile +--- a/fs/asfs/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/Makefile 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,8 @@ ++# ++# Makefile for the linux asfs filesystem routines. ++# ++ ++obj-$(CONFIG_ASFS_FS) += asfs.o ++ ++asfs-y += dir.o extents.o file.o inode.o namei.o nodes.o objects.o super.o symlink.o ++asfs-$(CONFIG_ASFS_RW) += adminspace.o bitfuncs.o +diff -aurN a/fs/asfs/adminspace.c b/fs/asfs/adminspace.c +--- a/fs/asfs/adminspace.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/adminspace.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,446 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * This file contains some parts of the original amiga version of ++ * SmartFilesystem source code. ++ * ++ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, ++ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek ++ * ++ * Adapted and modified by Marek 'March' Szyprowski ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++#include "bitfuncs.h" ++ ++#include ++ ++#ifdef CONFIG_ASFS_RW ++ ++static int setfreeblocks(struct super_block *sb, u32 freeblocks) ++{ ++ struct buffer_head *bh; ++ if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { ++ struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); ++ ASFS_SB(sb)->freeblocks = freeblocks; ++ ri->freeblocks = cpu_to_be32(freeblocks); ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ return 0; ++ } ++ return -EIO; ++} ++ ++static inline int enoughspace(struct super_block *sb, u32 blocks) ++{ ++ if (ASFS_SB(sb)->freeblocks - ASFS_ALWAYSFREE < blocks) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++ /* Determines the amount of free blocks starting from block /block/. ++ If there are no blocks found or if there was an error -1 is returned, ++ otherwise this function will count the number of free blocks until ++ an allocated block is encountered or until maxneeded has been ++ exceeded. */ ++ ++static int availablespace(struct super_block *sb, u32 block, u32 maxneeded) ++{ ++ struct buffer_head *bh = NULL; ++ struct fsBitmap *b; ++ u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5; ++ u32 maxbitmapblock = ASFS_SB(sb)->bitmapbase + ASFS_SB(sb)->blocks_bitmap; ++ int blocksfound = 0; ++ u32 bitstart; ++ int bitend; ++ u32 nextblock = ASFS_SB(sb)->bitmapbase + block / ASFS_SB(sb)->blocks_inbitmap; ++ ++ bitstart = block % ASFS_SB(sb)->blocks_inbitmap; ++ ++ while (nextblock < maxbitmapblock && (bh = asfs_breadcheck(sb, nextblock++, ASFS_BITMAP_ID))) { ++ b = (void *) bh->b_data; ++ ++ if ((bitend = bmffz(b->bitmap, longs, bitstart)) >= 0) { ++ blocksfound += bitend - bitstart; ++ asfs_brelse(bh); ++ return blocksfound; ++ } ++ blocksfound += ASFS_SB(sb)->blocks_inbitmap - bitstart; ++ if (blocksfound >= maxneeded) { ++ asfs_brelse(bh); ++ return blocksfound; ++ } ++ bitstart = 0; ++ asfs_brelse(bh); ++ } ++ ++ if (bh == NULL) ++ return (-1); ++ ++ return (blocksfound); ++} ++ ++int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, u32 * returned_block, u32 * returned_blocks) ++{ ++ struct buffer_head *bh; ++ u32 longs = ASFS_SB(sb)->blocks_inbitmap >> 5; ++ u32 space = 0; ++ u32 block; ++ u32 bitmapblock = ASFS_SB(sb)->bitmapbase + start / ASFS_SB(sb)->blocks_inbitmap; ++ u32 breakpoint; ++ int bitstart, bitend; ++ int reads; ++ ++ if (enoughspace(sb, maxneeded) == FALSE) { ++ *returned_block = 0; ++ *returned_blocks = 0; ++ return -ENOSPC; ++ } ++ ++ if (start >= ASFS_SB(sb)->totalblocks) ++ start -= ASFS_SB(sb)->totalblocks; ++ ++ if (end == 0) ++ end = ASFS_SB(sb)->totalblocks; ++ ++ reads = ((end - 1) / ASFS_SB(sb)->blocks_inbitmap) + 1 - start / ASFS_SB(sb)->blocks_inbitmap; ++ ++ if (start >= end) ++ reads += (ASFS_SB(sb)->totalblocks - 1) / ASFS_SB(sb)->blocks_inbitmap + 1; ++ ++ breakpoint = (start < end ? end : ASFS_SB(sb)->totalblocks); ++ ++ *returned_block = 0; ++ *returned_blocks = 0; ++ ++ bitend = start % ASFS_SB(sb)->blocks_inbitmap; ++ block = start - bitend; ++ ++ while ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { ++ struct fsBitmap *b = (void *) bh->b_data; ++ u32 localbreakpoint = breakpoint - block; ++ ++ if (localbreakpoint > ASFS_SB(sb)->blocks_inbitmap) ++ localbreakpoint = ASFS_SB(sb)->blocks_inbitmap; ++ ++ /* At this point space contains the amount of free blocks at ++ the end of the previous bitmap block. If there are no ++ free blocks at the start of this bitmap block, space will ++ be set to zero, since in that case the space isn't adjacent. */ ++ ++ while ((bitstart = bmffo(b->bitmap, longs, bitend)) < ASFS_SB(sb)->blocks_inbitmap) { ++ /* found the start of an empty space, now find out how large it is */ ++ ++ if (bitstart >= localbreakpoint) ++ break; ++ ++ if (bitstart != 0) ++ space = 0; ++ ++ bitend = bmffz(b->bitmap, longs, bitstart); ++ ++ if (bitend > localbreakpoint) ++ bitend = localbreakpoint; ++ ++ space += bitend - bitstart; ++ ++ if (*returned_blocks < space) { ++ *returned_block = block + bitend - space; ++ if (space >= maxneeded) { ++ *returned_blocks = maxneeded; ++ asfs_brelse(bh); ++ return 0; ++ } ++ *returned_blocks = space; ++ } ++ ++ if (bitend >= localbreakpoint) ++ break; ++ } ++ ++ if (--reads == 0) ++ break; ++ ++ /* no (more) empty spaces found in this block */ ++ ++ if (bitend != ASFS_SB(sb)->blocks_inbitmap) ++ space = 0; ++ ++ bitend = 0; ++ block += ASFS_SB(sb)->blocks_inbitmap; ++ ++ if (block >= ASFS_SB(sb)->totalblocks) { ++ block = 0; ++ space = 0; ++ breakpoint = end; ++ bitmapblock = ASFS_SB(sb)->bitmapbase; ++ } ++ asfs_brelse(bh); ++ } ++ ++ if (bh == NULL) ++ return -EIO; ++ ++ asfs_brelse(bh); ++ ++ if (*returned_blocks == 0) ++ return -ENOSPC; ++ else ++ return 0; ++} ++ ++int asfs_markspace(struct super_block *sb, u32 block, u32 blocks) ++{ ++ int errorcode; ++ ++ asfs_debug("markspace: Marking %d blocks from block %d\n", blocks, block); ++ ++ if ((availablespace(sb, block, blocks)) < blocks) { ++ printk("ASFS: Attempted to mark %d blocks from block %d, but some of them were already full!\n", blocks, block); ++ return -EIO; ++ } ++ ++ if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks - blocks)) == 0) { ++ struct buffer_head *bh; ++ u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap; ++ u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2; ++ u32 bitmapblock; ++ ++ block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap; ++ bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks; ++ ++ while (blocks > 0) { ++ if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { ++ struct fsBitmap *b = (void *) bh->b_data; ++ ++ blocks -= bmclr(b->bitmap, longs, block, blocks); ++ block = 0; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ } else ++ return -EIO; ++ } ++ } ++ ++ return (errorcode); ++} ++ ++ /* This function checks the bitmap and tries to locate at least /blocksneeded/ ++ adjacent unused blocks. If found it sets returned_block to the start block ++ and returns no error. If not found, ERROR_DISK_IS_FULL is returned and ++ returned_block is set to zero. Any other errors are returned as well. */ ++ ++static inline int internalfindspace(struct super_block *sb, u32 blocksneeded, u32 startblock, u32 endblock, u32 * returned_block) ++{ ++ u32 blocks; ++ int errorcode; ++ ++ if ((errorcode = asfs_findspace(sb, blocksneeded, startblock, endblock, returned_block, &blocks)) == 0) ++ if (blocks != blocksneeded) ++ return -ENOSPC; ++ ++ return errorcode; ++} ++ ++static int findandmarkspace(struct super_block *sb, u32 blocksneeded, u32 * returned_block) ++{ ++ int errorcode; ++ ++ if (enoughspace(sb, blocksneeded) != FALSE) { ++ if ((errorcode = internalfindspace(sb, blocksneeded, 0, ASFS_SB(sb)->totalblocks, returned_block)) == 0) ++ errorcode = asfs_markspace(sb, *returned_block, blocksneeded); ++ } else ++ errorcode = -ENOSPC; ++ ++ return (errorcode); ++} ++ ++/* ************************** */ ++ ++int asfs_freespace(struct super_block *sb, u32 block, u32 blocks) ++{ ++ int errorcode; ++ ++ asfs_debug("freespace: Freeing %d blocks from block %d\n", blocks, block); ++ ++ if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)->freeblocks + blocks)) == 0) { ++ struct buffer_head *bh; ++ u32 skipblocks = block / ASFS_SB(sb)->blocks_inbitmap; ++ u32 longs = (sb->s_blocksize - sizeof(struct fsBitmap)) >> 2; ++ u32 bitmapblock; ++ ++ block -= skipblocks * ASFS_SB(sb)->blocks_inbitmap; ++ bitmapblock = ASFS_SB(sb)->bitmapbase + skipblocks; ++ ++ while (blocks > 0) { ++ if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) { ++ struct fsBitmap *b = (void *) bh->b_data; ++ ++ blocks -= bmset(b->bitmap, longs, block, blocks); ++ block = 0; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ } else ++ return -EIO; ++ } ++ } ++ ++ return (errorcode); ++} ++ ++/*************** admin space containers ****************/ ++ ++int asfs_allocadminspace(struct super_block *sb, u32 *returned_block) ++{ ++ struct buffer_head *bh; ++ u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer; ++ int errorcode = -EIO; ++ ++ asfs_debug("allocadminspace: allocating new block\n"); ++ ++ while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { ++ struct fsAdminSpaceContainer *asc1 = (void *) bh->b_data; ++ struct fsAdminSpace *as1 = asc1->adminspace; ++ int adminspaces1 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); ++ ++ while (adminspaces1-- > 0) { ++ s16 bitoffset; ++ ++ if (as1->space != 0 && (bitoffset = bfffz(be32_to_cpu(as1->bits), 0)) >= 0) { ++ u32 emptyadminblock = be32_to_cpu(as1->space) + bitoffset; ++ as1->bits |= cpu_to_be32(1 << (31 - bitoffset)); ++ asfs_bstore(sb, bh); ++ *returned_block = emptyadminblock; ++ asfs_brelse(bh); ++ asfs_debug("allocadminspace: found block %d\n", *returned_block); ++ return 0; ++ } ++ as1++; ++ } ++ ++ adminspaceblock = be32_to_cpu(asc1->next); ++ asfs_brelse(bh); ++ ++ if (adminspaceblock == 0) { ++ u32 startblock; ++ ++ asfs_debug("allocadminspace: allocating new adminspace area\n"); ++ ++ /* If we get here it means current adminspace areas are all filled. ++ We would now need to find a new area and create a fsAdminSpace ++ structure in one of the AdminSpaceContainer blocks. If these ++ don't have any room left for new adminspace areas a new ++ AdminSpaceContainer would have to be created first which is ++ placed as the first block in the newly found admin area. */ ++ ++ adminspaceblock = ASFS_SB(sb)->adminspacecontainer; ++ ++ if ((errorcode = findandmarkspace(sb, 32, &startblock))) ++ return errorcode; ++ ++ while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { ++ struct fsAdminSpaceContainer *asc2 = (void *) bh->b_data; ++ struct fsAdminSpace *as2 = asc2->adminspace; ++ int adminspaces2 = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); ++ ++ while (adminspaces2-- > 0 && as2->space != 0) ++ as2++; ++ ++ if (adminspaces2 >= 0) { /* Found a unused AdminSpace in this AdminSpaceContainer! */ ++ as2->space = cpu_to_be32(startblock); ++ as2->bits = 0; ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ break; ++ } ++ ++ if (asc2->next == 0) { ++ /* Oh-oh... we marked our new adminspace area in use, but we couldn't ++ find space to store a fsAdminSpace structure in the existing ++ fsAdminSpaceContainer blocks. This means we need to create and ++ link a new fsAdminSpaceContainer as the first block in our newly ++ marked adminspace. */ ++ ++ asc2->next = cpu_to_be32(startblock); ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ /* Now preparing new AdminSpaceContainer */ ++ ++ if ((bh = asfs_getzeroblk(sb, startblock)) == NULL) ++ return -EIO; ++ ++ asc2 = (void *) bh->b_data; ++ asc2->bheader.id = cpu_to_be32(ASFS_ADMINSPACECONTAINER_ID); ++ asc2->bheader.ownblock = cpu_to_be32(startblock); ++ asc2->previous = cpu_to_be32(adminspaceblock); ++ asc2->adminspace[0].space = cpu_to_be32(startblock); ++ asc2->adminspace[0].bits = cpu_to_be32(0x80000000); ++ asc2->bits = 32; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ adminspaceblock = startblock; ++ break; /* Breaks through to outer loop! */ ++ } ++ adminspaceblock = be32_to_cpu(asc2->next); ++ asfs_brelse(bh); ++ } ++ } ++ } ++ return errorcode; ++} ++ ++int asfs_freeadminspace(struct super_block *sb, u32 block) ++{ ++ struct buffer_head *bh; ++ u32 adminspaceblock = ASFS_SB(sb)->adminspacecontainer; ++ ++ asfs_debug("freeadminspace: Entry -- freeing block %d\n", block); ++ ++ while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) { ++ struct fsAdminSpaceContainer *asc = (void *) bh->b_data; ++ struct fsAdminSpace *as = asc->adminspace; ++ int adminspaces = (sb->s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace); ++ ++ while (adminspaces-- > 0) { ++ if (block >= be32_to_cpu(as->space) && block < be32_to_cpu(as->space) + 32) { ++ s16 bitoffset = block - be32_to_cpu(as->space); ++ asfs_debug("freeadminspace: Block to be freed is located in AdminSpaceContainer block at %d\n", adminspaceblock); ++ as->bits &= cpu_to_be32(~(1 << (31 - bitoffset))); ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ return 0; ++ } ++ as++; ++ } ++ ++ if ((adminspaceblock = be32_to_cpu(asc->next)) == 0) ++ break; ++ ++ asfs_brelse(bh); ++ } ++ ++ if (bh != NULL) { ++ asfs_brelse(bh); ++ printk("ASFS: Unable to free an administration block. The block cannot be found."); ++ return -ENOENT; ++ } ++ ++ return -EIO; ++} ++ ++#endif +diff -aurN a/fs/asfs/asfs_fs.h b/fs/asfs/asfs_fs.h +--- a/fs/asfs/asfs_fs.h 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/asfs_fs.h 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,234 @@ ++#ifndef __LINUX_ASFS_FS_H ++#define __LINUX_ASFS_FS_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define asfs_debug(fmt,arg...) /* no debug at all */ ++//#define asfs_debug(fmt,arg...) printk(fmt,##arg) /* general debug infos */ ++ ++#if !defined (__BIG_ENDIAN) && !defined (__LITTLE_ENDIAN) ++#error Endianes must be known for ASFS to work. Sorry. ++#endif ++ ++#define ASFS_MAXFN_BUF (ASFS_MAXFN + 4) ++#define ASFS_DEFAULT_UID 0 ++#define ASFS_DEFAULT_GID 0 ++#define ASFS_DEFAULT_MODE 0644 /* default permission bits for files, dirs have same permission, but with "x" set */ ++ ++/* Extent structure located in RAM (e.g. inside inode structure), ++ currently used to store last used extent */ ++ ++struct inramExtent { ++ u32 startblock; /* Block from begginig of the file */ ++ u32 key; ++ u32 next; ++ u16 blocks; ++}; ++ ++/* inode in-kernel data */ ++ ++struct asfs_inode_info { ++ u32 firstblock; ++ u32 hashtable; ++ int modified; ++ loff_t mmu_private; ++ struct inramExtent ext_cache; ++ struct inode vfs_inode; ++}; ++ ++/* short cut to get to the asfs specific inode data */ ++static inline struct asfs_inode_info *ASFS_I(struct inode *inode) ++{ ++ return list_entry(inode, struct asfs_inode_info, vfs_inode); ++} ++ ++/* Amiga SFS superblock in-core data */ ++ ++struct asfs_sb_info { ++ u32 totalblocks; ++ u32 rootobjectcontainer; ++ u32 extentbnoderoot; ++ u32 objectnoderoot; ++ ++ u32 adminspacecontainer; ++ u32 bitmapbase; ++ u32 freeblocks; ++ u32 blocks_inbitmap; ++ u32 blocks_bitmap; ++ u32 block_rovingblockptr; ++ ++ uid_t uid; ++ gid_t gid; ++ umode_t mode; ++ u16 flags; ++ char *prefix; ++ char *root_volume; /* Volume prefix for absolute symlinks. */ ++ char *iocharset; ++ char *codepage; ++ struct nls_table *nls_io; ++ struct nls_table *nls_disk; ++}; ++ ++/* short cut to get to the asfs specific sb data */ ++static inline struct asfs_sb_info *ASFS_SB(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* io inline code */ ++ ++u32 asfs_calcchecksum(void *block, u32 blocksize); ++ ++static inline int ++asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n, u32 id) ++{ ++ if (asfs_calcchecksum(block, blocksize) == ++ be32_to_cpu(((struct fsBlockHeader *) block)->checksum) && ++ n == be32_to_cpu(((struct fsBlockHeader *) block)->ownblock) && ++ id == be32_to_cpu(((struct fsBlockHeader *) block)->id)) ++ return TRUE; ++ return FALSE; ++} ++ ++/* get fs structure from block and do some checks... */ ++static inline struct buffer_head * ++asfs_breadcheck(struct super_block *sb, u32 n, u32 type) ++{ ++ struct buffer_head *bh; ++ if ((bh = sb_bread(sb, n))) { ++ if (asfs_check_block ((void *)bh->b_data, sb->s_blocksize, n, type)) { ++ return bh; /* all okay */ ++ } ++ brelse(bh); ++ } ++ return NULL; /* error */ ++} ++ ++static inline struct buffer_head * ++asfs_getzeroblk(struct super_block *sb, int block) ++{ ++ struct buffer_head *bh; ++ bh = sb_getblk(sb, block); ++ lock_buffer(bh); ++ memset(bh->b_data, 0, sb->s_blocksize); ++ set_buffer_uptodate(bh); ++ unlock_buffer(bh); ++ return bh; ++} ++ ++static inline void ++asfs_bstore(struct super_block *sb, struct buffer_head *bh) ++{ ++ ((struct fsBlockHeader *) (bh->b_data))->checksum = ++ cpu_to_be32(asfs_calcchecksum(bh->b_data, sb->s_blocksize)); ++ mark_buffer_dirty(bh); ++} ++ ++static inline void asfs_brelse(struct buffer_head *bh) ++{ ++ brelse(bh); ++} ++ ++static inline void dec_count(struct inode *inode) ++{ ++ inode->i_nlink--; ++ mark_inode_dirty(inode); ++} ++ ++/* all prototypes */ ++ ++/* adminspace.c */ ++int asfs_allocadminspace(struct super_block *sb, u32 * block); ++int asfs_freeadminspace(struct super_block *sb, u32 block); ++int asfs_markspace(struct super_block *sb, u32 block, u32 blocks); ++int asfs_freespace(struct super_block *sb, u32 block, u32 blocks); ++int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, ++ u32 * returned_block, u32 * returned_blocks); ++ ++/* dir.c */ ++int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir); ++struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); ++ ++/* extents.c */ ++int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, ++ struct fsExtentBNode **ret_ebn); ++int asfs_deletebnode(struct super_block *sb, struct buffer_head *cb, u32 key); ++int asfs_deleteextents(struct super_block *sb, u32 key); ++int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, ++ u32 objectnode, u32 * io_lastextentbnode); ++ ++/* file.c */ ++int asfs_readpage(struct file *file, struct page *page); ++sector_t asfs_bmap(struct address_space *mapping, sector_t block); ++int asfs_writepage(struct page *page, struct writeback_control *wbc); ++int asfs_prepare_write(struct file *file, struct page *page, unsigned from, ++ unsigned to); ++void asfs_truncate(struct inode *inode); ++int asfs_file_open(struct inode *inode, struct file *filp); ++int asfs_file_release(struct inode *inode, struct file *filp); ++ ++/* inode.c */ ++struct inode *asfs_get_root_inode(struct super_block *sb); ++void asfs_read_locked_inode(struct inode *inode, void *arg); ++int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd); ++int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode); ++int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); ++int asfs_rmdir(struct inode *dir, struct dentry *dentry); ++int asfs_unlink(struct inode *dir, struct dentry *dentry); ++int asfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry); ++int asfs_notify_change(struct dentry *dentry, struct iattr *attr); ++ ++/* namei */ ++u8 asfs_lowerchar(u8 c); ++int asfs_check_name(const u8 *name, int len); ++int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t); ++u16 asfs_hash(u8 *name, int casesensitive); ++void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit); ++ ++/* nodes */ ++int asfs_getnode(struct super_block *sb, u32 nodeno, ++ struct buffer_head **ret_bh, struct fsObjectNode **ret_node); ++int asfs_createnode(struct super_block *sb, struct buffer_head **returned_cb, ++ struct fsNode **returned_node, u32 * returned_nodeno); ++int asfs_deletenode(struct super_block *sb, u32 objectnode); ++ ++/* objects */ ++struct fsObject *asfs_nextobject(struct fsObject *obj); ++struct fsObject *asfs_find_obj_by_name(struct super_block *sb, ++ struct fsObjectContainer *objcont, u8 * name); ++int asfs_readobject(struct super_block *sb, u32 objectnode, ++ struct buffer_head **cb, struct fsObject **returned_object); ++int asfs_createobject(struct super_block *sb, struct buffer_head **io_cb, ++ struct fsObject **io_o, struct fsObject *src_o, ++ u8 * objname, int force); ++int asfs_deleteobject(struct super_block *sb, struct buffer_head *cb, ++ struct fsObject *o); ++int asfs_renameobject(struct super_block *sb, struct buffer_head *cb1, ++ struct fsObject *o1, struct buffer_head *cbparent, ++ struct fsObject *oparent, u8 * newname); ++ ++int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objcb, ++ struct fsObject *o, u32 blocks, u32 * newspace, ++ u32 * addedblocks); ++int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, ++ struct fsObject *o, u32 newsize); ++ ++/* super.c */ ++struct super_block *asfs_read_super(struct super_block *sb, void *data, ++ int silent); ++void asfs_put_super(struct super_block *sb); ++int asfs_statfs(struct super_block *sb, struct kstatfs *buf); ++int asfs_remount(struct super_block *sb, int *flags, char *data); ++struct inode *asfs_alloc_inode(struct super_block *sb); ++void asfs_destroy_inode(struct inode *inode); ++ ++/* symlink.c */ ++int asfs_symlink_readpage(struct file *file, struct page *page); ++int asfs_write_symlink(struct inode *symfile, const char *symname); ++ ++#endif +diff -aurN a/fs/asfs/bitfuncs.c b/fs/asfs/bitfuncs.c +--- a/fs/asfs/bitfuncs.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/bitfuncs.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,171 @@ ++/* ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include "bitfuncs.h" ++ ++/* Bitmap (bm) functions: ++ These functions perform bit-operations on regions of memory which ++ are a multiple of 4 bytes in length. Bitmap is in bigendian byte order. ++*/ ++ ++/* This function finds the first set bit in a region of memory starting ++ with /bitoffset/. The region of memory is /longs/ longs long. It ++ returns the bitoffset of the first set bit it finds. */ ++ ++int bmffo(u32 *bitmap, int longs, int bitoffset) ++{ ++ u32 *scan = bitmap; ++ int longoffset, bit; ++ ++ longoffset = bitoffset >> 5; ++ longs -= longoffset; ++ scan += longoffset; ++ ++ bitoffset = bitoffset & 0x1F; ++ ++ if (bitoffset != 0) { ++ if ((bit = bfffo(be32_to_cpu(*scan), bitoffset)) >= 0) { ++ return (bit + ((scan - bitmap) << 5)); ++ } ++ scan++; ++ longs--; ++ } ++ ++ while (longs-- > 0) { ++ if (*scan++ != 0) { ++ return (bfffo(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5)); ++ } ++ } ++ ++ return (-1); ++} ++ ++/* This function finds the first unset bit in a region of memory starting ++ with /bitoffset/. The region of memory is /longs/ longs long. It ++ returns the bitoffset of the first unset bit it finds. */ ++ ++int bmffz(u32 *bitmap, int longs, int bitoffset) ++{ ++ u32 *scan = bitmap; ++ int longoffset, bit; ++ ++ longoffset = bitoffset >> 5; ++ longs -= longoffset; ++ scan += longoffset; ++ ++ bitoffset = bitoffset & 0x1F; ++ ++ if (bitoffset != 0) { ++ if ((bit = bfffz(be32_to_cpu(*scan), bitoffset)) >= 0) { ++ return (bit + ((scan - bitmap) << 5)); ++ } ++ scan++; ++ longs--; ++ } ++ ++ while (longs-- > 0) { ++ if (*scan++ != 0xFFFFFFFF) { ++ return (bfffz(be32_to_cpu(*--scan), 0) + ((scan - bitmap) << 5)); ++ } ++ } ++ ++ return (-1); ++} ++ ++/* This function clears /bits/ bits in a region of memory starting ++ with /bitoffset/. The region of memory is /longs/ longs long. If ++ the region of memory is too small to clear /bits/ bits then this ++ function exits after having cleared all bits till the end of the ++ memory region. In any case it returns the number of bits which ++ were actually cleared. */ ++ ++int bmclr(u32 *bitmap, int longs, int bitoffset, int bits) ++{ ++ u32 *scan = bitmap; ++ int longoffset; ++ int orgbits = bits; ++ ++ longoffset = bitoffset >> 5; ++ longs -= longoffset; ++ scan += longoffset; ++ ++ bitoffset = bitoffset & 0x1F; ++ ++ if (bitoffset != 0) { ++ if (bits < 32) { ++ *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, bits)); ++ } else { ++ *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, 32)); ++ } ++ scan++; ++ longs--; ++ bits -= 32 - bitoffset; ++ } ++ ++ while (bits > 0 && longs-- > 0) { ++ if (bits > 31) { ++ *scan++ = 0; ++ } else { ++ *scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), 0, bits)); ++ } ++ bits -= 32; ++ } ++ ++ if (bits <= 0) { ++ return (orgbits); ++ } ++ return (orgbits - bits); ++} ++ ++/* This function sets /bits/ bits in a region of memory starting ++ with /bitoffset/. The region of memory is /longs/ longs long. If ++ the region of memory is too small to set /bits/ bits then this ++ function exits after having set all bits till the end of the ++ memory region. In any case it returns the number of bits which ++ were actually set. */ ++ ++int bmset(u32 *bitmap, int longs, int bitoffset, int bits) ++{ ++ u32 *scan = bitmap; ++ int longoffset; ++ int orgbits = bits; ++ ++ longoffset = bitoffset >> 5; ++ longs -= longoffset; ++ scan += longoffset; ++ ++ bitoffset = bitoffset & 0x1F; ++ ++ if (bitoffset != 0) { ++ if (bits < 32) { ++ *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, bits)); ++ } else { ++ *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, 32)); ++ } ++ scan++; ++ longs--; ++ bits -= 32 - bitoffset; ++ } ++ ++ while (bits > 0 && longs-- > 0) { ++ if (bits > 31) { ++ *scan++ = 0xFFFFFFFF; ++ } else { ++ *scan = cpu_to_be32(bfset(be32_to_cpu(*scan), 0, bits)); ++ } ++ bits -= 32; ++ } ++ ++ if (bits <= 0) { ++ return (orgbits); ++ } ++ return (orgbits - bits); ++} +diff -aurN a/fs/asfs/bitfuncs.h b/fs/asfs/bitfuncs.h +--- a/fs/asfs/bitfuncs.h 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/bitfuncs.h 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,59 @@ ++/* ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#ifndef __BITFUNCS_H ++#define __BITFUNCS_H ++ ++#include ++#include ++ ++#include ++#include ++ ++/* Finds first set bit in /data/ starting at /bitoffset/. This function ++ considers the MSB to be the first bit. */ ++static inline int bfffo(u32 data, int bitoffset) ++{ ++ u32 mask = 0xffffffff >> bitoffset; ++ data &= mask; ++ return data == 0 ? -1 : 32-fls(data); ++} ++ ++/* Finds first zero bit in /data/ starting at /bitoffset/. This function ++ considers the MSB to be the first bit. */ ++static inline int bfffz(u32 data, int bitoffset) ++{ ++ return bfffo(~data, bitoffset); ++} ++ ++/* Sets /bits/ bits starting from /bitoffset/ in /data/. ++ /bits/ must be between 1 and 32. */ ++static inline u32 bfset(u32 data, int bitoffset, int bits) ++{ ++ u32 mask = ~((1 << (32 - bits)) - 1); ++ mask >>= bitoffset; ++ return data | mask; ++} ++ ++/* Clears /bits/ bits starting from /bitoffset/ in /data/. ++ /bits/ must be between 1 and 32. */ ++static inline u32 bfclr(u32 data, int bitoffset, int bits) ++{ ++ u32 mask = ~((1 << (32 - bits)) - 1); ++ mask >>= bitoffset; ++ return data & ~mask; ++} ++ ++/* bm??? functions assumes that in-memory bitmap is in bigendian byte order */ ++int bmffo(u32 *, int, int); ++int bmffz(u32 *, int, int); ++int bmclr(u32 *, int, int, int); ++int bmset(u32 *, int, int, int); ++ ++#endif +diff -aurN a/fs/asfs/dir.c b/fs/asfs/dir.c +--- a/fs/asfs/dir.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/dir.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,240 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * Copyright (C) 2003,2004 Marek 'March' Szyprowski ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++extern struct dentry_operations asfs_dentry_operations; ++ ++int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ++{ ++ struct inode *dir = filp->f_dentry->d_inode; ++ struct super_block *sb = dir->i_sb; ++ struct nls_table *nls_io = ASFS_SB(sb)->nls_io; ++ struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; ++ u8 buf[512]; ++ unsigned long f_pos; ++ int stored = 0; ++ ++ struct buffer_head *bh; ++ struct fsObjectContainer *objcont; ++ struct fsObject *obj; ++ u32 block; ++ int startnode; ++ int add; ++ ++ asfs_debug("asfs_readdir:\n"); ++ ++ if (filp->f_pos == ASFS_SB(sb)->totalblocks) ++ return stored; ++ ++ f_pos = filp->f_pos; ++ ++ if (f_pos == 0) { ++ filp->private_data = (void *)0; ++ if (filldir(dirent, ".", 1, f_pos, dir->i_ino, DT_DIR) < 0) ++ return 0; ++ filp->f_pos = f_pos = 1; ++ stored++; ++ } ++ if (f_pos == 1) { ++ if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_dentry), DT_DIR) < 0) ++ return stored; ++ filp->f_pos = f_pos = 2; ++ stored++; ++ } ++ ++ if (ASFS_I(dir)->firstblock == 0) { /* empty directory */ ++ filp->f_pos = ASFS_SB(sb)->totalblocks; ++ ASFS_I(dir)->modified = 0; ++ return stored; ++ } ++ ++ if (f_pos == 2) { /* reading directory from its beginning */ ++ block = ASFS_I(dir)->firstblock; ++ add = 1; ++ startnode = 0; ++ } else { ++ startnode = (int)filp->private_data; ++ add = 0; ++ if (ASFS_I(dir)->modified == 0) ++ block = f_pos; ++ else ++ block = ASFS_I(dir)->firstblock; ++ } ++ ++ do { ++ if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) ++ return stored; ++ objcont = (struct fsObjectContainer *) bh->b_data; ++ obj = &(objcont->object[0]); ++ ++ while (be32_to_cpu(obj->objectnode) > 0 && ++ ((char *)obj - (char *)objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { ++ ++ if (!add && be32_to_cpu(obj->objectnode) == startnode) ++ add++; ++ ++ if (add && !(obj->bits & OTYPE_HIDDEN)) { ++ unsigned int type; ++ asfs_translate(buf, obj->name, nls_io, nls_disk, 512); ++ asfs_debug("ASFS: DirFilling: entry #%d \"%s\" (node %u offset %u), type %x\n", \ ++ stored, buf, be32_to_cpu(obj->objectnode), block, obj->bits); ++ filp->f_pos = block; ++ ++ if (obj->bits & OTYPE_DIR) ++ type = DT_DIR; ++ else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) ++ type = DT_LNK; ++ else ++ type = DT_REG; ++ ++ if (filldir(dirent, buf, strlen(buf), block, be32_to_cpu(obj->objectnode), type) < 0) { ++ filp->private_data = (void *)be32_to_cpu(obj->objectnode); ++ ASFS_I(dir)->modified = 0; ++ asfs_debug("ASFS: DirFilling: to be continued...\n"); ++ asfs_brelse(bh); ++ return stored; ++ } ++ stored++; ++ } ++ obj = asfs_nextobject(obj); ++ } ++ block = be32_to_cpu(objcont->next); ++ asfs_brelse(bh); ++ ++ } while (block != 0); ++ ++ filp->f_pos = ASFS_SB(sb)->totalblocks; ++ ASFS_I(dir)->modified = 0; ++ ++ return stored; ++} ++ ++static struct fsObject *asfs_find_obj_by_name_nls(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name) ++{ ++ struct fsObject *obj; ++ u8 buf[512]; ++ ++ obj = &(objcont->object[0]); ++ while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { ++ asfs_translate(buf, obj->name, ASFS_SB(sb)->nls_io, ASFS_SB(sb)->nls_disk, 512); ++ if (asfs_namecmp(buf, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, ASFS_SB(sb)->nls_io) == 0) { ++ asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); ++ return obj; ++ } ++ obj = asfs_nextobject(obj); ++ } ++ return NULL; ++} ++ ++struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ++{ ++ int res = -EACCES; /* placeholder for "no data here" */ ++ struct inode *inode; ++ struct super_block *sb = dir->i_sb; ++ u8 *name = (u8 *) dentry->d_name.name; ++ struct buffer_head *bh; ++ struct fsObject *obj; ++ u8 bufname[ASFS_MAXFN_BUF]; ++ ++ asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); ++ ++ asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); ++ ++ lock_super(sb); ++ ++ if ((!strchr(name, '?')) && (ASFS_I(dir)->hashtable != 0)) { /* hashtable block is available and name can be reverse translated, quick search */ ++ struct fsObjectNode *node_p; ++ struct buffer_head *node_bh; ++ u32 node; ++ u16 hash16; ++ ++ asfs_debug("(quick search) "); ++ ++ if (!(bh = asfs_breadcheck(sb, ASFS_I(dir)->hashtable, ASFS_HASHTABLE_ID))) { ++ unlock_super(sb); ++ return ERR_PTR(res); ++ } ++ hash16 = asfs_hash(bufname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); ++ node = be32_to_cpu(((struct fsHashTable *) bh->b_data)->hashentry[HASHCHAIN(hash16)]); ++ asfs_brelse(bh); ++ ++ while (node != 0) { ++ if (asfs_getnode(sb, node, &node_bh, &node_p) != 0) ++ goto not_found; ++ if (be16_to_cpu(node_p->hash16) == hash16) { ++ if (!(bh = asfs_breadcheck(sb, be32_to_cpu(node_p->node.data), ASFS_OBJECTCONTAINER_ID))) { ++ asfs_brelse(node_bh); ++ unlock_super(sb); ++ return ERR_PTR(res); ++ } ++ if ((obj = asfs_find_obj_by_name(sb, (struct fsObjectContainer *) bh->b_data, bufname)) != NULL) { ++ asfs_brelse(node_bh); ++ goto found_inode; ++ } ++ asfs_brelse(bh); ++ } ++ node = be32_to_cpu(node_p->next); ++ asfs_brelse(node_bh); ++ } ++ } else { /* hashtable not available or name can't be reverse-translated, long search */ ++ struct fsObjectContainer *objcont; ++ u32 block; ++ ++ asfs_debug("(long search) "); ++ block = ASFS_I(dir)->firstblock; ++ while (block != 0) { ++ if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) { ++ unlock_super(sb); ++ return ERR_PTR(res); ++ } ++ objcont = (struct fsObjectContainer *) bh->b_data; ++ if ((obj = asfs_find_obj_by_name_nls(sb, objcont, name)) != NULL) ++ goto found_inode; ++ block = be32_to_cpu(objcont->next); ++ asfs_brelse(bh); ++ } ++ } ++ ++not_found: ++ unlock_super(sb); ++ inode = NULL; ++ asfs_debug("object not found.\n"); ++ if (0) { ++found_inode: ++ unlock_super(sb); ++ if (!(inode = iget_locked(sb, be32_to_cpu(obj->objectnode)))) { ++ asfs_debug("ASFS: Strange - no inode allocated.\n"); ++ return ERR_PTR(res); ++ } ++ if (inode->i_state & I_NEW) { ++ asfs_read_locked_inode(inode, obj); ++ unlock_new_inode(inode); ++ } ++ asfs_brelse(bh); ++ } ++ res = 0; ++ dentry->d_op = &asfs_dentry_operations; ++ d_add(dentry, inode); ++ return ERR_PTR(res); ++} +diff -aurN a/fs/asfs/extents.c b/fs/asfs/extents.c +--- a/fs/asfs/extents.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/extents.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,586 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * This file contains some parts of the original amiga version of ++ * SmartFilesystem source code. ++ * ++ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, ++ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek ++ * ++ * Adapted and modified by Marek 'March' Szyprowski ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++ /* This function looks for the BNode equal to the key. If no ++ exact match is available then the BNode which is slightly ++ lower than key will be returned. If no such BNode exists ++ either, then the first BNode in this block is returned. ++ ++ This function will return the first BNode even if there ++ are no BNode's at all in this block (this can only happen ++ for the Root of the tree). Be sure to check if the Root ++ is not empty before calling this function. */ ++ ++static struct BNode *searchforbnode(u32 key, struct BTreeContainer *tc) ++{ ++ struct BNode *tn; ++ s16 n = be16_to_cpu(tc->nodecount) - 1; ++ ++ tn = (struct BNode *) ((u8 *) tc->bnode + n * tc->nodesize); ++ for (;;) { ++ if (n <= 0 || key >= be32_to_cpu(tn->key)) ++ return tn; ++ ++ tn = (struct BNode *) ((u8 *) tn - tc->nodesize); ++ n--; ++ } ++} ++ ++/* This function finds the BNode with the given key. If no exact match can be ++ found then this function will return either the next or previous closest ++ match (don't rely on this). ++ ++ If there were no BNode's at all, then *returned_bh will be NULL. */ ++ ++static int findbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode) ++{ ++ u32 rootblock = ASFS_SB(sb)->extentbnoderoot; ++ ++ asfs_debug("findbnode: Looking for BNode with key %d\n", key); ++ ++ while ((*returned_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) { ++ struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data; ++ struct BTreeContainer *btc = &bnc->btc; ++ ++ if (btc->nodecount == 0) { ++ *returned_bnode = NULL; ++ break; ++ } ++ ++ *returned_bnode = searchforbnode(key, btc); ++ if (btc->isleaf == TRUE) ++ break; ++ ++ rootblock = be32_to_cpu((*returned_bnode)->data); ++ asfs_brelse(*returned_bh); ++ } ++ ++ if (*returned_bh == NULL) ++ return -EIO; ++ ++ return 0; ++} ++ ++int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, struct fsExtentBNode **ret_ebn) ++{ ++ int result; ++ if ((result = findbnode(sb, key, ret_bh, (struct BNode **)ret_ebn)) == 0) ++ if (be32_to_cpu((*ret_ebn)->key) != key) { ++ brelse(*ret_bh); ++ *ret_bh = NULL; ++ return -ENOENT; ++ } ++ ++ return result; ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++ /* This routine inserts a node sorted into a BTreeContainer. It does ++ this by starting at the end, and moving the nodes one by one to ++ a higher slot until the empty slot has the correct position for ++ this key. Donot use this function on completely filled ++ BTreeContainers! */ ++ ++static struct BNode *insertbnode(u32 key, struct BTreeContainer *btc) ++{ ++ struct BNode *bn; ++ bn = (struct BNode *) ((u8 *) btc->bnode + btc->nodesize * (be16_to_cpu(btc->nodecount) - 1)); ++ ++ for (;;) { ++ if (bn < btc->bnode || key > be32_to_cpu(bn->key)) { ++ bn = (struct BNode *) ((u8 *) bn + btc->nodesize); ++ bn->key = cpu_to_be32(key); ++ btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + 1); ++ break; ++ } else ++ memmove((u8 *)bn + btc->nodesize, bn, btc->nodesize); ++ ++ bn = (struct BNode *) ((u8 *) bn - btc->nodesize); ++ } ++ ++ return bn; ++} ++ ++static int getparentbtreecontainer(struct super_block *sb, struct buffer_head *bh, struct buffer_head **parent_bh) ++{ ++ u32 rootblock = ASFS_SB(sb)->extentbnoderoot; ++ u32 childkey = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->btc.bnode[0].key); ++ u32 childblock = be32_to_cpu(((struct fsBNodeContainer *) bh->b_data)->bheader.ownblock); ++ ++ asfs_debug("getparentbtreecontainer: Getting parent of block %d\n", childblock); ++ ++ /* This function gets the BTreeContainer parent of the passed in buffer_head. If ++ there is no parent this function sets dest_cont io_bh to NULL */ ++ ++ if (rootblock != childblock) { ++ while ((*parent_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) { ++ struct fsBNodeContainer *bnc = (void *) (*parent_bh)->b_data; ++ struct BTreeContainer *btc = &bnc->btc; ++ struct BNode *bn; ++ s16 n = be16_to_cpu(btc->nodecount); ++ ++ if (btc->isleaf == TRUE) { ++ asfs_brelse(*parent_bh); ++ break; ++ } ++ ++ while (n-- > 0) ++ if (be32_to_cpu(btc->bnode[n].data) == childblock) ++ return 0; /* Found parent!! */ ++ ++ bn = searchforbnode(childkey, btc); /* This searchforbnode() doesn't have to get EXACT key matches. */ ++ rootblock = be32_to_cpu(bn->data); ++ asfs_brelse(*parent_bh); ++ } ++ if (*parent_bh == NULL) ++ return -EIO; ++ } ++ ++ *parent_bh = NULL; ++ return 0; ++} ++ ++/* Spits a btreecontainer. It realses passed in bh! */ ++ ++static int splitbtreecontainer(struct super_block *sb, struct buffer_head *bh) ++{ ++ struct buffer_head *bhparent; ++ struct BNode *bn; ++ int errorcode; ++ ++ asfs_debug("splitbtreecontainer: splitting block %u\n", be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock)); ++ ++ if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { ++ if (bhparent == NULL) { ++ u32 newbcontblock; ++ u32 bcontblock; ++ /* We need to create Root tree-container - adding new level to extent tree */ ++ ++ asfs_debug("splitbtreecontainer: creating root tree-container.\n"); ++ ++ bhparent = bh; ++ if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newbcontblock))) { ++ struct fsBNodeContainer *bnc = (void *) bh->b_data; ++ struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; ++ struct BTreeContainer *btcparent = &bncparent->btc; ++ ++ bcontblock = be32_to_cpu(bncparent->bheader.ownblock); ++ memcpy(bh->b_data, bhparent->b_data, sb->s_blocksize); ++ bnc->bheader.ownblock = cpu_to_be32(newbcontblock); ++ asfs_bstore(sb, bh); ++ ++ memset(bhparent->b_data, '\0', sb->s_blocksize); /* Not strictly needed, but makes things more clear. */ ++ bncparent->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID); ++ bncparent->bheader.ownblock = cpu_to_be32(bcontblock); ++ btcparent->isleaf = FALSE; ++ btcparent->nodesize = sizeof(struct BNode); ++ btcparent->nodecount = 0; ++ ++ bn = insertbnode(0, btcparent); ++ bn->data = cpu_to_be32(newbcontblock); ++ ++ asfs_bstore(sb, bhparent); ++ } ++ if (bh == NULL) ++ errorcode = -EIO; ++ } ++ ++ if (errorcode == 0) { ++ struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; ++ struct BTreeContainer *btcparent = &bncparent->btc; ++ int branches1 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btcparent->nodesize; ++ ++ if (be16_to_cpu(btcparent->nodecount) == branches1) { ++ /* We need to split the parent tree-container first! */ ++ if ((errorcode = splitbtreecontainer(sb, bhparent)) == 0) { ++ /* bhparent might have changed after the split and has been released */ ++ if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { ++ bncparent = (void *) bhparent->b_data; ++ btcparent = &bncparent->btc; ++ } ++ } ++ } ++ ++ if (errorcode == 0) { ++ u32 newbcontblock; ++ struct buffer_head *bhnew; ++ ++ /* We can split this container and add it to the parent ++ because the parent has enough room. */ ++ ++ if ((errorcode = asfs_allocadminspace(sb, &newbcontblock)) == 0 && (bhnew = asfs_getzeroblk(sb, newbcontblock))) { ++ struct fsBNodeContainer *bncnew = (void *) bhnew->b_data; ++ struct BTreeContainer *btcnew = &bncnew->btc; ++ struct fsBNodeContainer *bnc = (void *) bh->b_data; ++ struct BTreeContainer *btc = &bnc->btc; ++ int branches2 = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; ++ u32 newkey; ++ ++ bncnew->bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID); ++ bncnew->bheader.ownblock = cpu_to_be32(newbcontblock); ++ ++ btcnew->isleaf = btc->isleaf; ++ btcnew->nodesize = btc->nodesize; ++ ++ btcnew->nodecount = cpu_to_be16(branches2 - branches2 / 2); ++ ++ memcpy(btcnew->bnode, (u8 *) btc->bnode + branches2 / 2 * btc->nodesize, (branches2 - branches2 / 2) * btc->nodesize); ++ newkey = be32_to_cpu(btcnew->bnode[0].key); ++ ++ asfs_bstore(sb, bhnew); ++ asfs_brelse(bhnew); ++ ++ btc->nodecount = cpu_to_be16(branches2 / 2); ++ asfs_bstore(sb, bh); ++ ++ bn = insertbnode(newkey, btcparent); ++ bn->data = cpu_to_be32(newbcontblock); ++ asfs_bstore(sb, bhparent); ++ } ++ } ++ } ++ asfs_brelse(bhparent); ++ } ++ asfs_brelse(bh); ++ ++ return errorcode; ++} ++ ++/* Returns created extentbnode - returned_bh need to saved and realesed in caller funkction! */ ++ ++int createextentbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode) ++{ ++ int errorcode; ++ ++ asfs_debug("createbnode: Creating BNode with key %d\n", key); ++ ++ while ((errorcode = findbnode(sb, key, returned_bh, returned_bnode)) == 0) { ++ struct fsBNodeContainer *bnc = (void *) (*returned_bh)->b_data; ++ struct BTreeContainer *btc = &bnc->btc; ++ int extbranches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; ++ ++ asfs_debug("createbnode: findbnode found block %d\n", be32_to_cpu(((struct fsBlockHeader *) (*returned_bh)->b_data)->ownblock)); ++ ++ if (be16_to_cpu(btc->nodecount) < extbranches) { ++ /* Simply insert new node in this BTreeContainer */ ++ asfs_debug("createbnode: Simple insert\n"); ++ *returned_bnode = insertbnode(key, btc); ++ break; ++ } else if ((errorcode = splitbtreecontainer(sb, *returned_bh)) != 0) ++ break; ++ ++ /* Loop and try insert it the normal way again :-) */ ++ } ++ ++ return (errorcode); ++} ++ ++ ++/* This routine removes a node from a BTreeContainer indentified ++ by its key. If no such key exists this routine does nothing. ++ It correctly handles empty BTreeContainers. */ ++ ++static void removebnode(u32 key, struct BTreeContainer *btc) ++{ ++ struct BNode *bn = btc->bnode; ++ int n = 0; ++ ++ asfs_debug("removebnode: key %d\n", key); ++ ++ while (n < be16_to_cpu(btc->nodecount)) { ++ if (be32_to_cpu(bn->key) == key) { ++ btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) - 1); ++ memmove(bn, (u8 *) bn + btc->nodesize, (be16_to_cpu(btc->nodecount) - n) * btc->nodesize); ++ break; ++ } ++ bn = (struct BNode *) ((u8 *) bn + btc->nodesize); ++ n++; ++ } ++} ++ ++int asfs_deletebnode(struct super_block *sb, struct buffer_head *bh, u32 key) ++{ ++ struct fsBNodeContainer *bnc1 = (void *) bh->b_data; ++ struct BTreeContainer *btc = &bnc1->btc; ++ u16 branches = (sb->s_blocksize - sizeof(struct fsBNodeContainer)) / btc->nodesize; ++ int errorcode = 0; ++ ++ /* Deletes specified internal node. */ ++ ++ removebnode(key, btc); ++ asfs_bstore(sb, bh); ++ ++ /* Now checks if the container still contains enough nodes, ++ and takes action accordingly. */ ++ ++ asfs_debug("deletebnode: branches = %d, btc->nodecount = %d\n", branches, be16_to_cpu(btc->nodecount)); ++ ++ if (be16_to_cpu(btc->nodecount) < (branches + 1) / 2) { ++ struct buffer_head *bhparent; ++ struct buffer_head *bhsec; ++ ++ /* nodecount has become to low. We need to merge this Container ++ with a neighbouring Container, or we need to steal a few nodes ++ from a neighbouring Container. */ ++ ++ /* We get the parent of the container here, so we can find out what ++ containers neighbour the container which currently hasn't got enough nodes. */ ++ ++ if ((errorcode = getparentbtreecontainer(sb, bh, &bhparent)) == 0) { ++ if (bhparent != NULL) { ++ struct fsBNodeContainer *bncparent = (void *) bhparent->b_data; ++ struct BTreeContainer *btcparent = &bncparent->btc; ++ s16 n; ++ ++ asfs_debug("deletebnode: get parent returned block %d.\n", be32_to_cpu(((struct fsBlockHeader *) bhparent->b_data)->ownblock)); ++ ++ for (n = 0; n < be16_to_cpu(btcparent->nodecount); n++) ++ if (btcparent->bnode[n].data == bnc1->bheader.ownblock) ++ break; ++ /* n is now the offset of our own bnode. */ ++ ++ if (n < be16_to_cpu(btcparent->nodecount) - 1) { /* Check if we have a next neighbour. */ ++ asfs_debug("deletebnode: using next container - merging blocks %d and %d\n", be32_to_cpu(bnc1->bheader.ownblock), be32_to_cpu(btcparent->bnode[n+1].data)); ++ ++ if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n + 1].data), ASFS_BNODECONTAINER_ID))) { ++ struct fsBNodeContainer *bnc_next = (void *) bhsec->b_data; ++ struct BTreeContainer *btc_next = &bnc_next->btc; ++ ++ if (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount) > branches) { /* Check if we need to steal nodes. */ ++ s16 nodestosteal = (be16_to_cpu(btc_next->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount); ++ ++ /* Merging them is not possible. Steal a few nodes then. */ ++ memcpy((u8 *) btc->bnode + be16_to_cpu(btc->nodecount) * btc->nodesize, btc_next->bnode, nodestosteal * btc->nodesize); ++ btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal); ++ asfs_bstore(sb, bh); ++ ++ memcpy(btc_next->bnode, (u8 *) btc_next->bnode + btc_next->nodesize * nodestosteal, ++ btc->nodesize * (be16_to_cpu(btc_next->nodecount) - nodestosteal)); ++ btc_next->nodecount = cpu_to_be16(be16_to_cpu(btc_next->nodecount) - nodestosteal); ++ asfs_bstore(sb, bhsec); ++ ++ btcparent->bnode[n + 1].key = btc_next->bnode[0].key; ++ asfs_bstore(sb, bhparent); ++ } else { /* Merging is possible. */ ++ memcpy((u8 *) btc->bnode + btc->nodesize * be16_to_cpu(btc->nodecount), btc_next->bnode, btc->nodesize * be16_to_cpu(btc_next->nodecount)); ++ btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + be16_to_cpu(btc_next->nodecount)); ++ asfs_bstore(sb, bh); ++ ++ if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0) ++ errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n + 1].key)); ++ } ++ asfs_brelse(bhsec); ++ } ++ } else if (n > 0) { /* Check if we have a previous neighbour. */ ++ asfs_debug("deletebnode: using prev container.\n"); ++ ++ if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent->bnode[n - 1].data), ASFS_BNODECONTAINER_ID)) == 0) { ++ struct fsBNodeContainer *bnc2 = (void *) bhsec->b_data; ++ struct BTreeContainer *btc2 = &bnc2->btc; ++ ++ if (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount) > branches) { ++ /* Merging them is not possible. Steal a few nodes then. */ ++ s16 nodestosteal = (be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount)) / 2 - be16_to_cpu(btc->nodecount); ++ ++ memmove((u8 *) btc->bnode + nodestosteal * btc->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize); ++ btc->nodecount = cpu_to_be16(be16_to_cpu(btc->nodecount) + nodestosteal); ++ memcpy(btc->bnode, (u8 *) btc2->bnode + (be16_to_cpu(btc2->nodecount) - nodestosteal) * btc2->nodesize, nodestosteal * btc->nodesize); ++ ++ asfs_bstore(sb, bh); ++ ++ btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) - nodestosteal); ++ asfs_bstore(sb, bhsec); ++ ++ btcparent->bnode[n].key = btc->bnode[0].key; ++ asfs_bstore(sb, bhparent); ++ } else { /* Merging is possible. */ ++ memcpy((u8 *) btc2->bnode + be16_to_cpu(btc2->nodecount) * btc2->nodesize, btc->bnode, be16_to_cpu(btc->nodecount) * btc->nodesize); ++ btc2->nodecount = cpu_to_be16(be16_to_cpu(btc2->nodecount) + be16_to_cpu(btc->nodecount)); ++ asfs_bstore(sb, bhsec); ++ ++ if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock))) == 0) ++ errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent->bnode[n].key)); ++ } ++ asfs_brelse(bhsec); ++ } ++ } ++ /* else ++ { ++ // Never happens, except for root and then we don't care. ++ } */ ++ } else if (btc->nodecount == 1) { ++ /* No parent, so must be root. */ ++ ++ asfs_debug("deletebnode: no parent so must be root\n"); ++ ++ if (btc->isleaf == FALSE) { ++ struct fsBNodeContainer *bnc3 = (void *) bh->b_data; ++ ++ /* The current root has only 1 node. We now copy the data of this node into the ++ root and promote that data to be the new root. The rootblock number stays the ++ same that way. */ ++ ++ if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btc->bnode[0].data), ASFS_BNODECONTAINER_ID))) { ++ u32 blockno = be32_to_cpu(((struct fsBlockHeader *) bh->b_data)->ownblock); ++ memcpy(bh->b_data, bhsec->b_data, sb->s_blocksize); ++ bnc3->bheader.ownblock = cpu_to_be32(blockno); ++ ++ asfs_bstore(sb, bh); ++ errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec->b_data)->ownblock)); ++ asfs_brelse(bhsec); ++ } else ++ errorcode = -EIO; ++ } ++ /* If not, then root contains leafs. */ ++ } ++ ++ asfs_debug("deletebnode: almost done\n"); ++ /* otherwise, it must be the root, and the root is allowed ++ to contain less than the minimum amount of nodes. */ ++ ++ } ++ if (bhparent != NULL) ++ asfs_brelse(bhparent); ++ } ++ ++ return errorcode; ++} ++ ++ /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it. ++ This function DOES NOT fix the next pointer in a possible fsExtentBNode which ++ might have been pointing to the first BNode we are deleting. Make sure you check ++ this yourself, if needed. ++ ++ If key is zero, than this function does nothing. */ ++ ++int asfs_deleteextents(struct super_block *sb, u32 key) ++{ ++ struct buffer_head *bh; ++ struct fsExtentBNode *ebn; ++ int errorcode = 0; ++ ++ asfs_debug("deleteextents: Entry -- deleting extents from key %d\n", key); ++ ++ while (key != 0 && (errorcode = findbnode(sb, key, &bh, (struct BNode **) &ebn)) == 0) { ++ /* node to be deleted located. */ ++ key = be32_to_cpu(ebn->next); ++ if ((errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key), be16_to_cpu(ebn->blocks))) != 0) ++ break; ++ ++ if ((errorcode = asfs_deletebnode(sb, bh, be32_to_cpu(ebn->key))) != 0) ++ break; ++ ++ asfs_brelse(bh); ++ } ++ ++ return (errorcode); ++} ++ ++ /* This function adds /blocks/ blocks starting at block /newspace/ to a file ++ identified by /objectnode/ and /lastextentbnode/. /io_lastextentbnode/ can ++ be zero if there is no ExtentBNode chain attached to this file yet. ++ /blocks/ ranges from 1 to 8192. To be able to extend Extents which are ++ almost full, it is wise to make this value no higher than 8192 blocks. ++ /io_lastextentbnode/ will contain the new lastextentbnode value when this ++ function completes. ++ If there was no chain yet, then this function will create a new one. */ ++ ++int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, u32 objectnode, u32 *io_lastextentbnode) ++{ ++ struct buffer_head *bh; ++ struct fsExtentBNode *ebn; ++ int errorcode = 0; ++ ++ if (*io_lastextentbnode != 0) { ++ /* There was already a ExtentBNode chain for this file. Extending it. */ ++ ++ asfs_debug(" addblocks: Extending existing ExtentBNode chain.\n"); ++ ++ if ((errorcode = asfs_getextent(sb, *io_lastextentbnode, &bh, &ebn)) == 0) { ++ if (be32_to_cpu(ebn->key) + be16_to_cpu(ebn->blocks) == newspace && be16_to_cpu(ebn->blocks) + blocks < 65536) { ++ /* It is possible to extent the last ExtentBNode! */ ++ asfs_debug(" addblocks: Extending last ExtentBNode.\n"); ++ ++ ebn->blocks = cpu_to_be16(be16_to_cpu(ebn->blocks) + blocks); ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ } else { ++ /* It isn't possible to extent the last ExtentBNode so we create ++ a new one and link it to the last ExtentBNode. */ ++ ++ ebn->next = cpu_to_be32(newspace); ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) { ++ asfs_debug(" addblocks: Created new ExtentBNode.\n"); ++ ++ ebn->key = cpu_to_be32(newspace); ++ ebn->prev = cpu_to_be32(*io_lastextentbnode); ++ ebn->next = 0; ++ ebn->blocks = cpu_to_be16(blocks); ++ ++ *io_lastextentbnode = newspace; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ ASFS_SB(sb)->block_rovingblockptr = newspace + blocks; ++ ++ /* to be changed in the future */ ++/* if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks) ++ ASFS_SB(sb)->block_rovingblockptr = 0;*/ ++ } ++ } ++ } ++ } else { ++ /* There is no ExtentBNode chain yet for this file. Attaching one! */ ++ if ((errorcode = createextentbnode(sb, newspace, &bh, (struct BNode **) &ebn)) == 0) { ++ asfs_debug(" addblocks: Created new ExtentBNode chain.\n"); ++ ++ ebn->key = cpu_to_be32(newspace); ++ ebn->prev = cpu_to_be32(objectnode + 0x80000000); ++ ebn->next = 0; ++ ebn->blocks = cpu_to_be16(blocks); ++ ++ *io_lastextentbnode = newspace; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ ASFS_SB(sb)->block_rovingblockptr = newspace + blocks; ++ ++/* if (ASFS_SB(sb)->block_rovingblockptr >= ASFS_SB(sb)->totalblocks) ++ ASFS_SB(sb)->block_rovingblockptr = 0;*/ ++ } ++ } ++ ++ asfs_debug(" addblocks: done.\n"); ++ ++ return errorcode; ++} ++#endif +diff -aurN a/fs/asfs/file.c b/fs/asfs/file.c +--- a/fs/asfs/file.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/file.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,251 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * Copyright (C) 2003,2004 Marek 'March' Szyprowski ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++static int ++asfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) ++{ ++ struct buffer_head *ebn_bh; ++ struct fsExtentBNode extent, *ebn_p; ++ u32 filedata; ++ unsigned long pos; ++ struct super_block *sb = inode->i_sb; ++#ifdef CONFIG_ASFS_RW ++ int error; ++ struct buffer_head *bh; ++ struct fsObject *obj; ++#endif ++ ++ asfs_debug("ASFS: get_block(%lu, %ld, %d)\n", inode->i_ino, block, create); ++ ++ if (block < 0) { ++ printk(KERN_ERR "ASFS: asfsget_block: requested block (%ld) < 0!\n", block); ++ return -EIO; ++ } else if (block >= inode->i_blocks && !create) { ++ printk(KERN_ERR "ASFS: asfsget_block: strange block request %ld!\n", block); ++ return -EIO; ++ } ++ ++ if (create) ++#ifdef CONFIG_ASFS_RW ++ ASFS_I(inode)->modified = TRUE; ++#else ++ return -EROFS; ++#endif ++ ++ if (block < inode->i_blocks) ++ create = 0; ++ ++ lock_super(sb); ++ ++#ifdef CONFIG_ASFS_RW ++ if (create) { ++ int blockstoadd; ++ u32 newspace, addedblocks; ++ ++ blockstoadd = block - inode->i_blocks + 1; ++ ++ if (blockstoadd < ASFS_BLOCKCHUNKS) ++ blockstoadd = ASFS_BLOCKCHUNKS; ++ ++ asfs_debug("ASFS get_block: Trying to add %d blocks to file\n", blockstoadd); ++ ++ if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { ++ unlock_super(sb); ++ return error; ++ } ++ ++ if ((error = asfs_addblockstofile(sb, bh, obj, blockstoadd, &newspace, &addedblocks)) != 0) { ++ asfs_brelse(bh); ++ unlock_super(sb); ++ return error; ++ } ++ ASFS_I(inode)->mmu_private += addedblocks * sb->s_blocksize; ++ inode->i_blocks += addedblocks; ++ ASFS_I(inode)->ext_cache.key = 0; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); ++ asfs_brelse(bh); ++ } ++#endif ++ ++ if (ASFS_I(inode)->ext_cache.key > 0 && ASFS_I(inode)->ext_cache.startblock <= block) { ++ extent.key = ASFS_I(inode)->ext_cache.key; ++ extent.next = ASFS_I(inode)->ext_cache.next; ++ extent.blocks = ASFS_I(inode)->ext_cache.blocks; ++ pos = ASFS_I(inode)->ext_cache.startblock; ++ } else { ++ if (asfs_getextent(inode->i_sb, ASFS_I(inode)->firstblock, &ebn_bh, &ebn_p) != 0) { ++ unlock_super(sb); ++ return -EIO; ++ } ++ extent.key = be32_to_cpu(ebn_p->key); ++ extent.next = be32_to_cpu(ebn_p->next); ++ extent.blocks = be16_to_cpu(ebn_p->blocks); ++ pos = 0; ++ asfs_brelse(ebn_bh); ++ } ++ ebn_p = &extent; ++ filedata = ebn_p->next; ++ ++ while (pos + ebn_p->blocks <= block && ebn_p->next != 0 && pos < inode->i_blocks) { ++ pos += ebn_p->blocks; ++ if (asfs_getextent(inode->i_sb, filedata, &ebn_bh, &ebn_p) != 0) { ++ unlock_super(sb); ++ return -EIO; ++ } ++ extent.key = be32_to_cpu(ebn_p->key); ++ extent.next = be32_to_cpu(ebn_p->next); ++ extent.blocks = be16_to_cpu(ebn_p->blocks); ++ ebn_p = &extent; ++ filedata = ebn_p->next; ++ asfs_brelse(ebn_bh); ++ } ++ ++ unlock_super(sb); ++ ++ map_bh(bh_result, inode->i_sb, (sector_t) (ebn_p->key + block - pos)); ++ ++ if (create) ++ set_buffer_new(bh_result); ++ ++ asfs_debug("ASFS: get_block - mapped block %lu\n", ebn_p->key + block - pos); ++ ++ ASFS_I(inode)->ext_cache.startblock = pos; ++ ASFS_I(inode)->ext_cache.key = ebn_p->key; ++ ASFS_I(inode)->ext_cache.next = ebn_p->next; ++ ASFS_I(inode)->ext_cache.blocks = ebn_p->blocks; ++ ++ return 0; ++} ++ ++int asfs_readpage(struct file *file, struct page *page) ++{ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ return block_read_full_page(page, asfs_get_block); ++} ++ ++sector_t asfs_bmap(struct address_space *mapping, sector_t block) ++{ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ return generic_block_bmap(mapping,block,asfs_get_block); ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++int asfs_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ return block_write_full_page(page, asfs_get_block, wbc); ++} ++ ++int asfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) ++{ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ return cont_prepare_write(page, from, to, asfs_get_block, &ASFS_I(page->mapping->host)->mmu_private); ++} ++ ++void asfs_truncate(struct inode *inode) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct buffer_head *bh; ++ struct fsObject *obj; ++ ++ asfs_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n", ++ (u32)inode->i_ino, (u32)ASFS_I(inode)->mmu_private, (u32)inode->i_size); ++ ++ if (inode->i_size > ASFS_I(inode)->mmu_private) { ++ printk("ASFS: enlarging file is not supported yet\n"); ++ return; ++ } ++ ++ lock_super(sb); ++ ++ if ((asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { ++ unlock_super(sb); ++ return; ++ } ++ ++ if (asfs_truncateblocksinfile(sb, bh, obj, inode->i_size) != 0) { ++ asfs_brelse(bh); ++ unlock_super(sb); ++ return; ++ } ++ ++ obj->object.file.size = cpu_to_be32(inode->i_size); ++ ASFS_I(inode)->mmu_private = inode->i_size; ++ ASFS_I(inode)->modified = TRUE; ++ inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ unlock_super(sb); ++} ++ ++int asfs_file_open(struct inode *inode, struct file *filp) ++{ ++ if (atomic_read(&filp->f_count) != 1) ++ return 0; ++ asfs_debug("ASFS: file open (node %d)\n", (int)inode->i_ino); ++ return 0; ++} ++ ++int asfs_file_release(struct inode *inode, struct file *filp) ++{ ++ int error = 0; ++ ++ asfs_debug("ASFS: file release (node %d oc %d)\n", (int)inode->i_ino, atomic_read(&filp->f_count)); ++ ++ if (atomic_read(&filp->f_count) != 0) ++ return 0; ++ ++ if (ASFS_I(inode)->modified == TRUE) { ++ struct buffer_head *bh; ++ struct fsObject *obj; ++ lock_super(inode->i_sb); ++ ++ if ((error = asfs_readobject(inode->i_sb, inode->i_ino, &bh, &obj)) != 0) { ++ unlock_super(inode->i_sb); ++ return error; ++ } ++ ++ obj->datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60); ++ if (inode->i_mode & S_IFREG) { ++ error = asfs_truncateblocksinfile(inode->i_sb, bh, obj, (u32)inode->i_size); ++ obj->object.file.size = cpu_to_be32(inode->i_size); ++ ASFS_I(inode)->mmu_private = inode->i_size; ++ inode->i_blocks = (be32_to_cpu(obj->object.file.size) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; ++ } ++ asfs_bstore(inode->i_sb, bh); ++ ++ unlock_super(inode->i_sb); ++ ++ asfs_brelse(bh); ++ } ++ ASFS_I(inode)->modified = FALSE; ++ ++ return error; ++} ++ ++#endif +diff -aurN a/fs/asfs/inode.c b/fs/asfs/inode.c +--- a/fs/asfs/inode.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/inode.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,426 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta8 ++ * ++ * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++/* Mapping from our types to the kernel */ ++ ++static struct address_space_operations asfs_aops = { ++ .readpage = asfs_readpage, ++ .sync_page = block_sync_page, ++ .bmap = asfs_bmap, ++#ifdef CONFIG_ASFS_RW ++ .writepage = asfs_writepage, ++ .prepare_write = asfs_prepare_write, ++ .commit_write = generic_commit_write, ++#endif ++}; ++ ++static struct file_operations asfs_file_operations = { ++ .llseek = generic_file_llseek, ++ .read = generic_file_read, ++ .mmap = generic_file_mmap, ++#ifdef CONFIG_ASFS_RW ++ .write = generic_file_write, ++ .open = asfs_file_open, ++ .release = asfs_file_release, ++ .fsync = file_fsync, ++#endif ++}; ++ ++static struct file_operations asfs_dir_operations = { ++ .read = generic_read_dir, ++ .readdir = asfs_readdir, ++}; ++ ++static struct inode_operations asfs_dir_inode_operations = { ++ .lookup = asfs_lookup, ++#ifdef CONFIG_ASFS_RW ++ .create = asfs_create, ++ .unlink = asfs_unlink, ++ .symlink = asfs_symlink, ++ .mkdir = asfs_mkdir, ++ .rmdir = asfs_rmdir, ++ .rename = asfs_rename, ++/* .setattr = asfs_notify_change,*/ ++#endif ++}; ++ ++static struct inode_operations asfs_file_inode_operations = { ++#ifdef CONFIG_ASFS_RW ++ .truncate = asfs_truncate, ++/* .setattr = asfs_notify_change,*/ ++#endif ++}; ++ ++static struct address_space_operations asfs_symlink_aops = { ++ .readpage = asfs_symlink_readpage, ++}; ++ ++static struct inode_operations asfs_symlink_inode_operations = { ++ .readlink = page_readlink, ++ .follow_link = page_follow_link_light, ++ .put_link = page_put_link, ++#ifdef CONFIG_ASFS_RW ++/* .setattr = asfs_notify_change,*/ ++#endif ++}; ++ ++void asfs_read_locked_inode(struct inode *inode, void *arg) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct fsObject *obj = arg; ++ ++ inode->i_mode = ASFS_SB(sb)->mode; ++ inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = be32_to_cpu(obj->datemodified) + (365*8+2)*24*60*60; ++ /* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */ ++ inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; ++ inode->i_uid = ASFS_SB(sb)->uid; ++ inode->i_gid = ASFS_SB(sb)->gid; ++ ++ asfs_debug("asfs_read_inode2: Setting-up node %lu... ", inode->i_ino); ++ ++ if (obj->bits & OTYPE_DIR) { ++ asfs_debug("dir (FirstdirBlock: %u, HashTable %u)\n", \ ++ be32_to_cpu(obj->object.dir.firstdirblock), be32_to_cpu(obj->object.dir.hashtable)); ++ ++ inode->i_size = 0; ++ inode->i_op = &asfs_dir_inode_operations; ++ inode->i_fop = &asfs_dir_operations; ++ inode->i_mode |= S_IFDIR | ((inode->i_mode & 0400) ? 0100 : 0) | ++ ((inode->i_mode & 0040) ? 0010 : 0) | ((inode->i_mode & 0004) ? 0001 : 0); ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); ++ ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable); ++ ASFS_I(inode)->modified = 0; ++ } else if (obj->bits & OTYPE_LINK && !(obj->bits & OTYPE_HARDLINK)) { ++ asfs_debug("symlink\n"); ++ inode->i_size = 0; ++ inode->i_op = &asfs_symlink_inode_operations; ++ inode->i_mapping->a_ops = &asfs_symlink_aops; ++ inode->i_mode |= S_IFLNK | S_IRWXUGO; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); ++ } else { ++ asfs_debug("file (Size: %u, FirstBlock: %u)\n", be32_to_cpu(obj->object.file.size), be32_to_cpu(obj->object.file.data)); ++ inode->i_size = be32_to_cpu(obj->object.file.size); ++ inode->i_blocks = (be32_to_cpu(obj->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++ inode->i_op = &asfs_file_inode_operations; ++ inode->i_fop = &asfs_file_operations; ++ inode->i_mapping->a_ops = &asfs_aops; ++ inode->i_mode |= S_IFREG; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); ++ ASFS_I(inode)->ext_cache.startblock = 0; ++ ASFS_I(inode)->ext_cache.key = 0; ++ ASFS_I(inode)->mmu_private = inode->i_size; ++ } ++ return; ++} ++ ++struct inode *asfs_get_root_inode(struct super_block *sb) ++{ ++ struct inode *result = NULL; ++ struct fsObject *obj; ++ struct buffer_head *bh; ++ ++ asfs_debug("asfs_get_root_inode\n"); ++ ++ if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { ++ obj = &(((struct fsObjectContainer *)bh->b_data)->object[0]); ++ if (be32_to_cpu(obj->objectnode) > 0) ++ result = iget_locked(sb, be32_to_cpu(obj->objectnode)); ++ ++ if (result != NULL && result->i_state & I_NEW) { ++ asfs_read_locked_inode(result, obj); ++ unlock_new_inode(result); ++ } ++ asfs_brelse(bh); ++ } ++ return result; ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++static void asfs_sync_dir_inode(struct inode *dir, struct fsObject *obj) ++{ ++ ASFS_I(dir)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); ++ ASFS_I(dir)->modified = 1; ++ dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME; ++ obj->datemodified = cpu_to_be32(dir->i_mtime.tv_sec - (365*8+2)*24*60*60); ++} ++ ++enum { it_file, it_dir, it_link }; ++ ++static int asfs_create_object(struct inode *dir, struct dentry *dentry, int mode, int type, const char *symname) ++{ ++ int error; ++ struct super_block *sb = dir->i_sb; ++ struct inode *inode; ++ struct buffer_head *bh, *dir_bh; ++ struct fsObject obj_data, *dir_obj, *obj; ++ u8 *name = (u8 *) dentry->d_name.name; ++ u8 bufname[ASFS_MAXFN_BUF]; ++ ++ asfs_debug("asfs_create_obj %s in dir node %d\n", name, (int)dir->i_ino); ++ ++ asfs_translate(bufname, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); ++ if ((error = asfs_check_name(bufname, strlen(bufname))) != 0) ++ return error; ++ ++ sb = dir->i_sb; ++ inode = new_inode(sb); ++ if (!inode) ++ return -ENOMEM; ++ ++ memset(&obj_data, 0, sizeof(struct fsObject)); ++ ++ obj_data.protection = cpu_to_be32(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE); ++ obj_data.datemodified = cpu_to_be32(inode->i_mtime.tv_sec - (365*8+2)*24*60*60); ++ switch (type) { ++ case it_dir: ++ obj_data.bits = OTYPE_DIR; ++ break; ++ case it_link: ++ obj_data.bits = OTYPE_LINK; ++ break; ++ default: ++ break; ++ } ++ ++ lock_super(sb); ++ ++ if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) { ++ dec_count(inode); ++ unlock_super(sb); ++ return error; ++ } ++ ++ bh = dir_bh; ++ obj = dir_obj; ++ ++ if ((error = asfs_createobject(sb, &bh, &obj, &obj_data, bufname, FALSE)) != 0) { ++ asfs_brelse(dir_bh); ++ dec_count(inode); ++ unlock_super(sb); ++ return error; ++ } ++ ++ inode->i_ino = be32_to_cpu(obj->objectnode); ++ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; ++ inode->i_size = inode->i_blocks = inode->i_blksize = 0; ++ inode->i_uid = dir->i_uid; ++ inode->i_gid = dir->i_gid; ++ inode->i_mode = mode | ASFS_SB(sb)->mode; ++ ++ switch (type) { ++ case it_dir: ++ inode->i_mode |= S_IFDIR; ++ inode->i_op = &asfs_dir_inode_operations; ++ inode->i_fop = &asfs_dir_operations; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.dir.firstdirblock); ++ ASFS_I(inode)->hashtable = be32_to_cpu(obj->object.dir.hashtable); ++ ASFS_I(inode)->modified = 0; ++ break; ++ case it_file: ++ inode->i_mode |= S_IFREG; ++ inode->i_op = &asfs_file_inode_operations; ++ inode->i_fop = &asfs_file_operations; ++ inode->i_mapping->a_ops = &asfs_aops; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); ++ ASFS_I(inode)->ext_cache.startblock = 0; ++ ASFS_I(inode)->ext_cache.key = 0; ++ ASFS_I(inode)->mmu_private = inode->i_size; ++ break; ++ case it_link: ++ inode->i_mode = S_IFLNK | S_IRWXUGO; ++ inode->i_op = &page_symlink_inode_operations; ++ inode->i_mapping->a_ops = &asfs_symlink_aops; ++ ASFS_I(inode)->firstblock = be32_to_cpu(obj->object.file.data); ++ error = asfs_write_symlink(inode, symname); ++ break; ++ default: ++ break; ++ } ++ ++ asfs_bstore(sb, bh); ++ insert_inode_hash(inode); ++ mark_inode_dirty(inode); ++ d_instantiate(dentry, inode); ++ asfs_sync_dir_inode(dir, dir_obj); ++ asfs_bstore(sb, dir_bh); ++ ++ unlock_super(sb); ++ asfs_brelse(bh); ++ asfs_brelse(dir_bh); ++ ++ return error; ++} ++ ++int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) ++{ ++ return asfs_create_object(dir, dentry, mode, it_file, NULL); ++} ++ ++int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ return asfs_create_object(dir, dentry, mode, it_dir, NULL); ++} ++ ++int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++{ ++ return asfs_create_object(dir, dentry, 0, it_link, symname); ++} ++ ++int asfs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ ++ if (ASFS_I(dentry->d_inode)->firstblock != 0) ++ return -ENOTEMPTY; ++ ++ return asfs_unlink(dir, dentry); ++} ++ ++int asfs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error; ++ struct super_block *sb = dir->i_sb; ++ struct buffer_head *bh, *dir_bh; ++ struct fsObject *dir_obj, *obj; ++ ++ asfs_debug("ASFS: %s\n", __FUNCTION__); ++ ++ lock_super(sb); ++ ++ if ((error = asfs_readobject(sb, inode->i_ino, &bh, &obj)) != 0) { ++ unlock_super(sb); ++ return error; ++ } ++ if ((error = asfs_deleteobject(sb, bh, obj)) != 0) { ++ asfs_brelse(bh); ++ unlock_super(sb); ++ return error; ++ } ++ asfs_brelse(bh); ++ ++ /* directory data could change after removing the object */ ++ if ((error = asfs_readobject(sb, dir->i_ino, &dir_bh, &dir_obj)) != 0) { ++ unlock_super(sb); ++ return error; ++ } ++ ++ asfs_sync_dir_inode(dir, dir_obj); ++ asfs_bstore(sb, dir_bh); ++ ++ dec_count(inode); ++ unlock_super(sb); ++ asfs_brelse(dir_bh); ++ ++ return 0; ++} ++ ++int asfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct super_block *sb = old_dir->i_sb; ++ struct buffer_head *src_bh, *old_bh, *new_bh; ++ int error; ++ struct fsObject *src_obj, *old_obj, *new_obj; ++ u8 bufname[ASFS_MAXFN_BUF]; ++ ++ asfs_debug("ASFS: rename (old=%u,\"%*s\" to new=%u,\"%*s\")\n", ++ (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, ++ (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); ++ ++ asfs_translate(bufname, (u8 *) new_dentry->d_name.name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io, ASFS_MAXFN_BUF); ++ if ((error = asfs_check_name(bufname, strlen(bufname))) != 0) ++ return error; ++ ++ ++ /* Unlink destination if it already exists */ ++ if (new_dentry->d_inode) ++ if ((error = asfs_unlink(new_dir, new_dentry)) != 0) ++ return error; ++ ++ lock_super(sb); ++ ++ if ((error = asfs_readobject(sb, old_dentry->d_inode->i_ino, &src_bh, &src_obj)) != 0) { ++ unlock_super(sb); ++ return error; ++ } ++ if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) { ++ asfs_brelse(src_bh); ++ unlock_super(sb); ++ return error; ++ } ++ ++ if ((error = asfs_renameobject(sb, src_bh, src_obj, new_bh, new_obj, bufname)) != 0) { ++ asfs_brelse(src_bh); ++ asfs_brelse(new_bh); ++ unlock_super(sb); ++ return error; ++ } ++ asfs_brelse(src_bh); ++ asfs_brelse(new_bh); ++ ++ if ((error = asfs_readobject(sb, old_dir->i_ino, &old_bh, &old_obj)) != 0) { ++ unlock_super(sb); ++ return error; ++ } ++ if ((error = asfs_readobject(sb, new_dir->i_ino, &new_bh, &new_obj)) != 0) { ++ asfs_brelse(old_bh); ++ unlock_super(sb); ++ return error; ++ } ++ ++ asfs_sync_dir_inode(old_dir, old_obj); ++ asfs_sync_dir_inode(new_dir, new_obj); ++ ++ asfs_bstore(sb, new_bh); ++ asfs_bstore(sb, old_bh); ++ ++ unlock_super(sb); ++ asfs_brelse(old_bh); ++ asfs_brelse(new_bh); ++ ++ mark_inode_dirty(old_dir); ++ mark_inode_dirty(new_dir); ++ ++ return 0; ++} ++ ++/* ++int asfs_notify_change(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ ++ asfs_debug("ASFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid); ++ ++ error = inode_change_ok(inode,attr); ++ ++ return error; ++} ++*/ ++#endif +diff -aurN a/fs/asfs/namei.c b/fs/asfs/namei.c +--- a/fs/asfs/namei.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/namei.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,197 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * Copyright (C) 2003,2004 Marek 'March' Szyprowski ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++static inline u8 asfs_upperchar(u8 c) ++{ ++ if ((c >= 224 && c <= 254 && c != 247) || (c >= 'a' && c <= 'z')) ++ c -= 32; ++ return (c); ++} ++ ++u8 asfs_lowerchar(u8 c) ++{ ++ if ((c >= 192 && c <= 222 && c != 215) || (c >= 'A' && c <= 'Z')) ++ c += 32; ++ return (c); ++} ++ ++static inline u8 asfs_nls_upperchar(u8 c, struct nls_table *t) ++{ ++ if (t) { ++ u8 nc = t->charset2upper[c]; ++ return nc ? nc : c; ++ } else ++ return asfs_upperchar(c); ++} ++ ++/* Check if the name is valid for a asfs object. */ ++ ++inline int asfs_check_name(const u8 *name, int len) ++{ ++ int i; ++ ++ if (len > ASFS_MAXFN) ++ return -ENAMETOOLONG; ++ ++ for (i = 0; i < len; i++) ++ if (name[i] < ' ' || name[i] == ':' || (name[i] > 0x7e && name[i] < 0xa0)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* Note: the dentry argument is the parent dentry. */ ++ ++static int asfs_hash_dentry(struct dentry *dentry, struct qstr *qstr) ++{ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ const u8 *name = qstr->name; ++ unsigned long hash; ++ int i; ++ struct nls_table *nls_io = ASFS_SB(sb)->nls_io; ++ ++ i = asfs_check_name(qstr->name,qstr->len); ++ if (i) ++ return i; ++ ++ hash = init_name_hash(); ++ ++ if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) ++ for (i=qstr->len; i > 0; name++, i--) ++ hash = partial_name_hash(*name, hash); ++ else ++ for (i=qstr->len; i > 0; name++, i--) ++ hash = partial_name_hash(asfs_nls_upperchar(*name, nls_io), hash); ++ ++ qstr->hash = end_name_hash(hash); ++ ++ return 0; ++} ++ ++static int asfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) ++{ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ const u8 *aname = a->name; ++ const u8 *bname = b->name; ++ int len; ++ struct nls_table *nls_io = ASFS_SB(sb)->nls_io; ++ ++ /* 'a' is the qstr of an already existing dentry, so the name ++ * must be valid. 'b' must be validated first. ++ */ ++ ++ if (asfs_check_name(b->name,b->len)) ++ return 1; ++ ++ if (a->len != b->len) ++ return 1; ++ ++ if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) { ++ for (len=a->len; len > 0; len--) ++ if (*aname++ != *bname++) ++ return 1; ++ } else { ++ for (len=a->len; len > 0; len--) ++ if (asfs_nls_upperchar(*aname++, nls_io) != asfs_nls_upperchar(*bname++, nls_io)) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++struct dentry_operations asfs_dentry_operations = { ++ d_hash: asfs_hash_dentry, ++ d_compare: asfs_compare_dentry, ++}; ++ ++int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t) ++{ ++ if (casesensitive) { ++ while (*s == *ct && *ct != '\0' && *ct != '/') { ++ s++; ++ ct++; ++ } ++ } else { ++ while (asfs_nls_upperchar(*s, t) == asfs_nls_upperchar(*ct, t) && *ct != '\0' ++ && *ct != '/') { ++ s++; ++ ct++; ++ } ++ } ++ return (*s == '\0' && (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s; ++} ++ ++u16 asfs_hash(u8 *name, int casesensitive) ++{ ++ u16 hashval = 0; ++ while (name[hashval] != 0 && name[hashval] != '/') ++ hashval++; ++ if (casesensitive) { ++ u8 c = *name; ++ while (c != 0 && c != '/') { ++ hashval = hashval * 13 + c; ++ c = *++name; ++ } ++ } else { ++ u8 c = *name; ++ while (c != 0 && c != '/') { ++ hashval = hashval * 13 + asfs_upperchar(c); ++ c = *++name; ++ } ++ } ++ return hashval; ++} ++ ++void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit) ++{ ++ wchar_t uni; ++ int i, len; ++ int from_len, to_len = limit; ++ ++ if (nls_to) { ++ from_len = strlen(from); ++ for (i=0; i < from_len && to_len > 1; ) { ++ len = nls_from->char2uni(&from[i], from_len-i, &uni); ++ if (len > 0) { ++ i += len; ++ len = nls_to->uni2char(uni, to, to_len); ++ if (len > 0) { ++ to += len; ++ to_len -= len; ++ } ++ } else ++ i++; ++ if (len < 0) { ++ *to++ = '?'; ++ to_len--; ++ } ++ } ++ *to = '\0'; ++ } else { ++ strncpy (to, from, limit); ++ to[limit] = '\0'; ++ } ++} +diff -aurN a/fs/asfs/nodes.c b/fs/asfs/nodes.c +--- a/fs/asfs/nodes.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/nodes.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,455 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * This file contains some parts of the original amiga version of ++ * SmartFilesystem source code. ++ * ++ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, ++ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek ++ * ++ * Adapted and modified by Marek 'March' Szyprowski ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++/* Finds a specific node by number. */ ++int asfs_getnode(struct super_block *sb, u32 nodeno, struct buffer_head **ret_bh, struct fsObjectNode **ret_node) ++{ ++ struct buffer_head *bh; ++ struct fsNodeContainer *nodecont; ++ u32 nodeindex = ASFS_SB(sb)->objectnoderoot; ++ ++ while ((bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) { ++ nodecont = (struct fsNodeContainer *) bh->b_data; ++ ++ if (be32_to_cpu(nodecont->nodes) == 1) { ++ *ret_node = (struct fsObjectNode *) ((u8 *) nodecont->node + NODE_STRUCT_SIZE * (nodeno - be32_to_cpu(nodecont->nodenumber))); ++ *ret_bh = bh; ++ return 0; ++ } else { ++ u16 containerentry = (nodeno - be32_to_cpu(nodecont->nodenumber)) / be32_to_cpu(nodecont->nodes); ++ nodeindex = be32_to_cpu(nodecont->node[containerentry]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); ++ } ++ asfs_brelse(bh); ++ } ++ if (bh == NULL) ++ return -EIO; ++ return -ENOENT; ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++ /* Looks for the parent of the passed-in buffer_head (fsNodeContainer) ++ starting from the root. It returns an error if any error occured. ++ If error is 0 and io_bh is NULL as well, then there was no parent (ie, ++ you asked parent of the root). Otherwise io_bh should contain the ++ parent of the passed-in NodeContainer. */ ++ ++static int parentnodecontainer(struct super_block *sb, struct buffer_head **io_bh) ++{ ++ u32 noderoot = ASFS_SB(sb)->objectnoderoot; ++ u32 childblock = be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock); ++ u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (*io_bh)->b_data)->nodenumber); ++ int errorcode = 0; ++ ++ if (noderoot == childblock) { ++ *io_bh = NULL; ++ return 0; ++ } ++ ++ while ((*io_bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) { ++ struct fsNodeContainer *nc = (void *) (*io_bh)->b_data; ++ ++ if (be32_to_cpu(nc->nodes) == 1) { ++ /* We've descended the tree to a leaf NodeContainer, something ++ which should never happen if the passed-in io_bh had ++ contained a valid fsNodeContainer. */ ++ printk("ASFS: Failed to locate the parent NodeContainer - node tree is corrupted!\n"); ++ *io_bh = NULL; ++ return -EIO; ++ } else { ++ u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); ++ noderoot = be32_to_cpu(nc->node[containerentry]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); ++ } ++ ++ if (noderoot == childblock) ++ break; ++ ++ asfs_brelse(*io_bh); ++ } ++ ++ if (*io_bh == NULL) ++ return -EIO; ++ ++ return errorcode; ++} ++ ++ ++static int isfull(struct super_block *sb, struct fsNodeContainer *nc) ++{ ++ u32 *p = nc->node; ++ s16 n = NODECONT_BLOCK_COUNT; ++ ++ while (--n >= 0) { ++ if (*p == 0 || (be32_to_cpu(*p) & 0x00000001) == 0) { ++ break; ++ } ++ p++; ++ } ++ ++ return n < 0; ++} ++ ++static int markparentfull(struct super_block *sb, struct buffer_head *bh) ++{ ++ u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (bh->b_data))->nodenumber); ++ int errorcode; ++ ++ if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) { ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); ++ ++ nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) | 0x00000001); ++ ++ asfs_bstore(sb, bh); ++ ++ if (isfull(sb, nc)) { /* This container now is full as well! Mark the next higher up container too then! */ ++ return markparentfull(sb, bh); ++ } ++ asfs_brelse(bh); ++ } ++ ++ return errorcode; ++} ++ ++static int addnewnodelevel(struct super_block *sb, u16 nodesize) ++{ ++ struct buffer_head *bh; ++ u32 noderoot = ASFS_SB(sb)->objectnoderoot; ++ int errorcode; ++ ++ /* Adds a new level to the Node tree. */ ++ ++ asfs_debug("addnewnodelevel: Entry\n"); ++ ++ if ((bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) { ++ struct buffer_head *newbh; ++ u32 newblock; ++ ++ if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (newbh = asfs_getzeroblk(sb, newblock))) { ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ struct fsNodeContainer *newnc = (void *) newbh->b_data; ++ ++ /* The newly allocated block will become a copy of the current root. */ ++ ++ newnc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID); ++ newnc->bheader.ownblock = cpu_to_be32(newblock); ++ newnc->nodenumber = nc->nodenumber; ++ newnc->nodes = nc->nodes; ++ memcpy(newnc->node, nc->node, sb->s_blocksize - sizeof(struct fsNodeContainer)); ++ ++ asfs_bstore(sb, newbh); ++ asfs_brelse(newbh); ++ ++ /* The current root will now be transformed into a new root. */ ++ ++ if (be32_to_cpu(nc->nodes) == 1) ++ nc->nodes = cpu_to_be32((sb->s_blocksize - sizeof(struct fsNodeContainer)) / nodesize); ++ else ++ nc->nodes = cpu_to_be32(be32_to_cpu(nc->nodes) * NODECONT_BLOCK_COUNT); ++ ++ nc->node[0] = cpu_to_be32((newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY)) + 1); /* Tree is full from that point! */ ++ memset(&nc->node[1], 0, sb->s_blocksize - sizeof(struct fsNodeContainer) - 4); ++ ++ asfs_bstore(sb, bh); ++ } ++ asfs_brelse(bh); ++ } else ++ errorcode = -EIO; ++ ++ return errorcode; ++} ++ ++static int createnodecontainer(struct super_block *sb, u32 nodenumber, u32 nodes, u32 * returned_block) ++{ ++ struct buffer_head *bh; ++ int errorcode; ++ u32 newblock; ++ ++ asfs_debug("createnodecontainer: nodenumber = %u, nodes = %u\n", nodenumber, nodes); ++ ++ if ((errorcode = asfs_allocadminspace(sb, &newblock)) == 0 && (bh = asfs_getzeroblk(sb, newblock))) { ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ ++ nc->bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID); ++ nc->bheader.ownblock = cpu_to_be32(newblock); ++ ++ nc->nodenumber = cpu_to_be32(nodenumber); ++ nc->nodes = cpu_to_be32(nodes); ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ *returned_block = newblock; ++ } ++ ++ return errorcode; ++} ++ ++ /* This function creates a new fsNode structure in a fsNodeContainer. If needed ++ it will create a new fsNodeContainers and a new fsNodeIndexContainer. */ ++ ++int asfs_createnode(struct super_block *sb, struct buffer_head **returned_bh, struct fsNode **returned_node, u32 * returned_nodeno) ++{ ++ u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE; ++ u32 noderoot = ASFS_SB(sb)->objectnoderoot; ++ u32 nodeindex = noderoot; ++ int errorcode = 0; ++ ++ while ((*returned_bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) { ++ struct fsNodeContainer *nc = (void *) (*returned_bh)->b_data; ++ ++ if (be32_to_cpu(nc->nodes) == 1) { /* Is it a leaf-container? */ ++ struct fsNode *n; ++ s16 i = nodecount; ++ ++ n = (struct fsNode *) nc->node; ++ ++ while (i-- > 0) { ++ if (n->data == 0) ++ break; ++ ++ n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); ++ } ++ ++ if (i >= 0) { ++ /* Found an empty fsNode structure! */ ++ *returned_node = n; ++ *returned_nodeno = be32_to_cpu(nc->nodenumber) + ((u8 *) n - (u8 *) nc->node) / NODE_STRUCT_SIZE; ++ ++ asfs_debug("createnode: Created Node %d\n", *returned_nodeno); ++ ++ /* Below we continue to look through the NodeContainer block. We skip the entry ++ we found to be unused, and see if there are any more unused entries. If we ++ do not find any more unused entries then this container is now full. */ ++ ++ n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); ++ ++ while (i-- > 0) { ++ if (n->data == 0) ++ break; ++ ++ n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); ++ } ++ ++ if (i < 0) { ++ /* No more empty fsNode structures in this block. Mark parent full. */ ++ errorcode = markparentfull(sb, *returned_bh); ++ } ++ ++ return errorcode; ++ } else { ++ /* What happened now is that we found a leaf-container which was ++ completely filled. In practice this should only happen when there ++ is only a single NodeContainer (only this container), or when there ++ was an error in one of the full-bits in a higher level container. */ ++ ++ if (noderoot != nodeindex) { ++ /*** Hmmm... it looks like there was a damaged full-bit or something. ++ In this case we'd probably better call markcontainerfull. */ ++ ++ printk("ASFS: Couldn't find empty Node in NodeContainer while NodeIndexContainer indicated there should be one!\n"); ++ ++ errorcode = -ENOSPC; ++ break; ++ } else { ++ /* Container is completely filled. */ ++ ++ if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0) ++ return errorcode; ++ ++ nodeindex = noderoot; ++ } ++ } ++ } else { /* This isn't a leaf container */ ++ u32 *p = nc->node; ++ s16 i = NODECONT_BLOCK_COUNT; ++ ++ /* We've read a normal container */ ++ ++ while (i-- > 0) { ++ if (*p != 0 && (be32_to_cpu(*p) & 0x00000001) == 0) ++ break; ++ ++ p++; ++ } ++ ++ if (i >= 0) { ++ /* Found a not completely filled Container */ ++ ++ nodeindex = be32_to_cpu(*p) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY); ++ } else { ++ /* Everything in the NodeIndexContainer was completely filled. There possibly ++ are some unused pointers in this block however. */ ++ ++ asfs_debug("createnode: NodeContainer at block has no empty Nodes.\n"); ++ ++ p = nc->node; ++ i = NODECONT_BLOCK_COUNT; ++ ++ while (i-- > 0) { ++ if (*p == 0) ++ break; ++ ++ p++; ++ } ++ ++ if (i >= 0) { ++ u32 newblock; ++ u32 nodes; ++ ++ /* Found an unused Container pointer */ ++ ++ if (be32_to_cpu(nc->nodes) == (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE) { ++ nodes = 1; ++ } else { ++ nodes = be32_to_cpu(nc->nodes) / NODECONT_BLOCK_COUNT; ++ } ++ ++ if ((errorcode = createnodecontainer(sb, be32_to_cpu(nc->nodenumber) + (p - nc->node) * be32_to_cpu(nc->nodes), nodes, &newblock)) != 0) { ++ break; ++ } ++ ++ *p = cpu_to_be32(newblock << (sb->s_blocksize_bits - ASFS_BLCKFACCURACY)); ++ ++ asfs_bstore(sb, *returned_bh); ++ } else { ++ /* Container is completely filled. This must be the top-level NodeIndex container ++ as otherwise the full-bit would have been wrong! */ ++ ++ if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0) ++ break; ++ ++ nodeindex = noderoot; ++ } ++ } ++ } ++ asfs_brelse(*returned_bh); ++ } ++ ++ if (*returned_bh == NULL) ++ return -EIO; ++ ++ return (errorcode); ++} ++ ++static int markparentempty(struct super_block *sb, struct buffer_head *bh) ++{ ++ u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber); ++ int errorcode; ++ ++ if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != 0) { ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ int wasfull; ++ u16 containerentry = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); ++ ++ wasfull = isfull(sb, nc); ++ ++ nc->node[containerentry] = cpu_to_be32(be32_to_cpu(nc->node[containerentry]) & ~0x00000001); ++ ++ asfs_bstore(sb, bh); ++ ++ if (wasfull) { ++ /* This container was completely full before! Mark the next higher up container too then! */ ++ return markparentempty(sb, bh); ++ } ++ asfs_brelse(bh); ++ } ++ ++ return errorcode; ++} ++ ++static int freecontainer(struct super_block *sb, struct buffer_head *bh) ++{ ++ u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh->b_data)->nodenumber); ++ int errorcode; ++ ++ if ((errorcode = parentnodecontainer(sb, &bh)) == 0 && bh != NULL) { /* This line also prevents the freeing of the noderoot. */ ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ u16 containerindex = (nodenumber - be32_to_cpu(nc->nodenumber)) / be32_to_cpu(nc->nodes); ++ ++ if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(nc->node[containerindex]) >> (sb->s_blocksize_bits - ASFS_BLCKFACCURACY))) == 0) { ++ u32 *p = nc->node; ++ s16 n = NODECONT_BLOCK_COUNT; ++ ++ nc->node[containerindex] = 0; ++ asfs_bstore(sb, bh); ++ ++ while (n-- > 0) ++ if (*p++ != 0) ++ break; ++ ++ if (n < 0) { /* This container is now completely empty! Free this NodeIndexContainer too then! */ ++ return freecontainer(sb, bh); ++ } ++ } ++ asfs_brelse(bh); ++ } ++ ++ return errorcode; ++} ++ ++static int internaldeletenode(struct super_block *sb, struct buffer_head *bh, struct fsNode *n) ++{ ++ struct fsNodeContainer *nc = (void *) bh->b_data; ++ u16 nodecount = (sb->s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE; ++ s16 i = nodecount; ++ s16 empty = 0; ++ int errorcode = 0; ++ ++ n->data = 0; ++ n = (struct fsNode *) nc->node; ++ ++ while (i-- > 0) { ++ if (n->data == 0) ++ empty++; ++ ++ n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE); ++ } ++ ++ asfs_bstore(sb, bh); ++ ++ if (empty == 1) /* NodeContainer was completely full before, so we need to mark it empty now. */ ++ errorcode = markparentempty(sb, bh); ++ else if (empty == nodecount) /* NodeContainer is now completely empty! Free it! */ ++ errorcode = freecontainer(sb, bh); ++ ++ return (errorcode); ++} ++ ++int asfs_deletenode(struct super_block *sb, u32 objectnode) ++{ ++ struct buffer_head *bh; ++ struct fsObjectNode *on; ++ int errorcode; ++ ++ asfs_debug("deletenode: Deleting Node %d\n", objectnode); ++ ++ if ((errorcode = asfs_getnode(sb, objectnode, &bh, &on)) == 0) ++ errorcode = internaldeletenode(sb, bh, (struct fsNode *) on); ++ ++ asfs_brelse(bh); ++ return (errorcode); ++} ++ ++#endif +diff -aurN a/fs/asfs/objects.c b/fs/asfs/objects.c +--- a/fs/asfs/objects.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/objects.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,765 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta7 ++ * ++ * This file contains some parts of the original amiga version of ++ * SmartFilesystem source code. ++ * ++ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, ++ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek ++ * ++ * Adapted and modified by Marek 'March' Szyprowski ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++ ++struct fsObject *asfs_nextobject(struct fsObject *obj) ++{ ++ int i; ++ u8 *p = obj->name; ++ ++ for (i = 2; i > 0; p++) ++ if (*p == '\0') ++ i--; ++ if ((p - (u8 *) obj) & 0x01) ++ p++; ++ ++ return ((struct fsObject *) p); ++} ++ ++struct fsObject *asfs_find_obj_by_name(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name) ++{ ++ struct fsObject *obj; ++ ++ obj = &(objcont->object[0]); ++ while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { ++ if (asfs_namecmp(obj->name, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, NULL) == 0) { ++ asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); ++ return obj; ++ } ++ obj = asfs_nextobject(obj); ++ } ++ return NULL; ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++struct fsObject *find_obj_by_node(struct super_block *sb, struct fsObjectContainer *objcont, u32 objnode) ++{ ++ struct fsObject *obj; ++ ++ obj = &(objcont->object[0]); ++ while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { ++ if (be32_to_cpu(obj->objectnode) == objnode) { ++ return obj; ++ } ++ obj = asfs_nextobject(obj); ++ } ++ return NULL; ++} ++ ++int asfs_readobject(struct super_block *sb, u32 objectnode, struct buffer_head **bh, struct fsObject **returned_object) ++{ ++ struct fsObjectNode *on; ++ int errorcode; ++ u32 contblock; ++ ++ asfs_debug("Seaching object - node %d\n", objectnode); ++ ++ if ((errorcode = asfs_getnode(sb, objectnode, bh, &on)) != 0) ++ return errorcode; ++ contblock = be32_to_cpu(on->node.data); ++ asfs_brelse(*bh); ++ ++ if (contblock > 0 && (*bh = asfs_breadcheck(sb, contblock, ASFS_OBJECTCONTAINER_ID))) { ++ *returned_object = find_obj_by_node(sb, (void *) (*bh)->b_data, objectnode); ++ if (*returned_object == NULL) { ++ brelse(*bh); ++ *bh = NULL; ++ return -ENOENT; ++ } ++ return 0; ++ } else ++ return -EIO; ++} ++ ++static int removeobjectcontainer(struct super_block *sb, struct buffer_head *bh) ++{ ++ struct fsObjectContainer *oc = (void *) bh->b_data; ++ int errorcode; ++ struct buffer_head *block; ++ ++ asfs_debug("removeobjectcontainer: block %u\n", be32_to_cpu(oc->bheader.ownblock)); ++ ++ if (oc->next != 0 && oc->next != oc->bheader.ownblock) { ++ struct fsObjectContainer *next_oc; ++ ++ if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID)) == NULL) ++ return -EIO; ++ ++ next_oc = (void *) block->b_data; ++ next_oc->previous = oc->previous; ++ ++ asfs_bstore(sb, block); ++ asfs_brelse(block); ++ } ++ ++ if (oc->previous != 0 && oc->previous != oc->bheader.ownblock) { ++ struct fsObjectContainer *previous_oc; ++ ++ if ((block = asfs_breadcheck(sb, be32_to_cpu(oc->previous), ASFS_OBJECTCONTAINER_ID)) == NULL) ++ return -EIO; ++ ++ previous_oc = (void *) block->b_data; ++ previous_oc->next = oc->next; ++ ++ asfs_bstore(sb, block); ++ asfs_brelse(block); ++ } else { ++ struct fsObject *parent_o; ++ ++ if ((errorcode = asfs_readobject(sb, be32_to_cpu(oc->parent), &block, &parent_o)) != 0) ++ return (errorcode); ++ ++ parent_o->object.dir.firstdirblock = oc->next; ++ ++ asfs_bstore(sb, block); ++ asfs_brelse(block); ++ } ++ ++ if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(oc->bheader.ownblock))) != 0) ++ return (errorcode); ++ ++ return (0); ++} ++ ++static int setrecycledinfodiff(struct super_block *sb, s32 deletedfiles, s32 deletedblocks) ++{ ++ struct buffer_head *bh; ++ ++ if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { ++ struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); ++ ++ ri->deletedfiles = cpu_to_be32(be32_to_cpu(ri->deletedfiles) + deletedfiles); ++ ri->deletedblocks = cpu_to_be32(be32_to_cpu(ri->deletedblocks) + deletedblocks); ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ } else ++ return -EIO; ++ return 0; ++} ++ ++ /* This function removes the fsObject structure passed in from the passed ++ buffer_head. If the ObjectContainer becomes completely empty it will be ++ delinked from the ObjectContainer chain and marked free for reuse. ++ This function doesn't delink the object from the hashchain! */ ++ ++static int simpleremoveobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) ++{ ++ struct fsObjectContainer *oc = (void *) bh->b_data; ++ int errorcode = 0; ++ ++ asfs_debug("simpleremoveobject:\n"); ++ ++ if (be32_to_cpu(oc->parent) == ASFS_RECYCLEDNODE) { ++ /* This object is removed from the Recycled directory. */ ++ if ((errorcode = setrecycledinfodiff(sb, -1, -((be32_to_cpu(o->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits))) != 0) ++ return errorcode; ++ } ++ ++ if ((asfs_nextobject(oc->object))->name[0] == '\0') ++ errorcode = removeobjectcontainer(sb, bh); ++ else { ++ struct fsObject *nexto; ++ int objlen; ++ ++ nexto = asfs_nextobject(o); ++ objlen = (u8 *) nexto - (u8 *) o; ++ ++ memmove(o, nexto, sb->s_blocksize - ((u8 *) nexto - (u8 *) oc)); ++ memset((u8 *) oc + sb->s_blocksize - objlen, 0, objlen); ++ ++ asfs_bstore(sb, bh); ++ } ++ return errorcode; ++} ++ ++/* This function delinks the passed in ObjectNode from its hash-chain. Handy when deleting ++ the object, or when renaming/moving it. */ ++ ++static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode) ++{ ++ struct fsObject *o; ++ int errorcode = 0; ++ struct buffer_head *block; ++ ++ asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode); ++ ++ if ((errorcode = asfs_readobject(sb, parentobjectnode, &block, &o)) == 0 && o->object.dir.hashtable != 0) { ++ u32 hashtable = be32_to_cpu(o->object.dir.hashtable); ++ asfs_brelse(block); ++ ++ if ((block = asfs_breadcheck(sb, hashtable, ASFS_HASHTABLE_ID))) { ++ struct buffer_head *node_bh; ++ struct fsObjectNode *onptr, on; ++ struct fsHashTable *ht = (void *) block->b_data; ++ u32 nexthash; ++ ++ if ((errorcode = asfs_getnode(sb, objectnode, &node_bh, &onptr)) == 0) { ++ u16 hashchain; ++ ++ asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n"); ++ ++ hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); ++ nexthash = be32_to_cpu(ht->hashentry[hashchain]); ++ ++ if (nexthash == objectnode) { ++ /* The hashtable directly points to the fsObject to be delinked. We simply ++ modify the Hashtable to point to the new nexthash entry. */ ++ ++ asfs_debug("dehashobject: The hashtable points directly to the to be delinked object\n"); ++ ++ ht->hashentry[hashchain] = onptr->next; ++ asfs_bstore(sb, block); ++ } else { ++ struct fsObjectNode *onsearch = 0; ++ ++ on = *onptr; ++ ++ asfs_debug("dehashobject: Walking through hashchain\n"); ++ ++ while (nexthash != 0 && nexthash != objectnode) { ++ asfs_brelse(node_bh); ++ if ((errorcode = asfs_getnode(sb, nexthash, &node_bh, &onsearch)) != 0) ++ break; ++ nexthash = be32_to_cpu(onsearch->next); ++ } ++ ++ if (errorcode == 0) { ++ if (nexthash != 0) { ++ /* Previous fsObjectNode found in hash chain. Modify the fsObjectNode to 'skip' the ++ ObjectNode which is being delinked from the hash chain. */ ++ ++ onsearch->next = on.next; ++ asfs_bstore(sb, node_bh); ++ } else { ++ printk("ASFS: Hashchain of object %d is corrupt or incorrectly linked.", objectnode); ++ ++ /*** This is strange. We have been looking for the fsObjectNode which is located before the ++ passed in fsObjectNode in the hash-chain. However, we never found the ++ fsObjectNode reffered to in the hash-chain! Has to be somekind ++ of internal error... */ ++ ++ errorcode = -ENOENT; ++ } ++ } ++ } ++ asfs_brelse(node_bh); ++ } ++ asfs_brelse(block); ++ } ++ } ++ return errorcode; ++} ++ ++ ++ /* This function removes an object from any directory. It takes care ++ of delinking the object from the hashchain and also frees the ++ objectnode number. */ ++ ++static int removeobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) ++{ ++ struct fsObjectContainer *oc = (void *) bh->b_data; ++ int errorcode; ++ ++ asfs_debug("removeobject\n"); ++ ++ if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o->objectnode), o->name, be32_to_cpu(oc->parent))) == 0) { ++ u32 objectnode = be32_to_cpu(o->objectnode); ++ ++ if ((errorcode = simpleremoveobject(sb, bh, o)) == 0) ++ errorcode = asfs_deletenode(sb, objectnode); ++ } ++ ++ return (errorcode); ++} ++ ++ /* This function deletes the specified object. */ ++int asfs_deleteobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o) ++{ ++ int errorcode = 0; ++ ++ asfs_debug("deleteobject: Entry -- deleting object %d (%s)\n", be32_to_cpu(o->objectnode), o->name); ++ ++ if ((o->bits & OTYPE_DIR) == 0 || o->object.dir.firstdirblock == 0) { ++ u8 bits = o->bits; ++ u32 hashblckno = be32_to_cpu(o->object.dir.hashtable); ++ u32 extentbnode = be32_to_cpu(o->object.file.data); ++ ++ if ((errorcode = removeobject(sb, bh, o)) == 0) { ++ if ((bits & OTYPE_LINK) != 0) { ++ asfs_debug("deleteobject: Object is soft link!\n"); ++ errorcode = asfs_freeadminspace(sb, extentbnode); ++ } else if ((bits & OTYPE_DIR) != 0) { ++ asfs_debug("deleteobject: Object is a directory!\n"); ++ errorcode = asfs_freeadminspace(sb, hashblckno); ++ } else { ++ asfs_debug("deleteobject: Object is a file\n"); ++ if (extentbnode != 0) ++ errorcode = asfs_deleteextents(sb, extentbnode); ++ } ++ } ++ } ++ ++ return (errorcode); ++} ++ ++ /* This function takes a HashBlock pointer, an ObjectNode and an ObjectName. ++ If there is a hashblock, then this function will correctly link the object ++ into the hashchain. If there isn't a hashblock (=0) then this function ++ does nothing. */ ++ ++static int hashobject(struct super_block *sb, u32 hashblock, struct fsObjectNode *on, u32 nodeno, u8 *objectname) ++{ ++ struct buffer_head *hash_bh; ++ ++ asfs_debug("hashobject, using hashblock %d\n", hashblock); ++ if (hashblock == 0) ++ return 0; ++ ++ if ((hash_bh = asfs_breadcheck(sb, hashblock, ASFS_HASHTABLE_ID))) { ++ struct fsHashTable *ht = (void *) hash_bh->b_data; ++ u32 nexthash; ++ u16 hashvalue, hashchain; ++ ++ hashvalue = asfs_hash(objectname, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); ++ hashchain = HASHCHAIN(hashvalue); ++ nexthash = be32_to_cpu(ht->hashentry[hashchain]); ++ ++ ht->hashentry[hashchain] = cpu_to_be32(nodeno); ++ ++ asfs_bstore(sb, hash_bh); ++ asfs_brelse(hash_bh); ++ ++ on->next = cpu_to_be32(nexthash); ++ on->hash16 = cpu_to_be16(hashvalue); ++ } else ++ return -EIO; ++ ++ return 0; ++} ++ ++ /* This function returns a pointer to the first unused byte in ++ an ObjectContainer. */ ++ ++static u8 *emptyspaceinobjectcontainer(struct super_block *sb, struct fsObjectContainer *oc) ++{ ++ struct fsObject *o = oc->object; ++ u8 *endadr; ++ ++ endadr = (u8 *) oc + sb->s_blocksize - sizeof(struct fsObject) - 2; ++ ++ while ((u8 *) o < endadr && o->name[0] != 0) ++ o = asfs_nextobject(o); ++ ++ return (u8 *) o; ++} ++ ++ /* This function will look in the directory indicated by io_o ++ for an ObjectContainer block which contains bytesneeded free ++ bytes. If none is found then this function simply creates a ++ new ObjectContainer and adds that to the indicated directory. */ ++ ++static int findobjectspace(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, u32 bytesneeded) ++{ ++ struct buffer_head *bhparent = *io_bh; ++ struct fsObject *oparent = *io_o; ++ struct buffer_head *bh; ++ u32 nextblock = be32_to_cpu(oparent->object.dir.firstdirblock); ++ int errorcode = 0; ++ ++ asfs_debug("findobjectspace: Looking for %u bytes in directory with ObjectNode number %d (in block %d)\n", bytesneeded, be32_to_cpu((*io_o)->objectnode), ++ be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); ++ ++ while (nextblock != 0 && (bh = asfs_breadcheck(sb, nextblock, ASFS_OBJECTCONTAINER_ID))) { ++ struct fsObjectContainer *oc = (void *) bh->b_data; ++ u8 *emptyspace; ++ ++ /* We need to find out how much free space this ObjectContainer has */ ++ ++ emptyspace = emptyspaceinobjectcontainer(sb, oc); ++ ++ if ((u8 *) oc + sb->s_blocksize - emptyspace >= bytesneeded) { ++ /* We found enough space in one of the ObjectContainer blocks!! ++ We return a struct fsObject *. */ ++ *io_bh = bh; ++ *io_o = (struct fsObject *) emptyspace; ++ break; ++ } ++ nextblock = be32_to_cpu(oc->next); ++ asfs_brelse(bh); ++ } ++ ++ if (nextblock == 0) { ++ u32 newcontblock; ++ /* If we get here, we traversed the *entire* directory (ough!) and found no empty ++ space large enough for our entry. We allocate new space and add it to this ++ directory. */ ++ ++ if ((errorcode = asfs_allocadminspace(sb, &newcontblock)) == 0 && (bh = asfs_getzeroblk(sb, newcontblock))) { ++ struct fsObjectContainer *oc = (void *) bh->b_data; ++ struct buffer_head *bhnext; ++ ++ asfs_debug("findobjectspace: No room was found, allocated new block at %u\n", newcontblock); ++ ++ /* Allocated new block. We will now link it to the START of the directory chain ++ so the new free space can be found quickly when more entries need to be added. */ ++ ++ oc->bheader.id = cpu_to_be32(ASFS_OBJECTCONTAINER_ID); ++ oc->bheader.ownblock = cpu_to_be32(newcontblock); ++ oc->parent = oparent->objectnode; ++ oc->next = oparent->object.dir.firstdirblock; ++ oc->previous = 0; ++ ++ oparent->object.dir.firstdirblock = cpu_to_be32(newcontblock); ++ ++ asfs_bstore(sb, bhparent); ++ ++ if (oc->next != 0 && (bhnext = asfs_breadcheck(sb, be32_to_cpu(oc->next), ASFS_OBJECTCONTAINER_ID))) { ++ struct fsObjectContainer *ocnext = (void *) bhnext->b_data; ++ ocnext->previous = cpu_to_be32(newcontblock); ++ asfs_bstore(sb, bhnext); ++ asfs_brelse(bhnext); ++ } ++ ++ *io_bh = bh; ++ *io_o = oc->object; ++ } ++ } ++ ++ asfs_debug("findobjectspace: new object will be in container block %u\n", be32_to_cpu(((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock)); ++ ++ return (errorcode); ++} ++ ++/* io_bh & io_o refer to the direct parent of the new object. Objectname is the ++ name of the new object (name only). Does not realese io_bh !!! */ ++ ++int asfs_createobject(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, struct fsObject *src_o, u8 *objectname, int force) ++{ ++ int errorcode; ++ u32 object_size; ++ u32 hashblock = be32_to_cpu((*io_o)->object.dir.hashtable); ++ ++ asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)->name); ++ ++ if (!force && ASFS_SB(sb)->freeblocks < ASFS_ALWAYSFREE) ++ return -ENOSPC; ++ ++ if (!force && be32_to_cpu((*io_o)->objectnode) == ASFS_RECYCLEDNODE) ++ return -EINVAL; ++ ++ object_size = sizeof(struct fsObject) + strlen(objectname) + 2; ++ ++ if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) { ++ struct fsObject *o2 = *io_o; ++ u8 *name = o2->name; ++ u8 *objname = objectname; ++ struct buffer_head *node_bh; ++ struct fsObjectNode *on; ++ u32 nodeno; ++ ++ **io_o = *src_o; /* Copying whole object data... */ ++ ++ while (*objname != 0) /* Copying name */ ++ *name++ = *objname++; ++ ++ *name++ = 0; ++ *name = 0; /* zero byte for comment */ ++ ++ if (o2->objectnode != 0) /* ObjectNode reuse or creation */ ++ errorcode = asfs_getnode(sb, o2->objectnode, &node_bh, &on); ++ else { ++ if ((errorcode = asfs_createnode(sb, &node_bh, (struct fsNode **) &on, &nodeno)) == 0) { ++ on->hash16 = cpu_to_be16(asfs_hash(o2->name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); ++ o2->objectnode = cpu_to_be32(nodeno); ++ } ++ asfs_debug("createnode returned with errorcode: %d\n", errorcode); ++ } ++ ++ if (errorcode == 0) { /* in io_bh there is a container with created object */ ++ on->node.data = ((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock; ++ if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), objectname)) == 0) { ++ asfs_bstore(sb, node_bh); ++ asfs_brelse(node_bh); ++ } else ++ errorcode = -EIO; ++ } ++ ++ if (errorcode == 0) { /* HashBlock reuse or creation:*/ ++ ++ if ((o2->bits & OTYPE_DIR) != 0 && o2->object.dir.hashtable == 0) { ++ struct buffer_head *hashbh; ++ u32 hashblock; ++ ++ asfs_debug("creating Hashblock\n"); ++ ++ if ((errorcode = asfs_allocadminspace(sb, &hashblock)) == 0 && (hashbh = asfs_getzeroblk(sb, hashblock))) { ++ struct fsHashTable *ht = (void *) hashbh->b_data; ++ ++ o2->object.dir.hashtable = cpu_to_be32(hashblock); ++ ++ ht->bheader.id = cpu_to_be32(ASFS_HASHTABLE_ID); ++ ht->bheader.ownblock = cpu_to_be32(hashblock); ++ ht->parent = o2->objectnode; ++ ++ asfs_bstore(sb, hashbh); ++ asfs_brelse(hashbh); ++ } ++ } ++ } ++ ++ if (errorcode == 0) { /* SoftLink creation: */ ++ if ((o2->bits & (OTYPE_LINK | OTYPE_HARDLINK)) == OTYPE_LINK && o2->object.file.data == 0) { ++ struct buffer_head *bh2; ++ u32 slinkblock; ++ ++ if ((errorcode = asfs_allocadminspace(sb, &slinkblock)) == 0 && (bh2 = asfs_getzeroblk(sb, slinkblock))) { ++ struct fsSoftLink *sl = (void *) bh2->b_data; ++ o2->object.file.data = cpu_to_be32(slinkblock); ++ sl->bheader.id = cpu_to_be32(ASFS_SOFTLINK_ID); ++ sl->bheader.ownblock = cpu_to_be32(slinkblock); ++ sl->parent = o2->objectnode; ++ sl->next = 0; ++ sl->previous = 0; ++ asfs_bstore(sb, bh2); ++ asfs_brelse(bh2); ++ } ++ } ++ } ++ } ++ asfs_debug("createobject: done.\n"); ++ ++ return (errorcode); ++} ++ ++ /* This function extends the file object 'o' with a number of blocks ++ (hopefully, if any blocks has been found!). Only new Extents will ++ be created -- the size of the file will not be altered, and changing ++ it is left up to the caller. If the file did not have any blocks ++ yet, then the o->object.file.data will be set to the first (new) ++ ExtentBNode. It returns the number of added blocks through ++ addedblocks pointer */ ++ ++int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objbh, struct fsObject *o, u32 blocks, u32 * newspace, u32 * addedblocks) ++{ ++ u32 lastextentbnode; ++ int errorcode = 0; ++ struct fsExtentBNode *ebnp; ++ struct buffer_head *block = NULL; ++ ++ ++ asfs_debug("extendblocksinfile: Trying to increasing number of blocks by %d.\n", blocks); ++ ++ lastextentbnode = be32_to_cpu(o->object.file.data); ++ ++ if (lastextentbnode != 0) { ++ while (lastextentbnode != 0 && errorcode == 0) { ++ if (block != NULL) ++ asfs_brelse(block); ++ errorcode = asfs_getextent(sb, lastextentbnode, &block, &ebnp); ++ lastextentbnode = be32_to_cpu(ebnp->next); ++ } ++ lastextentbnode = be32_to_cpu(ebnp->key); ++ } ++ ++ if (errorcode == 0) { ++ u32 searchstart; ++ ++ u32 found_block; ++ u32 found_blocks; ++ ++ *addedblocks = 0; ++ *newspace = 0; ++ ++ if (lastextentbnode != 0) ++ searchstart = be32_to_cpu(ebnp->key) + be16_to_cpu(ebnp->blocks); ++ else ++ searchstart = 0; //ASFS_SB(sb)->block_rovingblockptr; ++ ++ if ((errorcode = asfs_findspace(sb, blocks, searchstart, searchstart, &found_block, &found_blocks)) != 0) { ++ asfs_brelse(block); ++ asfs_debug("extendblocksinfile: findspace returned %s\n", errorcode == -ENOSPC ? "ENOSPC" : "error"); ++ return errorcode; ++ } ++ ++ blocks = found_blocks; ++ errorcode = asfs_markspace(sb, found_block, found_blocks); ++ *addedblocks = found_blocks; ++ *newspace = found_block; ++ ++ asfs_debug("extendblocksinfile: block = %u, lastextentbnode = %u, extentblocks = %d\n", found_block, lastextentbnode, blocks); ++ ++ if ((errorcode = asfs_addblocks(sb, blocks, found_block, be32_to_cpu(o->objectnode), &lastextentbnode)) != 0) { ++ asfs_debug("extendblocksinfile: addblocks returned errorcode %d\n", errorcode); ++ return errorcode; ++ } ++ ++ if (o->object.file.data == 0) ++ o->object.file.data = cpu_to_be32(lastextentbnode); ++ } ++ ++ if (block) ++ asfs_brelse(block); ++ asfs_bstore(sb, objbh); ++ ++ asfs_debug("addblockstofile: done. added %d blocks\n", *addedblocks); ++ ++ return errorcode; ++} ++ ++ /* The Object indicated by bh1 & o1, gets renamed to newname and placed ++ in the directory indicated by bhparent & oparent. */ ++ ++int asfs_renameobject(struct super_block *sb, struct buffer_head *bh1, struct fsObject *o1, struct buffer_head *bhparent, struct fsObject *oparent, u8 * newname) ++{ ++ struct fsObject object; ++ u32 oldparentnode = be32_to_cpu(((struct fsObjectContainer *) bh1->b_data)->parent); ++ u8 oldname[107]; ++ int errorcode; ++ ++ asfs_debug("renameobject: Renaming '%s' to '%s' in dir '%s'\n", o1->name, newname, oparent->name); ++ ++ object = *o1; ++ strcpy(oldname, o1->name); ++ ++ if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o1->objectnode), o1->name, oldparentnode)) == 0) { ++ u32 parentobjectnode = be32_to_cpu(oparent->objectnode); ++ ++ if ((errorcode = simpleremoveobject(sb, bh1, o1)) == 0) { ++ struct buffer_head *bh2 = bhparent; ++ struct fsObject *o2; ++ ++ /* oparent might changed after simpleremoveobject */ ++ oparent = o2 = find_obj_by_node(sb, (struct fsObjectContainer *) bhparent->b_data, parentobjectnode); ++ ++ /* In goes the Parent bh & o, out comes the New object's bh & o :-) */ ++ if ((errorcode = asfs_createobject(sb, &bh2, &o2, &object, newname, TRUE)) == 0) { ++ asfs_bstore(sb, bh2); ++ if (be32_to_cpu(oparent->objectnode) == ASFS_RECYCLEDNODE) { ++ asfs_debug("renameobject: Updating recycled dir info\n"); ++ if ((errorcode = setrecycledinfodiff(sb, 1, (be32_to_cpu(o2->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits)) != 0) { ++ brelse(bh2); ++ return errorcode; ++ } ++ } ++ brelse(bh2); ++ asfs_debug("renameobject: Succesfully created & stored new object.\n"); ++ } else { /* recreate object in old place, maybe this will not fail, but who knows... */ ++ asfs_debug("renameobject: Creating new object failed. Trying to recreate it in source directory.\n"); ++ if (asfs_readobject(sb, oldparentnode, &bh1, &o1) == 0) { ++ struct buffer_head *bh2 = bh1; ++ if (asfs_createobject(sb, &bh2, &o1, &object, oldname, TRUE) == 0) { ++ asfs_bstore(sb, bh2); ++ if (oldparentnode == ASFS_RECYCLEDNODE) { ++ asfs_debug("renameobject: Updating recycled dir info\n"); ++ setrecycledinfodiff(sb, 1, (be32_to_cpu(o1->object.file.size) + sb->s_blocksize - 1) >> sb->s_blocksize_bits); ++ } ++ brelse(bh2); ++ } ++ brelse(bh1); ++ } ++ } ++ } ++ } ++ return errorcode; ++} ++ ++ /* Truncates the specified file to /newsize/ bytes */ ++ ++int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, struct fsObject *o, u32 newsize) ++{ ++ struct buffer_head *ebh; ++ struct fsExtentBNode *ebn; ++ int errorcode; ++ u32 pos = 0; ++ u32 newblocks = (newsize + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++ u32 filedata = be32_to_cpu(o->object.file.data); ++ u32 eprev, ekey; ++ u16 eblocks; ++ ++ asfs_debug("trucateblocksinfile: newsize %u\n", newsize); ++ ++ if (filedata == 0) ++ return 0; ++ ++ for (;;) { ++ if ((errorcode = asfs_getextent(sb, filedata, &ebh, &ebn)) != 0) ++ return errorcode; ++ if (pos + be16_to_cpu(ebn->blocks) >= newblocks) ++ break; ++ pos += be16_to_cpu(ebn->blocks); ++ if ((filedata = be32_to_cpu(ebn->next)) == 0) ++ break; ++ asfs_brelse(ebh); ++ }; ++ ++ eblocks = newblocks - pos; ++ ekey = be32_to_cpu(ebn->key); ++ eprev = be32_to_cpu(ebn->prev); ++ ++ if (be16_to_cpu(ebn->blocks) < eblocks) { ++ printk("ASFS: Extent chain is too short or damaged!\n"); ++ asfs_brelse(ebh); ++ return -ENOENT; ++ } ++ if (be16_to_cpu(ebn->blocks) - eblocks > 0 && (errorcode = asfs_freespace(sb, be32_to_cpu(ebn->key) + eblocks, be16_to_cpu(ebn->blocks) - eblocks)) != 0) { ++ asfs_brelse(ebh); ++ return errorcode; ++ } ++ if (be32_to_cpu(ebn->next) > 0 && (errorcode = asfs_deleteextents(sb, be32_to_cpu(ebn->next))) != 0) { ++ asfs_brelse(ebh); ++ return errorcode; ++ } ++ ebn->blocks = cpu_to_be16(eblocks); ++ ebn->next = 0; ++ asfs_bstore(sb, ebh); ++ ++ if (eblocks == 0) { ++ if (eprev & MSB_MASK) { ++ o->object.file.data = 0; ++ asfs_bstore(sb, bh); ++ } else { ++ struct buffer_head *ebhp; ++ struct fsExtentBNode *ebnp; ++ ++ if ((errorcode = asfs_getextent(sb, eprev & !MSB_MASK, &ebhp, &ebnp)) != 0) { ++ asfs_brelse(ebh); ++ return errorcode; ++ } ++ ++ ebnp->next = 0; ++ asfs_bstore(sb, ebhp); ++ asfs_brelse(ebhp); ++ } ++ if ((errorcode = asfs_deletebnode(sb, ebh, ekey)) != 0) { ++ asfs_brelse(ebh); ++ return errorcode; ++ } ++ } ++ asfs_brelse(ebh); ++ ++ return 0; ++} ++#endif +diff -aurN a/fs/asfs/super.c b/fs/asfs/super.c +--- a/fs/asfs/super.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/super.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,488 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * ++ * version: 1.0beta9 for 2.6.xx kernel ++ * ++ * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski ++ * ++ * NLS support by Pavel Fedin (C) 2005 ++ * ++ * ++ * Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts ++ * of original amiga version of SmartFilesystem source code. ++ * ++ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx, ++ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek ++ * ++ * ++ * ASFS is based on the Amiga FFS filesystem for Linux ++ * Copyright (C) 1993 Ray Burr ++ * Copyright (C) 1996 Hans-Joachim Widmaier ++ * ++ * Earlier versions were based on the Linux implementation of ++ * the ROMFS file system ++ * Copyright (C) 1997-1999 Janos Farkas ++ * ++ * ASFS used some parts of the smbfs filesystem: ++ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke ++ * Copyright (C) 1997 by Volker Lendecke ++ * ++ * and parts of the Minix filesystem additionally ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ * Copyright (C) 1996 Gertjan van Wingerde ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++/* todo: ++ * - remove bugs ++ * - add missing features (maybe safe-delete, other...) ++ * - create other fs tools like mkfs.asfs and fsck.asfs, some data-recovery tools ++ */ ++ ++#define ASFS_VERSION "1.0beta9 (17.03.2005)" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++#include ++ ++static char asfs_default_codepage[] = CONFIG_ASFS_DEFAULT_CODEPAGE; ++static char asfs_default_iocharset[] = CONFIG_NLS_DEFAULT; ++ ++u32 asfs_calcchecksum(void *block, u32 blocksize) ++{ ++ u32 *data = block, checksum = 1; ++ while (blocksize > 0) { ++ checksum += be32_to_cpu(*data++); ++ blocksize -= 4; ++ } ++ checksum -= be32_to_cpu(((struct fsBlockHeader *)block)->checksum); ++ return -checksum; ++} ++ ++static struct super_operations asfs_ops = { ++ .alloc_inode = asfs_alloc_inode, ++ .destroy_inode = asfs_destroy_inode, ++ .put_super = asfs_put_super, ++ .statfs = asfs_statfs, ++#ifdef CONFIG_ASFS_RW ++ .remount_fs = asfs_remount, ++#endif ++}; ++ ++extern struct dentry_operations asfs_dentry_operations; ++ ++enum { ++ Opt_mode, Opt_setgid, Opt_setuid, Opt_prefix, Opt_volume, ++ Opt_lcvol, Opt_iocharset, Opt_codepage, Opt_ignore, Opt_err ++}; ++ ++static match_table_t tokens = { ++ {Opt_mode, "mode=%o"}, ++ {Opt_setgid, "setgid=%u"}, ++ {Opt_setuid, "setuid=%u"}, ++ {Opt_prefix, "prefix=%s"}, ++ {Opt_volume, "volume=%s"}, ++ {Opt_lcvol, "lowercasevol"}, ++ {Opt_iocharset, "iocharset=%s"}, ++ {Opt_codepage, "codepage=%s"}, ++ {Opt_ignore, "grpquota"}, ++ {Opt_ignore, "noquota"}, ++ {Opt_ignore, "quota"}, ++ {Opt_ignore, "usrquota"}, ++ {Opt_err, NULL}, ++}; ++ ++static int asfs_parse_options(char *options, struct super_block *sb) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ if (!options) ++ return 1; ++ while ((p = strsep(&options, ",")) != NULL) { ++ int token, option; ++ if (!*p) ++ continue; ++ token = match_token(p, tokens, args); ++ ++ switch (token) { ++ case Opt_mode: ++ if (match_octal(&args[0], &option)) ++ goto no_arg; ++ ASFS_SB(sb)->mode = option & 0777; ++ break; ++ case Opt_setgid: ++ if (match_int(&args[0], &option)) ++ goto no_arg; ++ ASFS_SB(sb)->gid = option; ++ break; ++ case Opt_setuid: ++ if (match_int(&args[0], &option)) ++ goto no_arg; ++ ASFS_SB(sb)->uid = option; ++ break; ++ case Opt_prefix: ++ if (ASFS_SB(sb)->prefix) { ++ kfree(ASFS_SB(sb)->prefix); ++ ASFS_SB(sb)->prefix = NULL; ++ } ++ ASFS_SB(sb)->prefix = match_strdup(&args[0]); ++ if (! ASFS_SB(sb)->prefix) ++ return 0; ++ break; ++ case Opt_volume: ++ if (ASFS_SB(sb)->root_volume) { ++ kfree(ASFS_SB(sb)->root_volume); ++ ASFS_SB(sb)->root_volume = NULL; ++ } ++ ASFS_SB(sb)->root_volume = match_strdup(&args[0]); ++ if (! ASFS_SB(sb)->root_volume) ++ return 0; ++ break; ++ case Opt_lcvol: ++ ASFS_SB(sb)->flags |= ASFS_VOL_LOWERCASE; ++ break; ++ case Opt_iocharset: ++ if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) ++ kfree(ASFS_SB(sb)->iocharset); ++ ASFS_SB(sb)->iocharset = match_strdup(&args[0]); ++ if (!ASFS_SB(sb)->iocharset) ++ return 0; ++ break; ++ case Opt_codepage: ++ if (ASFS_SB(sb)->codepage != asfs_default_codepage) ++ kfree(ASFS_SB(sb)->codepage); ++ ASFS_SB(sb)->codepage = match_strdup(&args[0]); ++ if (!ASFS_SB(sb)->codepage) ++ return 0; ++ case Opt_ignore: ++ /* Silently ignore the quota options */ ++ break; ++ default: ++no_arg: ++ printk("ASFS: Unrecognized mount option \"%s\" " ++ "or missing value\n", p); ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++static int asfs_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct asfs_sb_info *sbi; ++ struct buffer_head *bh; ++ struct fsRootBlock *rootblock; ++ struct inode *rootinode; ++ ++ sbi = kmalloc(sizeof(struct asfs_sb_info), GFP_KERNEL); ++ if (!sbi) ++ return -ENOMEM; ++ sb->s_fs_info = sbi; ++ ++ /* Fill in defaults */ ++ ASFS_SB(sb)->uid = ASFS_DEFAULT_UID; ++ ASFS_SB(sb)->gid = ASFS_DEFAULT_GID; ++ ASFS_SB(sb)->mode = ASFS_DEFAULT_MODE; ++ ASFS_SB(sb)->prefix = NULL; ++ ASFS_SB(sb)->root_volume = NULL; ++ ASFS_SB(sb)->flags = 0; ++ ASFS_SB(sb)->iocharset = asfs_default_iocharset; ++ ASFS_SB(sb)->codepage = asfs_default_codepage; ++ ++ if (!asfs_parse_options(data, sb)) { ++ printk(KERN_ERR "ASFS: Error parsing options\n"); ++ return -EINVAL; ++ } ++ ++ if (!sb_set_blocksize(sb, 512)) ++ return -EINVAL; ++ sb->s_maxbytes = ASFS_MAXFILESIZE; ++ ++ bh = sb_bread(sb, 0); ++ if (!bh) { ++ printk(KERN_ERR "ASFS: unable to read superblock\n"); ++ return -EINVAL; ++ } ++ ++ rootblock = (struct fsRootBlock *)bh->b_data; ++ ++ if (be32_to_cpu(rootblock->bheader.id) == ASFS_ROOTID && ++ be16_to_cpu(rootblock->version) == ASFS_STRUCTURE_VERISON) { ++ ++ sb->s_blocksize = be32_to_cpu(rootblock->blocksize); ++ ASFS_SB(sb)->totalblocks = be32_to_cpu(rootblock->totalblocks); ++ ASFS_SB(sb)->rootobjectcontainer = be32_to_cpu(rootblock->rootobjectcontainer); ++ ASFS_SB(sb)->extentbnoderoot = be32_to_cpu(rootblock->extentbnoderoot); ++ ASFS_SB(sb)->objectnoderoot = be32_to_cpu(rootblock->objectnoderoot); ++ ASFS_SB(sb)->flags |= 0xff & rootblock->bits; ++ ASFS_SB(sb)->adminspacecontainer = be32_to_cpu(rootblock->adminspacecontainer); ++ ASFS_SB(sb)->bitmapbase = be32_to_cpu(rootblock->bitmapbase); ++ ASFS_SB(sb)->blocks_inbitmap = (sb->s_blocksize - sizeof(struct fsBitmap))<<3; /* must be a multiple of 32 !! */ ++ ASFS_SB(sb)->blocks_bitmap = (ASFS_SB(sb)->totalblocks + ASFS_SB(sb)->blocks_inbitmap - 1) / ASFS_SB(sb)->blocks_inbitmap; ++ ASFS_SB(sb)->block_rovingblockptr = 0; ++ asfs_brelse(bh); ++ ++ if (!sb_set_blocksize(sb, sb->s_blocksize)) { ++ printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but blocksize %ld is not supported!\n", \ ++ sb->s_id, sb->s_blocksize); ++ return -EINVAL; ++ } ++ ++ bh = sb_bread(sb, 0); ++ if (!bh) { ++ printk(KERN_ERR "ASFS: unable to read superblock\n"); ++ goto out; ++ } ++ rootblock = (struct fsRootBlock *)bh->b_data; ++ ++ if (asfs_check_block((void *)rootblock, sb->s_blocksize, 0, ASFS_ROOTID)) { ++#ifdef CONFIG_ASFS_RW ++ struct buffer_head *tmpbh; ++ if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) { ++ struct fsRootInfo *ri = (struct fsRootInfo *)((u8 *)tmpbh->b_data + sb->s_blocksize - sizeof(struct fsRootInfo)); ++ ASFS_SB(sb)->freeblocks = be32_to_cpu(ri->freeblocks); ++ asfs_brelse(tmpbh); ++ } else ++ ASFS_SB(sb)->freeblocks = 0; ++ ++ if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->rootobjectcontainer+2, ASFS_TRANSACTIONFAILURE_ID))) { ++ printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but it has unfinished transaction. Mounting read-only.\n", sb->s_id); ++ ASFS_SB(sb)->flags |= ASFS_READONLY; ++ asfs_brelse(tmpbh); ++ } ++ ++ if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)->totalblocks-1, ASFS_ROOTID)) == NULL) { ++ printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but there is no second RootBlock! Mounting read-only.\n", sb->s_id); ++ ASFS_SB(sb)->flags |= ASFS_READONLY; ++ asfs_brelse(tmpbh); ++ } ++ if (!(ASFS_SB(sb)->flags & ASFS_READONLY)) ++ printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id); ++#else ++ ASFS_SB(sb)->freeblocks = 0; ++ ASFS_SB(sb)->flags |= ASFS_READONLY; ++ printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb->s_id); ++#endif ++ } else { ++ if (!silent) ++ printk(KERN_ERR "VFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \ ++ sb->s_id); ++ goto out; ++ } ++ } else { ++ if (!silent) ++ printk(KERN_ERR "VFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \ ++ sb->s_id); ++ goto out; ++ } ++ ++ asfs_brelse(bh); ++ ++ sb->s_magic = ASFS_MAGIC; ++ sb->s_flags |= MS_NODEV | MS_NOSUID; ++ if (ASFS_SB(sb)->flags & ASFS_READONLY) ++ sb->s_flags |= MS_RDONLY; ++ sb->s_op = &asfs_ops; ++ asfs_debug("Case sensitive: %s\n", (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) ? "yes" : "no"); ++ ++ if (ASFS_SB(sb)->codepage[0] != '\0' && strcmp(ASFS_SB(sb)->codepage, "none") != 0) { ++ ASFS_SB(sb)->nls_disk = load_nls(ASFS_SB(sb)->codepage); ++ if (!ASFS_SB(sb)->nls_disk) { ++ printk(KERN_ERR "ASFS: codepage %s not found\n", ASFS_SB(sb)->codepage); ++ return -EINVAL; ++ } ++ ASFS_SB(sb)->nls_io = load_nls(ASFS_SB(sb)->iocharset); ++ if (!ASFS_SB(sb)->nls_io) { ++ printk(KERN_ERR "ASFS: IO charset %s not found\n", ASFS_SB(sb)->iocharset); ++ goto out2; ++ } ++ } else { ++ ASFS_SB(sb)->nls_io = NULL; ++ ASFS_SB(sb)->nls_disk = NULL; ++ } ++ ++ if ((rootinode = asfs_get_root_inode(sb))) { ++ if ((sb->s_root = d_alloc_root(rootinode))) { ++ sb->s_root->d_op = &asfs_dentry_operations; ++ return 0; ++ } ++ iput(rootinode); ++ } ++ unload_nls(ASFS_SB(sb)->nls_io); ++out2: ++ unload_nls(ASFS_SB(sb)->nls_disk); ++ return -EINVAL; ++ ++out: ++ asfs_brelse(bh); ++ return -EINVAL; ++ ++} ++ ++#ifdef CONFIG_ASFS_RW ++int asfs_remount(struct super_block *sb, int *flags, char *data) ++{ ++ asfs_debug("ASFS: remount (flags=0x%x, opts=\"%s\")\n",*flags,data); ++ ++ if (!asfs_parse_options(data,sb)) ++ return -EINVAL; ++ ++ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) ++ return 0; ++ ++ if (*flags & MS_RDONLY) { ++ sb->s_flags |= MS_RDONLY; ++ } else if (!(ASFS_SB(sb)->flags & ASFS_READONLY)) { ++ sb->s_flags &= ~MS_RDONLY; ++ } else { ++ printk("VFS: Can't remount Amiga SFS on dev %s read/write because of errors.", sb->s_id); ++ return -EINVAL; ++ } ++ return 0; ++} ++#endif ++ ++void asfs_put_super(struct super_block *sb) ++{ ++ struct asfs_sb_info *sbi = ASFS_SB(sb); ++ ++ if (ASFS_SB(sb)->prefix) ++ kfree(ASFS_SB(sb)->prefix); ++ if (ASFS_SB(sb)->root_volume) ++ kfree(ASFS_SB(sb)->root_volume); ++ if (ASFS_SB(sb)->nls_disk) ++ unload_nls(ASFS_SB(sb)->nls_disk); ++ if (ASFS_SB(sb)->nls_io) ++ unload_nls(ASFS_SB(sb)->nls_io); ++ if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) ++ kfree(ASFS_SB(sb)->iocharset); ++ if (ASFS_SB(sb)->codepage != asfs_default_codepage) ++ kfree(ASFS_SB(sb)->codepage); ++ ++ kfree(sbi); ++ sb->s_fs_info = NULL; ++ return; ++} ++ ++/* That's simple too. */ ++int asfs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ buf->f_type = ASFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_bfree = buf->f_bavail = ASFS_SB(sb)->freeblocks; ++ buf->f_blocks = ASFS_SB(sb)->totalblocks; ++ buf->f_namelen = ASFS_MAXFN; ++ return 0; ++} ++ ++/* --- new in 2.6.x --- */ ++static kmem_cache_t * asfs_inode_cachep; ++ ++struct inode *asfs_alloc_inode(struct super_block *sb) ++{ ++ struct asfs_inode_info *ei; ++ ei = (struct asfs_inode_info *)kmem_cache_alloc(asfs_inode_cachep, SLAB_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++void asfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(asfs_inode_cachep, ASFS_I(inode)); ++} ++ ++static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ++{ ++ struct asfs_inode_info *ei = (struct asfs_inode_info *) foo; ++ ++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == ++ SLAB_CTOR_CONSTRUCTOR) { ++ inode_init_once(&ei->vfs_inode); ++ } ++} ++ ++static int init_inodecache(void) ++{ ++ asfs_inode_cachep = kmem_cache_create("asfs_inode_cache", ++ sizeof(struct asfs_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ init_once, NULL); ++ if (asfs_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void destroy_inodecache(void) ++{ ++ if (kmem_cache_destroy(asfs_inode_cachep)) ++ printk(KERN_INFO "asfs_inode_cache: not all structures were freed\n"); ++} ++ ++static struct super_block *asfs_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, asfs_fill_super); ++} ++ ++static struct file_system_type asfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "asfs", ++ .get_sb = asfs_get_sb, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++ ++static int __init init_asfs_fs(void) ++{ ++ int err = init_inodecache(); ++ if (err) ++ goto out1; ++ err = register_filesystem(&asfs_fs_type); ++ if (err) ++ goto out; ++ return 0; ++out: ++ destroy_inodecache(); ++out1: ++ return err; ++} ++ ++static void __exit exit_asfs_fs(void) ++{ ++ unregister_filesystem(&asfs_fs_type); ++ destroy_inodecache(); ++} ++ ++/* Yes, works even as a module... :) */ ++ ++#ifdef CONFIG_ASFS_RW ++MODULE_DESCRIPTION("Amiga Smart File System (read/write) support for Linux kernel 2.6.x v" ASFS_VERSION); ++#else ++MODULE_DESCRIPTION("Amiga Smart File System (read-only) support for Linux kernel 2.6.x v" ASFS_VERSION); ++#endif ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Marek Szyprowski "); ++ ++module_init(init_asfs_fs) ++module_exit(exit_asfs_fs) +diff -aurN a/fs/asfs/symlink.c b/fs/asfs/symlink.c +--- a/fs/asfs/symlink.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/fs/asfs/symlink.c 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,235 @@ ++/* ++ * ++ * Amiga Smart File System, Linux implementation ++ * version: 1.0beta9 ++ * ++ * Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asfs_fs.h" ++ ++#include ++#include ++ ++int asfs_symlink_readpage(struct file *file, struct page *page) ++{ ++ struct buffer_head *bh; ++ struct fsSoftLink *slinkcont; ++ struct inode *inode = page->mapping->host; ++ struct super_block *sb = inode->i_sb; ++ struct nls_table *nls_io = ASFS_SB(sb)->nls_io; ++ struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; ++ char *link = kmap(page); ++ int i = 0, j = 0; ++ char c, lc = 0, *prefix, *lf, *p; ++ wchar_t uni; ++ int clen; ++ ++ if (!(bh = asfs_breadcheck(sb, ASFS_I(inode)->firstblock, ASFS_SOFTLINK_ID))) { ++ SetPageError(page); ++ kunmap(page); ++ unlock_page(page); ++ return -EIO; ++ } ++ slinkcont = (struct fsSoftLink *) bh->b_data; ++ ++ lf = slinkcont->string; ++ prefix = ASFS_SB(sb)->prefix ? ASFS_SB(sb)->prefix : "/"; ++ ++ if ((p = strchr(lf,':'))) { /* Handle assign or volume name */ ++ if (ASFS_SB(sb)->root_volume && ++ strncmp(lf, ASFS_SB(sb)->root_volume, strlen(ASFS_SB(sb)->root_volume)) == 0) { ++ /* global root volume name found */ ++ link[i++] = '/'; ++ lf = p+1; ++ } else { ++ /* adding volume prefix */ ++ while (i < 1023 && (c = prefix[i])) ++ link[i++] = c; ++ while (i < 1023 && lf[j] != ':') ++ { ++ c = lf[j++]; ++ if (ASFS_SB(sb)->flags & ASFS_VOL_LOWERCASE) ++ c = asfs_lowerchar(c); ++ if (nls_io) ++ { ++ clen = nls_disk->char2uni(&c, 1, &uni); ++ if (clen>0) { ++ clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); ++ if (clen>0) ++ i += clen; ++ } ++ if (clen<0) ++ link[i++] = '?'; ++ } else ++ link[i++] = c; ++ } ++ if (i < 1023) ++ link[i++] = '/'; ++ j++; ++ } ++ lc = '/'; ++ } ++ ++ while (i < 1023 && (c = lf[j])) { ++ if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ ++ link[i++] = '.'; ++ link[i++] = '.'; ++ } ++ lc = c; ++ if (nls_io) ++ { ++ clen = nls_disk->char2uni(&c, 1, &uni); ++ if (clen>0) { ++ clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); ++ if (clen>0) ++ i += clen; ++ } ++ if (clen<0) ++ link[i++] = '?'; ++ } else ++ link[i++] = c; ++ j++; ++ } ++ link[i] = '\0'; ++ SetPageUptodate(page); ++ kunmap(page); ++ unlock_page(page); ++ asfs_brelse(bh); ++ return 0; ++} ++ ++#ifdef CONFIG_ASFS_RW ++ ++int asfs_write_symlink(struct inode *symfile, const char *symname) ++{ ++ struct super_block *sb = symfile->i_sb; ++ struct buffer_head *bh; ++ struct fsSoftLink *slinkcont; ++ struct nls_table *nls_io = ASFS_SB(sb)->nls_io; ++ struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; ++ char *p, c, lc; ++ int i, maxlen, pflen; ++ wchar_t uni; ++ int clen, blen; ++ ++ asfs_debug("asfs_write_symlink %s to node %d\n", symname, (int)symfile->i_ino); ++ ++ if (!(bh = asfs_breadcheck(sb, ASFS_I(symfile)->firstblock, ASFS_SOFTLINK_ID))) { ++ unlock_super(sb); ++ return -EIO; ++ } ++ slinkcont = (struct fsSoftLink *) bh->b_data; ++ ++ /* translating symlink target path */ ++ ++ maxlen = sb->s_blocksize - sizeof(struct fsSoftLink) - 2; ++ i = 0; ++ p = slinkcont->string; ++ lc = '/'; ++ ++ if (*symname == '/') { ++ while (*symname == '/') ++ symname++; ++ if (ASFS_SB(sb)->prefix && ++ strncmp(symname-1, ASFS_SB(sb)->prefix, (pflen = strlen(ASFS_SB(sb)->prefix))) == 0) { ++ /* found volume prefix, ommiting it */ ++ symname += pflen; ++ blen = strlen(symname); ++ while (*symname != '/' && *symname != '\0') { ++ clen = nls_io->char2uni(symname, blen, &uni); ++ if (clen>0) { ++ symname += clen; ++ blen -= clen; ++ clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); ++ if (clen>0) ++ p += clen; ++ } ++ else ++ { ++ symname++; ++ blen--; ++ } ++ if (clen<0) ++ *p++ = '?'; ++ i++; ++ } ++ symname++; ++ *p++ = ':'; ++ } else if (ASFS_SB(sb)->root_volume) { /* adding root volume name */ ++ while (ASFS_SB(sb)->root_volume[i]) ++ *p++ = ASFS_SB(sb)->root_volume[i++]; ++ *p++ = ':'; ++ } else { /* do nothing */ ++ *p++ = '/'; ++ } ++ i++; ++ } ++ ++ blen = strlen(symname); ++ while (i < maxlen && (c = *symname)) { ++ if (c == '.' && lc == '/' && symname[1] == '.' && symname[2] == '/') { ++ *p++ = '/'; ++ i++; ++ symname += 3; ++ blen -= 3; ++ lc = '/'; ++ } else if (c == '.' && lc == '/' && symname[1] == '/') { ++ symname += 2; ++ blen -= 2; ++ lc = '/'; ++ } else { ++ clen = nls_io->char2uni(symname, blen, &uni); ++ if (clen>0) { ++ symname += clen; ++ blen -= clen; ++ clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); ++ if (clen>0) ++ lc = *p; ++ p += clen; ++ } ++ else ++ { ++ symname++; ++ blen--; ++ } ++ if (clen<0) ++ { ++ *p++ = '?'; ++ lc = '?'; ++ } ++ i++; ++ } ++ if (lc == '/') ++ while (*symname == '/') ++ { ++ symname++; ++ blen--; ++ } ++ } ++ *p = 0; ++ ++ asfs_bstore(sb, bh); ++ asfs_brelse(bh); ++ ++ unlock_super(sb); ++ ++ return 0; ++} ++ ++#endif +diff -aurN a/include/linux/amigasfs.h b/include/linux/amigasfs.h +--- a/include/linux/amigasfs.h 1969-12-31 19:00:00.000000000 -0500 ++++ b/include/linux/amigasfs.h 2005-06-15 22:05:09.000000000 -0400 +@@ -0,0 +1,276 @@ ++#ifndef __LINUX_AMIGASFS_H ++#define __LINUX_AMIGASFS_H ++ ++#include ++ ++/* some helper macros... */ ++#define ASFS_MAKE_ID(a,b,c,d) (((a)&0xff)<<24|((b)&0xff)<<16|((c)&0xff)<<8|((d)&0xff)) ++ ++/* Amiga SFS block IDs */ ++#define ASFS_ROOTID ASFS_MAKE_ID('S','F','S','\0') ++#define ASFS_OBJECTCONTAINER_ID ASFS_MAKE_ID('O','B','J','C') ++#define ASFS_BNODECONTAINER_ID ASFS_MAKE_ID('B','N','D','C') ++#define ASFS_NODECONTAINER_ID ASFS_MAKE_ID('N','D','C',' ') ++#define ASFS_HASHTABLE_ID ASFS_MAKE_ID('H','T','A','B') ++#define ASFS_SOFTLINK_ID ASFS_MAKE_ID('S','L','N','K') ++#define ASFS_ADMINSPACECONTAINER_ID ASFS_MAKE_ID('A','D','M','C') ++#define ASFS_BITMAP_ID ASFS_MAKE_ID('B','T','M','P') ++#define ASFS_TRANSACTIONFAILURE_ID ASFS_MAKE_ID('T','R','F','A') ++ ++/* Amiga SFS defines and magic values */ ++ ++#define ASFS_MAGIC 0xa0ff ++#define ASFS_MAXFN (105u) ++#define ASFS_MAXFILESIZE 0x8FFFFFFE ++ ++#define ASFS_STRUCTURE_VERISON (3) ++#define ASFS_BLCKFACCURACY (5) ++ ++#define ASFS_ROOTBITS_CASESENSITIVE (128) ++#define ASFS_READONLY (512) ++#define ASFS_VOL_LOWERCASE (1024) ++ ++#define ASFS_ROOTNODE (1) ++#define ASFS_RECYCLEDNODE (2) ++ ++#define OTYPE_HIDDEN (1) ++#define OTYPE_HARDLINK (32) ++#define OTYPE_LINK (64) ++#define OTYPE_DIR (128) ++ ++#define MSB_MASK (1ul << 31) ++ ++#define NODE_STRUCT_SIZE (10) /* (sizeof(struct fsObjectNode)) */ ++#define NODECONT_BLOCK_COUNT ((sb->s_blocksize - sizeof(struct fsNodeContainer)) / sizeof(u32)) ++ ++#define ASFS_ALWAYSFREE (16) /* keep this amount of blocks free */ ++ ++#define ASFS_BLOCKCHUNKS (16) /* try to allocate this number of blocks in one request */ ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++ ++/* amigados protection bits */ ++ ++#define FIBB_SCRIPT 6 /* program is a script (execute) file */ ++#define FIBB_PURE 5 /* program is reentrant and rexecutable */ ++#define FIBB_ARCHIVE 4 /* cleared whenever file is changed */ ++#define FIBB_READ 3 /* ignored by old filesystem */ ++#define FIBB_WRITE 2 /* ignored by old filesystem */ ++#define FIBB_EXECUTE 1 /* ignored by system, used by Shell */ ++#define FIBB_DELETE 0 /* prevent file from being deleted */ ++ ++#define FIBF_SCRIPT (1<s_blocksize) - sizeof(struct fsHashTable))>>2)) ++ ++/* Each block has its own header with checksum and id, its called fsBlockHeader */ ++ ++struct fsBlockHeader { ++ u32 id; /* 4 character id string of this block */ ++ u32 checksum; /* The checksum */ ++ u32 ownblock; /* The blocknumber of the block this block is stored at */ ++}; ++ ++/* On-disk "super block", called fsRootBlock */ ++ ++struct fsRootBlock { ++ struct fsBlockHeader bheader; ++ ++ u16 version; /* Version number of the filesystem block structure */ ++ u16 sequencenumber; /* The Root with the highest sequencenumber is valid */ ++ ++ u32 datecreated; /* Creation date (when first formatted). Cannot be changed. */ ++ u8 bits; /* various settings, see defines below. */ ++ u8 pad1; ++ u16 pad2; ++ ++ u32 reserved1[2]; ++ ++ u32 firstbyteh; /* The first byte of our partition from the start of the */ ++ u32 firstbyte; /* disk. firstbyteh = upper 32 bits, firstbyte = lower 32 bits. */ ++ ++ u32 lastbyteh; /* The last byte of our partition, excluding this one. */ ++ u32 lastbyte; ++ ++ u32 totalblocks; /* size of this partition in blocks */ ++ u32 blocksize; /* blocksize used */ ++ ++ u32 reserved2[2]; ++ u32 reserved3[8]; ++ ++ u32 bitmapbase; /* location of the bitmap */ ++ u32 adminspacecontainer; /* location of first adminspace container */ ++ u32 rootobjectcontainer; /* location of the root objectcontainer */ ++ u32 extentbnoderoot; /* location of the root of the extentbnode B-tree */ ++ u32 objectnoderoot; /* location of the root of the objectnode tree */ ++ ++ u32 reserved4[3]; ++}; ++ ++/* On disk inode, called fsObject */ ++ ++struct fsObject { ++ u16 owneruid; ++ u16 ownergid; ++ u32 objectnode; ++ u32 protection; ++ ++ union { ++ struct { ++ u32 data; ++ u32 size; ++ } file; ++ ++ struct { ++ u32 hashtable; /* for directories & root, 0 means no hashblock */ ++ u32 firstdirblock; ++ } dir; ++ } object; ++ ++ u32 datemodified; ++ u8 bits; ++ ++ u8 name[0]; ++ u8 comment[0]; ++}; ++ ++/* On disk block containging a number of fsObjects */ ++ ++struct fsObjectContainer { ++ struct fsBlockHeader bheader; ++ ++ u32 parent; ++ u32 next; ++ u32 previous; /* 0 for the first block in the directory chain */ ++ ++ struct fsObject object[0]; ++}; ++ ++/* BTree structures, used to collect file data position on disk */ ++ ++struct fsExtentBNode { ++ u32 key; /* data! */ ++ u32 next; ++ u32 prev; ++ u16 blocks; /* The size in blocks of the region this Extent controls */ ++}; ++ ++struct BNode { ++ u32 key; ++ u32 data; ++}; ++ ++struct BTreeContainer { ++ u16 nodecount; ++ u8 isleaf; ++ u8 nodesize; /* Must be a multiple of 2 */ ++ ++ struct BNode bnode[0]; ++}; ++ ++/* On disk block with BTreeContainer */ ++ ++struct fsBNodeContainer { ++ struct fsBlockHeader bheader; ++ struct BTreeContainer btc; ++}; ++ ++/* On disk block with soft link data */ ++ ++struct fsSoftLink { ++ struct fsBlockHeader bheader; ++ u32 parent; ++ u32 next; ++ u32 previous; ++ u8 string[0]; ++}; ++ ++/* On disk block with hashtable data */ ++ ++struct fsHashTable { ++ struct fsBlockHeader bheader; ++ u32 parent; ++ u32 hashentry[0]; ++}; ++ ++/* On disk block with node index and some helper structures */ ++ ++struct fsNodeContainer { ++ struct fsBlockHeader bheader; ++ u32 nodenumber; ++ u32 nodes; ++ u32 node[0]; ++}; ++ ++struct fsNode { ++ u32 data; ++}; ++ ++struct fsObjectNode { ++ struct fsNode node; ++ u32 next; ++ u16 hash16; ++} __attribute__ ((packed)); ++ ++/* Some adminspace and bitmap block structures */ ++ ++struct fsAdminSpace { ++ u32 space; ++ u32 bits; ++/* Set bits are used blocks, bit 31 is the first block in the AdminSpace. */ ++}; ++ ++struct fsAdminSpaceContainer { ++ struct fsBlockHeader bheader; ++ ++ u32 next; ++ u32 previous; ++ ++ u8 bits; ++ u8 pad1; ++ u16 pad2; ++ ++ struct fsAdminSpace adminspace[0]; ++}; ++ ++struct fsBitmap { ++ struct fsBlockHeader bheader; ++ ++ u32 bitmap[0]; ++ ++/* Bits are 1 if the block is free, and 0 if full. ++ Bitmap must consist of an integral number of longwords. */ ++}; ++ ++/* The fsRootInfo structure has all kinds of information about the format ++ of the disk. */ ++ ++struct fsRootInfo { ++ u32 deletedblocks; /* Amount in blocks which deleted files consume. */ ++ u32 deletedfiles; /* Number of deleted files in recycled. */ ++ u32 freeblocks; /* Cached number of free blocks on disk. */ ++ ++ u32 datecreated; ++ ++ u32 lastallocatedblock; /* Block which was most recently allocated */ ++ u32 lastallocatedadminspace; /* AdminSpaceContainer which most recently was used to allocate a block */ ++ u32 lastallocatedextentnode; /* ExtentNode which was most recently created */ ++ u32 lastallocatedobjectnode; /* ObjectNode which was most recently created */ ++ ++ u32 rovingpointer; ++}; ++ ++#endif diff --git a/debian/patches-debian/ia64-irq-affinity-upfix.patch b/debian/patches-debian/ia64-irq-affinity-upfix.patch new file mode 100644 index 000000000..cb172a288 --- /dev/null +++ b/debian/patches-debian/ia64-irq-affinity-upfix.patch @@ -0,0 +1,19 @@ +## DP: Description: Add a no-op set_irq_affinity_info() for non-SMP +## DP: Patch author: dann frazier +## DP: Upstream status: Submitted +# +diff -aurN a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c +--- a/arch/ia64/kernel/irq.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/arch/ia64/kernel/irq.c 2005-06-15 22:13:24.000000000 -0400 +@@ -141,7 +141,10 @@ + } + } + +- ++#else /* !CONFIG_SMP */ ++void set_irq_affinity_info (unsigned int irq, int hwid, int redir) ++{ ++} + #endif /* CONFIG_SMP */ + + #ifdef CONFIG_HOTPLUG_CPU diff --git a/debian/patches-debian/modular-ide-pnp.patch b/debian/patches-debian/modular-ide-pnp.patch new file mode 100644 index 000000000..b31a9a05d --- /dev/null +++ b/debian/patches-debian/modular-ide-pnp.patch @@ -0,0 +1,75 @@ +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Allow modular built of ide-pnp +## DP: Patch author: Herbert Xu, Christoph Hellwig +## DP: Upstream status: submitted +# +diff -aurN a/drivers/ide/Kconfig b/drivers/ide/Kconfig +--- a/drivers/ide/Kconfig 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/Kconfig 2005-06-15 22:15:06.000000000 -0400 +@@ -315,7 +315,7 @@ + Otherwise say N. + + config BLK_DEV_IDEPNP +- bool "PNP EIDE support" ++ tristate "PNP EIDE support" + depends on PNP + help + If you have a PnP (Plug and Play) compatible EIDE card and +diff -aurN a/drivers/ide/Makefile b/drivers/ide/Makefile +--- a/drivers/ide/Makefile 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/Makefile 2005-06-15 22:15:06.000000000 -0400 +@@ -22,7 +22,6 @@ + ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o + ide-core-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o + ide-core-$(CONFIG_PROC_FS) += ide-proc.o +-ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o + + # built-in only drivers from arm/ + ide-core-$(CONFIG_IDE_ARM) += arm/ide_arm.o +@@ -43,6 +42,7 @@ + + obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o + obj-$(CONFIG_IDE_GENERIC) += ide-generic.o ++obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o + + obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o + obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o +diff -aurN a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c +--- a/drivers/ide/ide-pnp.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/ide-pnp.c 2005-06-15 22:15:06.000000000 -0400 +@@ -69,7 +69,21 @@ + .remove = idepnp_remove, + }; + +-void __init pnpide_init(void) ++int __init pnpide_init(void) + { +- pnp_register_driver(&idepnp_driver); ++ return pnp_register_driver(&idepnp_driver); + } ++ ++#ifdef MODULE ++static void __exit pnpide_exit(void) ++{ ++ pnp_unregister_driver(&idepnp_driver); ++} ++ ++module_init(pnpide_init); ++module_exit(pnpide_exit); ++#endif ++ ++MODULE_AUTHOR("Andrey Panin"); ++MODULE_DESCRIPTION("Enabler for ISAPNP IDE devices"); ++MODULE_LICENSE("GPL"); +diff -aurN a/drivers/ide/ide.c b/drivers/ide/ide.c +--- a/drivers/ide/ide.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/ide.c 2005-06-15 22:15:06.000000000 -0400 +@@ -1789,7 +1789,7 @@ + return 1; + } + +-extern void pnpide_init(void); ++extern int pnpide_init(void); + extern void h8300_ide_init(void); + + /* diff --git a/debian/patches-debian/modular-ide.patch b/debian/patches-debian/modular-ide.patch new file mode 100644 index 000000000..e85a6eedc --- /dev/null +++ b/debian/patches-debian/modular-ide.patch @@ -0,0 +1,126 @@ +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: fix IDE modularisation +## DP: Patch author: Herbert Xu +## DP: Upstream status: rejected +# +diff -aurN a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c +--- a/drivers/ide/ide-generic.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/ide-generic.c 2005-06-15 22:16:56.000000000 -0400 +@@ -11,9 +11,14 @@ + #include + #include + #include ++#include + + static int __init ide_generic_init(void) + { ++#ifdef CONFIG_BLK_DEV_IDEPCI ++ ide_scan_pcibus(); ++#endif ++ + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) + ide_get_lock(NULL, NULL); /* for atari only */ + +diff -aurN a/drivers/ide/ide.c b/drivers/ide/ide.c +--- a/drivers/ide/ide.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/ide.c 2005-06-15 22:16:56.000000000 -0400 +@@ -175,10 +175,11 @@ + static int initializing; /* set while initializing built-in drivers */ + + DECLARE_MUTEX(ide_cfg_sem); ++EXPORT_SYMBOL(ide_cfg_sem); + __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock); + + #ifdef CONFIG_BLK_DEV_IDEPCI +-static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ ++int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ + #endif + + #ifdef CONFIG_IDEDMA_AUTO +@@ -1418,6 +1419,8 @@ + + EXPORT_SYMBOL(generic_ide_ioctl); + ++EXPORT_SYMBOL(ide_add_generic_settings); ++ + /* + * stridx() returns the offset of c within s, + * or -1 if c is '\0' or not found within s. +@@ -1797,9 +1800,9 @@ + */ + static void __init probe_for_hwifs (void) + { +-#ifdef CONFIG_BLK_DEV_IDEPCI +- ide_scan_pcibus(ide_scan_direction); +-#endif /* CONFIG_BLK_DEV_IDEPCI */ ++#if defined(CONFIG_BLK_DEV_IDEPCI) && !defined(MODULE) ++ ide_scan_pcibus(); ++#endif /* CONFIG_BLK_DEV_IDEPCI && !MODULE */ + + #ifdef CONFIG_ETRAX_IDE + { +diff -aurN a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c +--- a/drivers/ide/setup-pci.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/ide/setup-pci.c 2005-06-15 22:16:56.000000000 -0400 +@@ -837,7 +837,7 @@ + * boot up the pci layer takes over the job. + */ + +-static int __init ide_scan_pcidev(struct pci_dev *dev) ++static int ide_scan_pcidev(struct pci_dev *dev) + { + struct list_head *l; + struct pci_driver *d; +@@ -863,21 +863,23 @@ + + /** + * ide_scan_pcibus - perform the initial IDE driver scan +- * @scan_direction: set for reverse order scanning + * + * Perform the initial bus rather than driver ordered scan of the + * PCI drivers. After this all IDE pci handling becomes standard + * module ordering not traditionally ordered. + */ + +-void __init ide_scan_pcibus (int scan_direction) ++void ide_scan_pcibus(void) + { + struct pci_dev *dev = NULL; + struct pci_driver *d; + struct list_head *l, *n; + ++ if (!pre_init) ++ return; ++ + pre_init = 0; +- if (!scan_direction) { ++ if (!ide_scan_direction) { + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + ide_scan_pcidev(dev); + } +@@ -899,3 +901,5 @@ + pci_register_driver(d); + } + } ++ ++EXPORT_SYMBOL_GPL(ide_scan_pcibus); +diff -aurN a/include/linux/ide.h b/include/linux/ide.h +--- a/include/linux/ide.h 2005-06-06 11:22:29.000000000 -0400 ++++ b/include/linux/ide.h 2005-06-15 22:16:56.000000000 -0400 +@@ -1115,6 +1115,7 @@ + extern ide_hwif_t ide_hwifs[]; /* master data repository */ + #endif + extern int noautodma; ++extern int ide_scan_direction; + + extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs); + extern int __ide_end_request (ide_drive_t *drive, struct request *rq, int uptodate, int nrsecs); +@@ -1323,7 +1324,7 @@ + + extern int ideprobe_init(void); + +-extern void ide_scan_pcibus(int scan_direction) __init; ++extern void ide_scan_pcibus(void); + extern int ide_pci_register_driver(struct pci_driver *driver); + extern void ide_pci_unregister_driver(struct pci_driver *driver); + void ide_pci_setup_ports(struct pci_dev *, struct ide_pci_device_s *, int, ata_index_t *); diff --git a/debian/patches-debian/modular-vesafb.patch b/debian/patches-debian/modular-vesafb.patch new file mode 100644 index 000000000..58f5b8865 --- /dev/null +++ b/debian/patches-debian/modular-vesafb.patch @@ -0,0 +1,197 @@ +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: modularised VESA FB +## DP: Patch author: initial author unknown, fixed up by Andres Salomon +## DP: Upstream status: not submitted +# +diff -aurN a/drivers/video/Kconfig b/drivers/video/Kconfig +--- a/drivers/video/Kconfig 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/video/Kconfig 2005-06-15 22:32:51.000000000 -0400 +@@ -477,7 +477,7 @@ + cards. Say Y if you have one of those. + + config FB_VESA +- bool "VESA VGA graphics support" ++ tristate "VESA VGA graphics support" + depends on (FB = y) && (X86 || X86_64) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA +diff -aurN a/drivers/video/vesafb.c b/drivers/video/vesafb.c +--- a/drivers/video/vesafb.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/drivers/video/vesafb.c 2005-06-15 22:33:47.000000000 -0400 +@@ -25,6 +25,12 @@ + #define dac_reg (0x3c8) + #define dac_val (0x3c9) + ++struct vesafb_info ++{ ++ u32 pseudo_palette[256]; ++ int mtrr_hdl; ++}; ++ + /* --------------------------------------------------------------------- */ + + static struct fb_var_screeninfo vesafb_defined __initdata = { +@@ -44,17 +50,34 @@ + .accel = FB_ACCEL_NONE, + }; + +-static int inverse = 0; +-static int mtrr = 1; ++static int mtrr __initdata = 1; + static int vram_remap __initdata = 0; /* Set amount of memory to be used */ + static int vram_total __initdata = 0; /* Set total amount of memory */ + static int pmi_setpal = 0; /* pmi for palette changes ??? */ ++static int redraw __initdata = 0; + static int ypan = 0; /* 0..nothing, 1..ypan, 2..ywrap */ ++static int ywrap __initdata = 0; + static unsigned short *pmi_base = NULL; + static void (*pmi_start)(void); + static void (*pmi_pal)(void); + static int depth; + ++module_param(redraw, bool, 0); ++module_param(ypan, bool, 0); ++module_param(ywrap, bool, 0); ++module_param_named(vgapal, pmi_setpal, invbool, 0); ++MODULE_PARM_DESC(vgapal, "Use VGA for setting palette (default)"); ++module_param_named(pmipal, pmi_setpal, bool, 0); ++MODULE_PARM_DESC(pmipal, "Use PMI for setting palette"); ++module_param(mtrr, bool, 0); ++MODULE_PARM_DESC(mtrr, "Enable MTRR support (default)"); ++module_param_named(nomtrr, mtrr, invbool, 0); ++MODULE_PARM_DESC(nomtrr, "Disable MTRR support"); ++module_param(vram_remap, int, 0); ++MODULE_PARM_DESC(vram_remap, "Set total amount of memory to be used"); ++module_param(vram_total, int, 0); ++MODULE_PARM_DESC(vram_total, "Total amount of memory"); ++ + /* --------------------------------------------------------------------- */ + + static int vesafb_pan_display(struct fb_var_screeninfo *var, +@@ -182,6 +205,7 @@ + .fb_cursor = soft_cursor, + }; + ++#ifndef MODULE + static int __init vesafb_setup(char *options) + { + char *this_opt; +@@ -192,9 +216,7 @@ + while ((this_opt = strsep(&options, ",")) != NULL) { + if (!*this_opt) continue; + +- if (! strcmp(this_opt, "inverse")) +- inverse=1; +- else if (! strcmp(this_opt, "redraw")) ++ if (! strcmp(this_opt, "redraw")) + ypan=0; + else if (! strcmp(this_opt, "ypan")) + ypan=1; +@@ -215,11 +237,13 @@ + } + return 0; + } ++#endif + + static int __init vesafb_probe(struct device *device) + { + struct platform_device *dev = to_platform_device(device); + struct fb_info *info; ++ struct vesafb_info *vfb_info; + int i, err; + unsigned int size_vmode; + unsigned int size_remap; +@@ -277,13 +301,14 @@ + spaces our resource handlers simply don't know about */ + } + +- info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev); ++ info = framebuffer_alloc(sizeof(struct vesafb_info), &dev->dev); + if (!info) { + release_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len); + return -ENOMEM; + } +- info->pseudo_palette = info->par; +- info->par = NULL; ++ vfb_info = (struct vesafb_info *) info->par; ++ vfb_info->mtrr_hdl = -1; ++ info->pseudo_palette = vfb_info->pseudo_palette; + + info->screen_base = ioremap(vesafb_fix.smem_start, vesafb_fix.smem_len); + if (!info->screen_base) { +@@ -392,7 +417,7 @@ + temp_size &= (temp_size - 1); + + /* Try and find a power of two to add */ +- while (temp_size && mtrr_add(vesafb_fix.smem_start, temp_size, MTRR_TYPE_WRCOMB, 1)==-EINVAL) { ++ while (temp_size && (vfb_info->mtrr_hdl = mtrr_add(vesafb_fix.smem_start, temp_size, MTRR_TYPE_WRCOMB, 1)) == -EINVAL) { + temp_size >>= 1; + } + } +@@ -414,6 +439,7 @@ + } + printk(KERN_INFO "fb%d: %s frame buffer device\n", + info->node, info->fix.id); ++ dev_set_drvdata(device, info); + return 0; + err: + framebuffer_release(info); +@@ -421,10 +447,24 @@ + return err; + } + ++static void __exit vesafb_remove(struct device *device) ++{ ++ struct fb_info *info = dev_get_drvdata(device); ++ struct vesafb_info *vfb_info = (struct vesafb_info *) info->par; ++ ++ unregister_framebuffer(info); ++ if (vfb_info->mtrr_hdl >= 0) ++ mtrr_del(vfb_info->mtrr_hdl, 0, 0); ++ iounmap(info->screen_base); ++ framebuffer_release(info); ++ release_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len); ++} ++ + static struct device_driver vesafb_driver = { + .name = "vesafb", + .bus = &platform_bus_type, + .probe = vesafb_probe, ++ .remove = vesafb_remove, + }; + + static struct platform_device vesafb_device = { +@@ -434,11 +474,18 @@ + static int __init vesafb_init(void) + { + int ret; ++#ifndef MODULE + char *option = NULL; + + /* ignore error return of fb_get_options */ + fb_get_options("vesafb", &option); + vesafb_setup(option); ++#else ++ if (redraw) ++ ypan = 0; ++ if (ywrap) ++ ypan = 2; ++#endif + ret = driver_register(&vesafb_driver); + + if (!ret) { +@@ -448,6 +495,14 @@ + } + return ret; + } ++ ++static void __exit vesafb_exit(void) ++{ ++ platform_device_unregister(&vesafb_device); ++ driver_unregister(&vesafb_driver); ++} ++ + module_init(vesafb_init); ++module_exit(vesafb_exit); + + MODULE_LICENSE("GPL"); diff --git a/debian/patches-debian/powerpc-calibrate-tau.patch b/debian/patches-debian/powerpc-calibrate-tau.patch new file mode 100644 index 000000000..9e1fa701a --- /dev/null +++ b/debian/patches-debian/powerpc-calibrate-tau.patch @@ -0,0 +1,69 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Adds support TAU calibration on G3 processors +## DP: Patch author: Nicolas DET +## DP: Upstream status: submitted + +diff -aurN a/arch/ppc/Kconfig b/arch/ppc/Kconfig +--- a/arch/ppc/Kconfig 2005-06-06 11:22:29.000000000 -0400 ++++ b/arch/ppc/Kconfig 2005-06-17 20:48:38.000000000 -0400 +@@ -198,6 +198,24 @@ + + If in doubt, say N here. + ++config TAU_CALIBRATED ++ bool "The CPU sensor has been calibrated." ++ depends on TAU ++ help ++ Enable it you got the real temperature with an external sensor ++ and you know the offset between the one advertised by the CPU ++ and the real one ++ ++config TAU_CALIBRATED_VALUE ++ int "Offset of the themal sensor" ++ depends on TAU_CALIBRATED ++ default "0" ++ help ++ This is the offset of the thermal sensor compare to the real value ++ For example, if you get 27°C in /proc/cpuinfo (uncalibrated) and ++ you know real one is 53°C, then you should set 26 as offset. ++ value = Real val - CPU val; ++ + config MATH_EMULATION + bool "Math emulation" + depends on 4xx || 8xx || E500 +diff -aurN a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c +--- a/arch/ppc/kernel/setup.c 2005-06-06 11:22:29.000000000 -0400 ++++ b/arch/ppc/kernel/setup.c 2005-06-17 20:50:14.000000000 -0400 +@@ -201,17 +201,25 @@ + + #ifdef CONFIG_TAU + if (cur_cpu_spec[i]->cpu_features & CPU_FTR_TAU) { ++#ifdef CONFIG_TAU_CALIBRATED ++ int is_calibrated = 1; ++ int temp_offset = CONFIG_TAU_CALIBRATED_VALUE; ++#else ++ int is_calibrated = 0; ++ int temp_offset = 0; ++#endif + #ifdef CONFIG_TAU_AVERAGE + /* more straightforward, but potentially misleading */ +- seq_printf(m, "temperature \t: %u C (uncalibrated)\n", +- cpu_temp(i)); ++ seq_printf(m, "temperature \t: %u C %s- average\n", ++ cpu_temp(i) + temp_offset, is_calibrated ? "" : "(uncalibrated) " ); + #else + /* show the actual temp sensor range */ + u32 temp; + temp = cpu_temp_both(i); +- seq_printf(m, "temperature \t: %u-%u C (uncalibrated)\n", +- temp & 0xff, temp >> 16); +-#endif ++ seq_printf(m, "temperature \t: %u-%u C %s\n", ++ (temp & 0xff) + temp_offset, (temp >> 16) + temp_offset, is_calibrated ? "" : "(uncalibrated)" ); ++ ++#endif /* CONFIG_TAU_AVERAGE */ + } + #endif /* CONFIG_TAU */ + diff --git a/debian/patches-debian/powerpc-fix-power3-ftbfs.patch b/debian/patches-debian/powerpc-fix-power3-ftbfs.patch new file mode 100644 index 000000000..f6fac482a --- /dev/null +++ b/debian/patches-debian/powerpc-fix-power3-ftbfs.patch @@ -0,0 +1,27 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Works around a broken build system, namely the +## DP: Description: simple bootloader on power 3/4. +## DP: Misc: Pulled from the ubuntu tree. +## DP: Patch author: fabbione@ubuntu.com +## DP: Upstream status: FTBFS fix, guess it will be fixed upstream too + +. $(dirname $0)/DPATCH + +@DPATCH@ +diff -urNad linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c /usr/src/dpatchtemp/dpep.cQRwcC/linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c +--- linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c 2004-12-24 22:33:51.000000000 +0100 ++++ /usr/src/dpatchtemp/dpep.cQRwcC/linux-source-2.6.10-2.6.10/arch/ppc/boot/simple/misc-prep.c 2004-12-28 10:43:29.838010536 +0100 +@@ -152,9 +152,11 @@ + hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; + } + ++#if defined(CONFIG_6xx) + /* Now go and clear out the BATs and ensure that our MSR is + * correct .*/ + disable_6xx_mmu(); ++#endif + + /* Make r3 be a pointer to the residual data. */ + return (unsigned long)hold_residual; diff --git a/debian/patches-debian/powerpc-g3-750cxe.patch b/debian/patches-debian/powerpc-g3-750cxe.patch new file mode 100644 index 000000000..f50cb5e4f --- /dev/null +++ b/debian/patches-debian/powerpc-g3-750cxe.patch @@ -0,0 +1,68 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Adds support for 750CXe rev 3.1, prv 0008 3311 (previously recognized as 745/755). +## DP: Patch author: Nicolas DET +## DP: Upstream status: submitted + +. $(dirname $0)/DPATCH + +@DPATCH@ +--- linux-2.6.11.orig/arch/ppc/kernel/cputable.c 2005-03-02 08:38:09.000000000 +0100 ++++ linux-2.6.11_nico/arch/ppc/kernel/cputable.c 2005-03-04 15:39:11.032975088 +0100 +@@ -198,20 +198,6 @@ + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750 + }, +- { /* 745/755 */ +- .pvr_mask = 0xfffff000, +- .pvr_value = 0x00083000, +- .cpu_name = "745/755", +- .cpu_features = CPU_FTR_COMMON | +- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | +- CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | +- CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, +- .cpu_user_features = COMMON_PPC, +- .icache_bsize = 32, +- .dcache_bsize = 32, +- .num_pmcs = 4, +- .cpu_setup = __setup_cpu_750 +- }, + { /* 750CX (80100 and 8010x?) */ + .pvr_mask = 0xfffffff0, + .pvr_value = 0x00080100, +@@ -254,6 +240,34 @@ + .num_pmcs = 4, + .cpu_setup = __setup_cpu_750cx + }, ++ { /* 750CXe (00082311 or 00083311) revision 3.1 / 3.1 pre_GA */ ++ .pvr_mask = 0xffff0fff, ++ .pvr_value = 0x00080311, ++ .cpu_name = "750CXe rev 3.1", ++ .cpu_features = CPU_FTR_COMMON | ++ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | ++ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | ++ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, ++ .cpu_user_features = COMMON_PPC, ++ .icache_bsize = 32, ++ .dcache_bsize = 32, ++ .num_pmcs = 4, ++ .cpu_setup = __setup_cpu_750cx ++ }, ++ { /* 745/755 */ ++ .pvr_mask = 0xfffff000, ++ .pvr_value = 0x00083000, ++ .cpu_name = "745/755", ++ .cpu_features = CPU_FTR_COMMON | ++ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | ++ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | ++ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP, ++ .cpu_user_features = COMMON_PPC, ++ .icache_bsize = 32, ++ .dcache_bsize = 32, ++ .num_pmcs = 4, ++ .cpu_setup = __setup_cpu_750 ++ }, + { /* 750FX rev 1.x */ + .pvr_mask = 0xffffff00, + .pvr_value = 0x70000100, diff --git a/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch b/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch new file mode 100644 index 000000000..be9d5574b --- /dev/null +++ b/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch @@ -0,0 +1,30 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: [PATCH] ppc32: fixes FTBFS on power3/4. +## DP: This patch doesn't build pmac_cache.S on power3/power4, since it is +## DP: broken there and not needed. +## DP: Patch author: Sven Luther +## DP: Upstream status: FTBFS, submitted to linuxppc-dev and benh. + +. $(dirname $0)/DPATCH + +@DPATCH@ +--- kernel-source-2.6.11/arch/ppc/platforms/Makefile.orig 2005-03-27 11:38:25.000000000 +0200 ++++ kernel-source-2.6.11/arch/ppc/platforms/Makefile 2005-03-27 11:39:23.000000000 +0200 +@@ -9,9 +9,15 @@ + ifeq ($(CONFIG_APUS),y) + obj-$(CONFIG_PCI) += apus_pci.o + endif ++ifeq ($(CONFIG_6xx),y) + obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \ + pmac_feature.o pmac_pci.o pmac_sleep.o \ + pmac_low_i2c.o pmac_cache.o ++else ++obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \ ++ pmac_feature.o pmac_pci.o pmac_sleep.o \ ++ pmac_low_i2c.o ++endif + obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o \ + chrp_pegasos_eth.o + obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_setup.o diff --git a/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch b/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch new file mode 100644 index 000000000..68be493d4 --- /dev/null +++ b/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch @@ -0,0 +1,2868 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Enables IBM eServer i/pSeries Virtual SCSI Target Driver +## DP: Description: Needed for i/pSeries with logical partitions (LPAR). +## DP: Patch author: Dave Boutcher (boutcher@us.ibm.com) +## DP: Upstream status: unknown, sent to me by Cajus Pollmeier. + +diff -aurN a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +--- a/drivers/scsi/Kconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/scsi/Kconfig 2005-06-18 12:02:58.000000000 -0400 +@@ -813,6 +813,14 @@ + To compile this driver as a module, choose M here: the + module will be called ibmvscsic. + ++config SCSI_IBMVSCSIS ++ tristate "IBM Virtual SCSI Server support" ++ depends on PPC_PSERIES ++ help ++ This is the IBM Virtual SCSI Server ++ To compile this driver as a module, choose M here: the ++ module will be called ibmvscsis. ++ + config SCSI_INITIO + tristate "Initio 9100U(W) support" + depends on PCI && SCSI +diff -aurN a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile +--- a/drivers/scsi/ibmvscsi/Makefile 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/scsi/ibmvscsi/Makefile 2005-06-18 12:02:58.000000000 -0400 +@@ -3,3 +3,5 @@ + ibmvscsic-y += ibmvscsi.o + ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o + ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o ++ ++obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsis.o +diff -aurN a/drivers/scsi/ibmvscsi/ibmvscsis.c b/drivers/scsi/ibmvscsi/ibmvscsis.c +--- a/drivers/scsi/ibmvscsi/ibmvscsis.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/drivers/scsi/ibmvscsi/ibmvscsis.c 2005-06-18 12:02:58.000000000 -0400 +@@ -0,0 +1,2818 @@ ++/**************************************************************************/ ++/* -*- -linux- -*- */ ++/* IBM eServer i/pSeries Virtual SCSI Target Driver */ ++/* Copyright (C) 2003 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. */ ++/* */ ++/* This program is free software; you can redistribute it and/or modify */ ++/* it under the terms of the GNU General Public License as published by */ ++/* the Free Software Foundation; either version 2 of the License, or */ ++/* (at your option) any later version. */ ++/* */ ++/* This program is distributed in the hope that it will be useful, */ ++/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ ++/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ ++/* GNU General Public License for more details. */ ++/* */ ++/* You should have received a copy of the GNU General Public License */ ++/* along with this program; if not, write to the Free Software */ ++/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ ++/* USA */ ++/* */ ++/* This module contains the eServer virtual SCSI target code. The driver */ ++/* takes SRP requests from the virtual SCSI client (the linux version is */ ++/* int ibmvscsi.c, but there can be other clients, like AIX or OF) and */ ++/* passes them on to real devices in this system. */ ++/* */ ++/* The basic hierarchy (and somewhat the organization of this file) is */ ++/* that SCSI CDBs are in SRPs are in CRQs. */ ++/* */ ++/**************************************************************************/ ++/* ++ TODO: ++ - Support redirecting SRP SCSI requests to a real SCSI driver ++*/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "../scsi.h" ++#include "viosrp.h" ++ ++#define IBMVSCSIS_VERSION "1.2" ++ ++MODULE_DESCRIPTION("IBM Virtual SCSI Target"); ++MODULE_AUTHOR("Dave Boutcher"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(IBMVSCSIS_VERSION); ++ ++static int ibmvscsis_debug = 0; ++ ++/* These are fixed and come from the device tree...we ++ * just store them here to save getting them every time. ++ */ ++static char system_id[64] = ""; ++static char partition_name[97] = "UNKNOWN"; ++static unsigned int partition_number = -1; ++ ++/* ++ * Quick macro to enable/disable interrupts ++ * TODO: move to vio.h to be common with ibmvscsi.c ++ */ ++#define h_vio_signal(ua, mode) \ ++ plpar_hcall_norets(H_VIO_SIGNAL, ua, mode) ++ ++/* ++ * These are indexes into the following table, and have to match!!! ++ */ ++#define SENSE_SUCCESS 0 ++#define SENSE_ABORT 1 ++#define SENSE_INVALID_ID 2 ++#define SENSE_DEVICE_FAULT 3 ++#define SENSE_DEVICE_BUSY 4 ++#define SENSE_UNIT_OFFLINE 5 ++#define SENSE_INVALID_CMD 6 ++#define SENSE_INTERMEDIATE 7 ++#define SENSE_WRITE_PROT 8 ++#define SENSE_INVALID_FIELD 9 ++ ++#define TARGET_MAX_NAME_LEN 128 ++ ++static unsigned char ibmvscsis_sense_data[][3] = { ++/* ++ * Sense key lookup table ++ * Format: SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier ++ * Adapted from 3w-xxxx.h ++ */ ++ {0x00, 0x00, 0x00}, /* Success */ ++ {0x0b, 0x00, 0x00}, /* Aborted command */ ++ {0x0b, 0x14, 0x00}, /* ID not found */ ++ {0x04, 0x00, 0x00}, /* Device fault */ ++ {0x0b, 0x00, 0x00}, /* Device busy */ ++ {0x02, 0x04, 0x00}, /* Unit offline */ ++ {0x05, 0x20, 0x00}, /* Invalid Command */ ++ {0x10, 0x00, 0x00}, /* Intermediate */ ++ {0x07, 0x27, 0x00}, /* Write Protected */ ++ {0x05, 0x24, 0x00}, /* Invalid field */ ++}; ++ ++/* ++ * SCSI defined structure for inquiry data ++ * TODO: Seral number is currently messed up if you do ++ * scsiinfo. I'm not sure why and I think it comes out of ++ * here ++ */ ++struct inquiry_data { ++ u8 qual_type; ++ u8 rmb_reserve; ++ u8 version; ++ u8 aerc_naca_hisup_format; ++ u8 addl_len; ++ u8 sccs_reserved; ++ u8 bque_encserv_vs_multip_mchngr_reserved; ++ u8 reladr_reserved_linked_cmdqueue_vs; ++ char vendor[8]; ++ char product[16]; ++ char revision[4]; ++ char vendor_specific[20]; ++ char reserved1[2]; ++ char version_descriptor[16]; ++ char reserved2[22]; ++ char unique[158]; ++}; ++ ++extern int vio_num_address_cells; ++ ++/* ++ * an RPA command/response transport queue. This is our structure ++ * that points to the actual queue. feel free to modify this structure ++ * as needed ++ */ ++struct crq_queue { ++ struct viosrp_crq *msgs; ++ int size, cur; ++ dma_addr_t msg_token; ++ spinlock_t lock; ++}; ++ ++/* ++ * This structure tracks our fundamental unit of work. Whenever ++ * an SRP Information Unit (IU) arrives, we track all the good stuff ++ * here ++ */ ++struct iu_entry { ++ union viosrp_iu *iu; ++ struct server_adapter *adapter; ++ struct list_head next; ++ dma_addr_t iu_token; ++ int aborted; ++ struct { ++ dma_addr_t remote_token; ++ char *data_buffer; ++ dma_addr_t data_token; ++ long data_len; ++ struct vdev *vd; ++ char in_use:1; ++ char diunder:1; ++ char diover:1; ++ char dounder:1; ++ char doover:1; ++ char write:1; ++ char linked:1; ++ int data_out_residual_count; ++ int data_in_residual_count; ++ int ioerr; ++ } req; ++}; ++ ++/* ++ * a pool of ius for use ++ */ ++struct iu_pool { ++ spinlock_t lock; ++ struct list_head iu_entries; ++ struct iu_entry *list; ++ union viosrp_iu *iu_storage; ++ dma_addr_t iu_token; ++ u32 size; ++}; ++ ++/* ++ * Represents a single device that someone told us about ++ * that we treat as a LUN ++ */ ++struct vdev { ++ struct list_head list; ++ char type; /* 'B' for block, 'S' for SCSI */ ++ atomic_t refcount; ++ int disabled; ++ u64 lun; ++ struct kobject kobj; ++ struct { ++ char device_name[TARGET_MAX_NAME_LEN]; ++ struct block_device *bdev; ++ long blksize; ++ long lastlba; ++ int ro; ++ } b; ++}; ++ ++/* ++ * Represents a bus. target #'s in SCSI are 6 bits long, ++ * so you can have 64 targets per bus ++ */ ++#define TARGETS_PER_BUS (64) ++#define BUS_PER_ADAPTER (8) ++struct vbus { ++ struct vdev *vdev[TARGETS_PER_BUS]; ++ atomic_t num_targets; ++ struct kobject kobj; ++ int bus_num; ++}; ++ ++/* ++ * Buffer cache ++ */ ++struct dma_buffer { ++ dma_addr_t token; ++ char *addr; ++ size_t len; ++}; ++#define DMA_BUFFER_CACHE_SIZE (16) ++#define DMA_BUFFER_INIT_COUNT (4) ++#define DMA_BUFFER_INIT_LEN (PAGE_SIZE*16) ++ ++/* all driver data associated with a host adapter */ ++struct server_adapter { ++ struct device *dev; ++ struct vio_dev *dma_dev; ++ struct crq_queue queue; ++ struct work_struct crq_task; ++ struct tasklet_struct endio_tasklet; ++ struct iu_pool pool; ++ spinlock_t lock; ++ struct bio *bio_done; ++ struct bio *bio_donetail; ++ struct list_head inflight; ++ struct vbus *vbus[8]; ++ int nvdevs; ++ char name[32]; ++ unsigned long liobn; ++ unsigned long riobn; ++ ++ atomic_t num_buses; ++ struct kobject stats_kobj; ++ ++ /* This ugly expression allocates a bit array of ++ * in-use flags large enough for the number of buffers ++ */ ++ unsigned long dma_buffer_use[(DMA_BUFFER_CACHE_SIZE + ++ sizeof(unsigned long) - 1) ++ / sizeof(unsigned long)]; ++ struct dma_buffer dma_buffer[DMA_BUFFER_CACHE_SIZE]; ++ ++ /* Statistics only */ ++ atomic_t iu_count; /* number allocated */ ++ atomic_t bio_count; /* number allocated */ ++ atomic_t crq_processed; ++ atomic_t interrupts; ++ atomic_t read_processed; ++ atomic_t write_processed; ++ atomic_t buffers_allocated; ++ atomic_t errors; ++}; ++ ++/* ++ * Forward declarations ++ */ ++static long send_rsp(struct iu_entry *iue, int status); ++ ++/* ++ * The following are lifted from usb.h ++ */ ++#define DEBUG 1 ++#ifdef DEBUG ++#define dbg(format, arg...) if (ibmvscsis_debug) printk(KERN_WARNING __FILE__ ": " format , ## arg) ++#else ++#define dbg(format, arg...) do {} while (0) ++#endif ++#define err(format, arg...) printk(KERN_ERR "ibmvscsis: " format , ## arg) ++#define info(format, arg...) printk(KERN_INFO "ibmvscsis: " format , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING "ibmvscsis: " format , ## arg) ++ ++/* ============================================================== ++ * Utility Routines ++ * ============================================================== ++ */ ++/* ++ * return an 8 byte lun given a bus, target, lun. ++ * Today this only supports single level luns. Should we add a level or a ++ * 64 bit LUN as input to support multi-level luns? ++ */ ++u64 make_lun(unsigned int bus, unsigned int target, unsigned int lun) ++{ ++ u16 result = (0x8000 | ++ ((target & 0x003f) << 8) | ++ ((bus & 0x0007) << 5) | (lun & 0x001f)); ++ return ((u64) result) << 48; ++} ++ ++/* ++ * Given an 8 byte LUN, return the first level bus/target/lun. ++ * Today this doesn't support multi-level LUNs ++ */ ++#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007)) ++#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f)) ++#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f)) ++ ++static u8 getcontrolbyte(u8 * cdb) ++{ ++ return cdb[COMMAND_SIZE(cdb[0]) - 1]; ++} ++ ++static u8 getlink(struct iu_entry *iue) ++{ ++ return (getcontrolbyte(iue->iu->srp.cmd.cdb) & 0x01); ++} ++ ++/* ++ * Given an SRP, figure out the data in length ++ */ ++static int did_len(struct srp_cmd *cmd) ++{ ++ struct memory_descriptor *md; ++ struct indirect_descriptor *id; ++ int offset = cmd->additional_cdb_len * 4; ++ ++ switch (cmd->data_out_format) { ++ case SRP_NO_BUFFER: ++ offset += 0; ++ break; ++ case SRP_DIRECT_BUFFER: ++ offset += sizeof(struct memory_descriptor); ++ break; ++ case SRP_INDIRECT_BUFFER: ++ offset += sizeof(struct indirect_descriptor) ++ + ++ ((cmd->data_out_count - ++ 1) * sizeof(struct memory_descriptor)); ++ break; ++ default: ++ err("client error. Invalid data_out_format %d\n", ++ cmd->data_out_format); ++ return 0; ++ } ++ ++ switch (cmd->data_in_format) { ++ case SRP_NO_BUFFER: ++ return 0; ++ case SRP_DIRECT_BUFFER: ++ md = (struct memory_descriptor *)(cmd->additional_data + ++ offset); ++ return md->length; ++ case SRP_INDIRECT_BUFFER: ++ id = (struct indirect_descriptor *)(cmd->additional_data + ++ offset); ++ return id->total_length; ++ default: ++ err("client error. Invalid data_in_format %d\n", ++ cmd->data_in_format); ++ return 0; ++ } ++} ++ ++/* ++ * We keep a pool of IUs, this routine builds the pool. The pool is ++ * per-adapter. The size of the pool is negotiated as part of the SRP ++ * login, where we negotiate the number of requests (IUs) the client ++ * can send us. This routine is not synchronized. ++ */ ++static int initialize_iu_pool(struct server_adapter *adapter, int size) ++{ ++ struct iu_pool *pool = &adapter->pool; ++ int i; ++ ++ pool->size = size; ++ pool->lock = SPIN_LOCK_UNLOCKED; ++ INIT_LIST_HEAD(&pool->iu_entries); ++ ++ pool->list = kmalloc(pool->size * sizeof(*pool->list), GFP_KERNEL); ++ if (!pool->list) { ++ err("Error: Cannot allocate memory for IU list\n"); ++ return -ENOMEM; ++ } ++ memset(pool->list, 0x00, pool->size * sizeof(*pool->list)); ++ ++ pool->iu_storage = ++ dma_alloc_coherent(adapter->dev, ++ pool->size * sizeof(*pool->iu_storage), ++ &pool->iu_token, 0); ++ if (!pool->iu_storage) { ++ err("Error: Cannot allocate memory for IU pool\n"); ++ kfree(pool->list); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < pool->size; ++i) { ++ pool->list[i].iu = pool->iu_storage + i; ++ pool->list[i].iu_token = ++ pool->iu_token + sizeof(*pool->iu_storage) * i; ++ pool->list[i].adapter = adapter; ++ list_add_tail(&pool->list[i].next, &pool->iu_entries); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Free the pool we allocated in initialize_iu_pool ++ */ ++static void release_iu_pool(struct server_adapter *adapter) ++{ ++ struct iu_pool *pool = &adapter->pool; ++ int i, in_use = 0; ++ for (i = 0; i < pool->size; ++i) ++ if (pool->list[i].req.in_use) ++ ++in_use; ++ if (in_use) ++ err("Releasing event pool with %d events still in use?\n", ++ in_use); ++ kfree(pool->list); ++ dma_free_coherent(adapter->dev, pool->size * sizeof(*pool->iu_storage), ++ pool->iu_storage, pool->iu_token); ++} ++ ++/* ++ * Get an IU from the pool. Return NULL of the pool is empty. This ++ * routine is syncronized by a lock. The routine sets all the important ++ * fields to 0 ++ */ ++static struct iu_entry *get_iu(struct server_adapter *adapter) ++{ ++ struct iu_entry *e; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&adapter->pool.lock, flags); ++ if (!list_empty(&adapter->pool.iu_entries)) { ++ e = list_entry(adapter->pool.iu_entries.next, struct iu_entry, ++ next); ++ list_del(adapter->pool.iu_entries.next); ++ ++ if (e->req.in_use) { ++ err("Found in-use iue in free pool!"); ++ } ++ ++ memset(&e->req, 0x00, sizeof(e->req)); ++ ++ e->req.in_use = 1; ++ } else { ++ e = NULL; ++ } ++ ++ spin_unlock_irqrestore(&adapter->pool.lock, flags); ++ atomic_inc(&adapter->iu_count); ++ return e; ++} ++ ++/* ++ * Return an IU to the pool. This routine is synchronized ++ */ ++static void free_iu(struct iu_entry *iue) ++{ ++ unsigned long flags; ++ if (iue->req.vd) { ++ atomic_dec(&iue->req.vd->refcount); ++ } ++ ++ spin_lock_irqsave(&iue->adapter->pool.lock, flags); ++ if (iue->req.in_use == 0) { ++ warn("Internal error, freeing iue twice!\n"); ++ } else { ++ iue->req.in_use = 0; ++ list_add_tail(&iue->next, &iue->adapter->pool.iu_entries); ++ } ++ spin_unlock_irqrestore(&iue->adapter->pool.lock, flags); ++ atomic_dec(&iue->adapter->iu_count); ++} ++ ++/* ++ * Get a CRQ from the inter-partition queue. ++ */ ++static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue) ++{ ++ struct viosrp_crq *crq; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&queue->lock, flags); ++ crq = &queue->msgs[queue->cur]; ++ if (crq->valid & 0x80) { ++ if (++queue->cur == queue->size) ++ queue->cur = 0; ++ } else ++ crq = NULL; ++ spin_unlock_irqrestore(&queue->lock, flags); ++ ++ return crq; ++} ++ ++/* ++ * Make the RDMA hypervisor call. There should be a better way to do this ++ * than inline assembler. ++ * TODO: Fix the inline assembler ++ */ ++static long h_copy_rdma(long length, ++ unsigned long sliobn, unsigned long slioba, ++ unsigned long dliobn, unsigned long dlioba) ++{ ++ long lpar_rc = 0; ++ __asm__ __volatile__(" li 3,0x110 \n\t" ++ " mr 4, %1 \n\t" ++ " mr 5, %2 \n\t" ++ " mr 6, %3 \n\t" ++ " mr 7, %4 \n\t" ++ " mr 8, %5 \n\t" ++ " .long 0x44000022 \n\t" ++ " mr %0, 3 \n\t":"=&r"(lpar_rc) ++ :"r"(length), "r"(sliobn), "r"(slioba), ++ "r"(dliobn), "r"(dlioba) ++ :"r0", "r3", "r4", "r5", "r6", "r7", "r8", "cr0", ++ "cr1", "ctr", "xer", "memory"); ++ return lpar_rc; ++} ++ ++/* ++ * Send an SRP to another partition using the CRQ. ++ */ ++static int send_srp(struct iu_entry *iue, u64 length) ++{ ++ long rc, rc1; ++ union { ++ struct viosrp_crq cooked; ++ u64 raw[2]; ++ } crq; ++ ++ /* First copy the SRP */ ++ rc = h_copy_rdma(length, ++ iue->adapter->liobn, ++ iue->iu_token, ++ iue->adapter->riobn, iue->req.remote_token); ++ ++ if (rc) { ++ err("Error %ld transferring data to client\n", rc); ++ } ++ ++ crq.cooked.valid = 0x80; ++ crq.cooked.format = VIOSRP_SRP_FORMAT; ++ crq.cooked.reserved = 0x00; ++ crq.cooked.timeout = 0x00; ++ crq.cooked.IU_length = length; ++ crq.cooked.IU_data_ptr = iue->iu->srp.generic.tag; ++ ++ if (rc == 0) { ++ crq.cooked.status = 0x99; /* TODO: is this right? */ ++ } else { ++ crq.cooked.status = 0x00; ++ } ++ ++ rc1 = ++ plpar_hcall_norets(H_SEND_CRQ, iue->adapter->dma_dev->unit_address, ++ crq.raw[0], crq.raw[1]); ++ ++ if (rc1) { ++ err("Error %ld sending response to client\n", rc1); ++ return rc1; ++ } ++ ++ return rc; ++} ++ ++/* ++ * Send data to a single SRP memory descriptor ++ * Returns amount of data sent, or negative value on error ++ */ ++static long send_md_data(dma_addr_t stoken, int len, ++ struct memory_descriptor *md, ++ struct server_adapter *adapter) ++{ ++ int tosend; ++ long rc; ++ ++ if (len < md->length) ++ tosend = len; ++ else ++ tosend = md->length; ++ ++ rc = h_copy_rdma(tosend, ++ adapter->liobn, ++ stoken, adapter->riobn, md->virtual_address); ++ ++ if (rc != H_Success) { ++ err(" Error %ld transferring data to client\n", rc); ++ return -1; ++ } ++ ++ return tosend; ++} ++ ++/* ++ * Send data to the SRP data_in buffers ++ * Returns amount of data sent, or negative value on error ++ */ ++static long send_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue) ++{ ++ struct srp_cmd *cmd = &iue->iu->srp.cmd; ++ struct memory_descriptor *md; ++ struct indirect_descriptor *id; ++ int offset = 0; ++ int total_length = 0; ++ int i; ++ int thislen; ++ int bytes; ++ int sentlen = 0; ++ ++ offset = cmd->additional_cdb_len * 4; ++ ++ switch (cmd->data_out_format) { ++ case SRP_NO_BUFFER: ++ offset += 0; ++ break; ++ case SRP_DIRECT_BUFFER: ++ offset += sizeof(struct memory_descriptor); ++ break; ++ case SRP_INDIRECT_BUFFER: ++ offset += sizeof(struct indirect_descriptor) ++ + ++ ((cmd->data_out_count - ++ 1) * sizeof(struct memory_descriptor)); ++ break; ++ default: ++ err("client error: Invalid data_out_format %d\n", ++ cmd->data_out_format); ++ return 0; ++ } ++ ++ switch (cmd->data_in_format) { ++ case SRP_NO_BUFFER: ++ return 0; ++ case SRP_DIRECT_BUFFER: ++ md = (struct memory_descriptor *)(cmd->additional_data + ++ offset); ++ sentlen = send_md_data(stoken, len, md, iue->adapter); ++ len -= sentlen; ++ if (len) { ++ iue->req.diover = 1; ++ iue->req.data_in_residual_count = len; ++ } ++ return sentlen; ++ } ++ ++ if (cmd->data_in_format != SRP_INDIRECT_BUFFER) { ++ err("client error Invalid data_in_format %d\n", ++ cmd->data_in_format); ++ return 0; ++ } ++ ++ id = (struct indirect_descriptor *)(cmd->additional_data + offset); ++ ++ total_length = id->total_length; ++ ++ /* Work through the partial memory descriptor list */ ++ for (i = 0; ((i < cmd->data_in_count) && (len)); i++) { ++ if (len > id->list[i].length) { ++ thislen = id->list[i].length; ++ } else { ++ thislen = len; ++ } ++ ++ bytes = ++ send_md_data(stoken + sentlen, thislen, id->list + i, ++ iue->adapter); ++ if (bytes < 0) ++ return bytes; ++ ++ if (bytes != thislen) { ++ warn("Error: Tried to send %d, sent %d\n", thislen, ++ bytes); ++ } ++ ++ sentlen += bytes; ++ total_length -= bytes; ++ len -= bytes; ++ } ++ ++ if (len) { ++ iue->req.diover = 1; ++ iue->req.data_in_residual_count = len; ++ } ++ ++ return sentlen; ++} ++ ++/* ++ * Get data from the other partition from a single SRP memory descriptor ++ * Returns amount of data sent, or negative value on error ++ */ ++static long get_md_data(dma_addr_t ttoken, int len, ++ struct memory_descriptor *md, ++ struct server_adapter *adapter) ++{ ++ int toget; ++ long rc; ++ ++ if (len < md->length) ++ toget = len; ++ else ++ toget = md->length; ++ ++ rc = h_copy_rdma(toget, ++ adapter->riobn, ++ md->virtual_address, adapter->liobn, ttoken); ++ ++ if (rc != H_Success) { ++ err("Error %ld transferring data to client\n", rc); ++ return -1; ++ } ++ ++ return toget; ++} ++ ++/* ++ * Get data from an SRP data in area. ++ * Returns amount of data sent, or negative value on error ++ */ ++static long get_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue) ++{ ++ struct srp_cmd *cmd = &iue->iu->srp.cmd; ++ struct memory_descriptor *md; ++ struct indirect_descriptor *id; ++ int offset = 0; ++ int total_length = 0; ++ int i; ++ int thislen; ++ int bytes; ++ int sentlen = 0; ++ ++ offset = cmd->additional_cdb_len * 4; ++ ++ switch (cmd->data_out_format) { ++ case SRP_NO_BUFFER: ++ return 0; ++ break; ++ case SRP_DIRECT_BUFFER: ++ md = (struct memory_descriptor *)(cmd->additional_data + ++ offset); ++ return get_md_data(stoken, len, md, iue->adapter); ++ break; ++ } ++ ++ if (cmd->data_out_format != SRP_INDIRECT_BUFFER) { ++ err("client error: Invalid data_out_format %d\n", ++ cmd->data_out_format); ++ return 0; ++ } ++ ++ id = (struct indirect_descriptor *)(cmd->additional_data + offset); ++ ++ total_length = id->total_length; ++ ++ /* Work through the partial memory descriptor list */ ++ for (i = 0; ((i < cmd->data_out_count) && (len)); i++) { ++ if (len > id->list[i].length) { ++ thislen = id->list[i].length; ++ } else { ++ thislen = len; ++ } ++ ++ bytes = ++ get_md_data(stoken + sentlen, thislen, id->list + i, ++ iue->adapter); ++ if (bytes < 0) ++ return bytes; ++ ++ if (bytes != thislen) { ++ err("Partial data sent to client (%d/%d)\n", bytes, thislen); ++ } ++ ++ sentlen += bytes; ++ total_length -= bytes; ++ len -= bytes; ++ } ++ ++ return sentlen; ++} ++ ++/* ++ * Get some data buffers to start. This doesn't lock the adapter structure! ++ */ ++static void init_data_buffer(struct server_adapter *adapter) ++{ ++ int i; ++ ++ for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) { ++ if (adapter->dma_buffer[i].addr == NULL) { ++ adapter->dma_buffer[i].addr = (char *) ++ dma_alloc_coherent(adapter->dev, ++ DMA_BUFFER_INIT_LEN, ++ &adapter->dma_buffer[i].token, ++ 0); ++ adapter->dma_buffer[i].len = DMA_BUFFER_INIT_LEN; ++ dbg("data buf %p token %8.8x, len %ld\n", ++ adapter->dma_buffer[i].addr, ++ adapter->dma_buffer[i].token, ++ adapter->dma_buffer[i].len); ++ atomic_inc(&adapter->buffers_allocated); ++ } ++ } ++ ++ return; ++} ++ ++/* ++ * Get a memory buffer that includes a mapped TCE. ++ */ ++static void get_data_buffer(char **buffer, dma_addr_t * data_token, size_t len, ++ struct server_adapter *adapter) ++{ ++ int i; ++ ++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) { ++ if ((adapter->dma_buffer[i].addr) && ++ (adapter->dma_buffer[i].len >= len) && ++ (!test_and_set_bit(i, adapter->dma_buffer_use))) { ++ *buffer = adapter->dma_buffer[i].addr; ++ *data_token = adapter->dma_buffer[i].token; ++ return; ++ } ++ } ++ ++ /* Couldn't get a buffer! Try and get a new one */ ++ *buffer = (char *)dma_alloc_coherent(adapter->dev, len, data_token, 0); ++ atomic_inc(&adapter->buffers_allocated); ++ dbg("get: %p, %8.8x, %ld\n", *buffer, *data_token, len); ++ return; ++} ++ ++/* ++ * Free a memory buffer that includes a mapped TCE. ++ */ ++static void free_data_buffer(char *buffer, dma_addr_t data_token, size_t len, ++ struct server_adapter *adapter) ++{ ++ int i; ++ ++ /* First see if this buffer is already in the cache */ ++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) { ++ if (adapter->dma_buffer[i].addr == buffer) { ++ if (adapter->dma_buffer[i].token != data_token) { ++ err("Inconsistent data buffer pool info!\n"); ++ } ++ if (!test_and_clear_bit(i, adapter->dma_buffer_use)) { ++ err("Freeing data buffer twice!\n"); ++ } ++ return; ++ } ++ } ++ ++ /* See if there is an empty slot in our list */ ++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) { ++ if (!test_and_set_bit(i, adapter->dma_buffer_use)) { ++ if (adapter->dma_buffer[i].addr == NULL) { ++ adapter->dma_buffer[i].addr = buffer; ++ adapter->dma_buffer[i].token = data_token; ++ adapter->dma_buffer[i].len = len; ++ clear_bit(i, adapter->dma_buffer_use); ++ return; ++ } else { ++ clear_bit(i, adapter->dma_buffer_use); ++ } ++ } ++ } ++ ++ /* Now see if there is a smaller buffer we should throw out */ ++ for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) { ++ if (!test_and_set_bit(i, adapter->dma_buffer_use)) { ++ if (adapter->dma_buffer[i].len < len) { ++ dbg("fre1: %p, %8.8x, %ld\n", ++ adapter->dma_buffer[i].addr, ++ adapter->dma_buffer[i].token, ++ adapter->dma_buffer[i].len); ++ ++ dma_free_coherent(adapter->dev, ++ adapter->dma_buffer[i].len, ++ adapter->dma_buffer[i].addr, ++ adapter->dma_buffer[i].token); ++ ++ atomic_dec(&adapter->buffers_allocated); ++ ++ adapter->dma_buffer[i].addr = buffer; ++ adapter->dma_buffer[i].token = data_token; ++ adapter->dma_buffer[i].len = len; ++ clear_bit(i, adapter->dma_buffer_use); ++ return; ++ } else { ++ clear_bit(i, adapter->dma_buffer_use); ++ } ++ } ++ } ++ ++ /* No space to cache this. Give it back to the kernel */ ++ dbg("fre2: %p, %8.8x, %ld\n", buffer, data_token, len); ++ dma_free_coherent(adapter->dev, len, buffer, data_token); ++ atomic_dec(&adapter->buffers_allocated); ++} ++ ++/* ++ * Release all the data buffers ++ */ ++static void release_data_buffer(struct server_adapter *adapter) ++{ ++ int i; ++ int free_in_use = 0; ++ ++ for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) { ++ if (adapter->dma_buffer[i].addr != NULL) { ++ if (test_bit(i, adapter->dma_buffer_use)) { ++ free_in_use++; ++ } ++ dma_free_coherent(adapter->dev, ++ adapter->dma_buffer[i].len, ++ adapter->dma_buffer[i].addr, ++ adapter->dma_buffer[i].token); ++ ++ atomic_dec(&adapter->buffers_allocated); ++ } ++ } ++ ++ if (free_in_use) { ++ err("Freeing %d in-use data buffers\n", free_in_use); ++ } ++ return; ++} ++ ++/* ++ * the routine that gets called on end_io of our bios. We basically ++ * schedule the processing to be done in our task, since we don't want ++ * do things like RDMA in someone else's interrupt handler ++ * ++ * Each iu request may result in multiple bio requests. only proceed ++ * when all the bio requests have done. ++ */ ++static int ibmvscsis_end_io(struct bio *bio, unsigned int nbytes, int error) ++{ ++ struct iu_entry *iue = (struct iu_entry *)bio->bi_private; ++ struct server_adapter *adapter = iue->adapter; ++ unsigned long flags; ++ ++ if (bio->bi_size) ++ return 1; ++ ++ if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { ++ iue->req.ioerr = 1; ++ }; ++ ++ /* Add the bio to the done queue */ ++ spin_lock_irqsave(&adapter->lock, flags); ++ if (adapter->bio_donetail) { ++ adapter->bio_donetail->bi_next = bio; ++ adapter->bio_donetail = bio; ++ } else ++ adapter->bio_done = adapter->bio_donetail = bio; ++ bio->bi_next = NULL; ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ ++ /* Schedule the task */ ++ tasklet_schedule(&adapter->endio_tasklet); ++ ++ return 0; ++} ++ ++/* ++ * Find the vdev structure from the LUN field in an SRP IUE ++ * Note that this routine bumps a refcount field in the vdev. ++ * Normally this is done when free_iu is called. ++ */ ++static struct vdev *find_device(struct iu_entry *iue) ++{ ++ u16 *lun = (u16 *) & iue->iu->srp.cmd.lun; ++ u32 bus = (lun[0] & 0x00E0) >> 5; ++ u32 target = (lun[0] & 0x3F00) >> 8; ++ u32 slun = (lun[0] & 0x001F); ++ struct vdev *vd; ++ unsigned long flags; ++ ++ /* If asking for a lun other than 0, return nope */ ++ if (slun) { ++ return NULL; ++ } ++ ++ /* Only from SRP CMD */ ++ if (iue->iu->srp.generic.type != SRP_CMD_TYPE) ++ return NULL; ++ ++ /* if not a recognized LUN format, return NULL */ ++ if ((lun[0] & 0xC000) != 0x8000) ++ return NULL; ++ ++ spin_lock_irqsave(&iue->adapter->lock, flags); ++ if (iue->adapter->vbus[bus] == NULL) { ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ return NULL; ++ } ++ ++ vd = iue->adapter->vbus[bus]->vdev[target]; ++ ++ if ((vd == NULL) || (vd->disabled)) { ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ return NULL; ++ } ++ ++ if (vd) { ++ atomic_inc(&vd->refcount); ++ } ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ ++ return vd; ++} ++ ++/* ++ * Process BH buffer completions. When the end_io routine gets called ++ * we queue the bio on an internal queue and start a task to process them ++ */ ++static void endio_task(unsigned long data) ++{ ++ struct server_adapter *adapter = (struct server_adapter *)data; ++ struct iu_entry *iue= NULL; ++ struct bio *bio; ++ int bytes; ++ unsigned long flags; ++ ++ do { ++ spin_lock_irqsave(&adapter->lock, flags); ++ if ((bio = adapter->bio_done)) { ++ if (bio == adapter->bio_donetail) ++ adapter->bio_donetail = NULL; ++ adapter->bio_done = bio->bi_next; ++ bio->bi_next = NULL; ++ } ++ if (bio) { ++ /* Remove this iue from the in-flight list */ ++ iue = (struct iu_entry *)bio->bi_private; ++ if (!iue->req.in_use) { ++ err("Internal error! freed iue in bio!!!\n"); ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ return; ++ } ++ ++ list_del(&iue->next); ++ } ++ ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ ++ if (bio) { ++ /* Send back the SRP and data if this request was NOT ++ * aborted ++ */ ++ if (!iue->aborted) { ++ ++ if (!iue->req.ioerr) { ++ /* return data if this was a read */ ++ if (!iue->req.write) { ++ bytes = ++ send_cmd_data(iue->req. ++ data_token, ++ iue->req. ++ data_len, ++ iue); ++ if (bytes != iue->req.data_len) { ++ err("Error sending data " ++ "on response " ++ "(tried %d, sent %d\n", ++ bio->bi_size, bytes); ++ send_rsp(iue, ++ SENSE_ABORT); ++ } else { ++ send_rsp(iue, ++ SENSE_SUCCESS); ++ } ++ } else { ++ send_rsp(iue, SENSE_SUCCESS); ++ } ++ } else { ++ err("Block operation failed\n"); ++ print_command(iue->iu->srp.cmd.cdb); ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ } ++ } ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ free_data_buffer(iue->req.data_buffer, ++ iue->req.data_token, iue->req.data_len, ++ adapter); ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ ++ free_iu(iue); ++ ++ bio_put(bio); ++ atomic_dec(&adapter->bio_count); ++ } ++ } while (bio); ++} ++ ++/* ============================================================== ++ * SCSI Command Emulation Routines ++ * ============================================================== ++ */ ++ ++/* ++ * Process an inquiry SCSI Command ++ */ ++static void process_inquiry(struct iu_entry *iue) ++{ ++ struct inquiry_data *id; ++ dma_addr_t data_token; ++ u8 *raw_id; ++ int bytes; ++ ++ id = (struct inquiry_data *)dma_alloc_coherent(iue->adapter->dev, ++ sizeof(*id), ++ &data_token, 0); ++ raw_id = (u8 *)id; ++ memset(id, 0x00, sizeof(*id)); ++ ++ /* If we have a valid device */ ++ if (iue->req.vd) { ++ /* Standard inquiry page */ ++ if ((iue->iu->srp.cmd.cdb[1] == 0x00) && ++ (iue->iu->srp.cmd.cdb[2] == 0x00)) { ++ dbg(" inquiry returning device\n"); ++ id->qual_type = 0x00; /* Direct Access */ ++ id->rmb_reserve = 0x00; /* TODO: CD is removable */ ++ id->version = 0x84; /* ISO/IE */ ++ id->aerc_naca_hisup_format = 0x22;/* naca & fmt 0x02 */ ++ id->addl_len = sizeof(*id) - 4; ++ id->bque_encserv_vs_multip_mchngr_reserved = 0x00; ++ id->reladr_reserved_linked_cmdqueue_vs = 0x02;/*CMDQ*/ ++ memcpy(id->vendor, "IBM ", 8); ++ memcpy(id->product, "VSCSI blkdev ", 16); ++ memcpy(id->revision, "0001", 4); ++ snprintf(id->unique,sizeof(id->unique), ++ "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n", ++ system_id, ++ partition_number, ++ iue->adapter->dma_dev->unit_address, ++ GETBUS(iue->req.vd->lun), ++ GETTARGET(iue->req.vd->lun), ++ GETLUN(iue->req.vd->lun)); ++ } else if ((iue->iu->srp.cmd.cdb[1] == 0x01) && ++ (iue->iu->srp.cmd.cdb[2] == 0x00)) { ++ /* Supported VPD pages */ ++ raw_id[0] = 0x00; /* qualifier & type */ ++ raw_id[1] = 0x80; /* page */ ++ raw_id[2] = 0x00; /* reserved */ ++ raw_id[3] = 0x03; /* length */ ++ raw_id[4] = 0x00; /* page 0 */ ++ raw_id[5] = 0x80; /* serial number page */ ++ } else if ((iue->iu->srp.cmd.cdb[1] == 0x01) && ++ (iue->iu->srp.cmd.cdb[2] == 0x80)) { ++ /* serial number page */ ++ raw_id[0] = 0x00; /* qualifier & type */ ++ raw_id[1] = 0x80; /* page */ ++ raw_id[2] = 0x00; /* reserved */ ++ snprintf((char *)(raw_id+4), ++ sizeof(*id)-4, ++ "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n", ++ system_id, ++ partition_number, ++ iue->adapter->dma_dev->unit_address, ++ GETBUS(iue->req.vd->lun), ++ GETTARGET(iue->req.vd->lun), ++ GETLUN(iue->req.vd->lun)); ++ raw_id[3] = strlen((char *)raw_id+4); ++ } else { ++ /* Some unsupported data */ ++ send_rsp(iue, SENSE_INVALID_FIELD); ++ free_iu(iue); ++ return; ++ } ++ } else { ++ dbg(" inquiry returning no device\n"); ++ id->qual_type = 0x7F; /* Not supported, no device */ ++ } ++ ++ bytes = send_cmd_data(data_token, sizeof(*id), iue); ++ ++ dma_free_coherent(iue->adapter->dev, sizeof(*id), id, data_token); ++ ++ if (bytes < 0) { ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ } else { ++ send_rsp(iue, SENSE_SUCCESS); ++ } ++ ++ free_iu(iue); ++} ++ ++/* ++ * Handle an I/O. Called by WRITE6, WRITE10, etc ++ */ ++static void process_rw(char *cmd, int rw, struct iu_entry *iue, long lba, ++ long len) ++{ ++ char *buffer; ++ struct bio *bio; ++ int bytes; ++ int num_biovec; ++ int cur_biovec; ++ long flags; ++ ++ dbg("%s %16.16lx[%d:%d:%d][%s] lba %ld len %ld reladr %d link %d\n", ++ cmd, ++ iue->iu->srp.cmd.lun, ++ GETBUS(iue->iu->srp.cmd.lun), ++ GETTARGET(iue->iu->srp.cmd.lun), ++ GETLUN(iue->iu->srp.cmd.lun), ++ iue->req.vd->b.device_name, ++ lba, ++ len / iue->req.vd->b.blksize, ++ iue->iu->srp.cmd.cdb[1] & 0x01, iue->req.linked); ++ ++ if (rw == WRITE) { ++ atomic_inc(&iue->adapter->write_processed); ++ } else if (rw == READ) { ++ atomic_inc(&iue->adapter->read_processed); ++ } else { ++ err("Major internal error...rw not read or write\n"); ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ ++ free_iu(iue); ++ return; ++ } ++ ++ if (len == 0) { ++ warn("Zero length I/O\n"); ++ send_rsp(iue, SENSE_INVALID_CMD); ++ ++ free_iu(iue); ++ return; ++ } ++ ++ /* Writing to a read-only device */ ++ if ((rw == WRITE) && (iue->req.vd->b.ro)) { ++ warn("WRITE to read-only device\n"); ++ send_rsp(iue, SENSE_WRITE_PROT); ++ ++ free_iu(iue); ++ return; ++ } ++ ++ get_data_buffer(&buffer, &iue->req.data_token, len, iue->adapter); ++ iue->req.data_buffer = buffer; ++ iue->req.data_len = len; ++ if (buffer == NULL) { ++ err("Not able to get a data buffer (%lu pages)\n", ++ len / PAGE_SIZE); ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ ++ free_iu(iue); ++ return; ++ } ++ ++ /* if reladr */ ++ if (iue->iu->srp.cmd.cdb[1] & 0x01) { ++ lba = lba + iue->req.vd->b.lastlba; ++ } ++ ++ /* If this command is linked, Keep this lba */ ++ if (iue->req.linked) { ++ iue->req.vd->b.lastlba = lba; ++ } else { ++ iue->req.vd->b.lastlba = 0; ++ } ++ ++ if (rw == WRITE) { ++ iue->req.write = 1; ++ /* Get the data */ ++ bytes = get_cmd_data(iue->req.data_token, len, iue); ++ if (bytes != len) { ++ err("Error transferring data\n"); ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ ++ free_iu(iue); ++ return; ++ } ++ } ++ ++ num_biovec = (len - 1) / PAGE_CACHE_SIZE + 1; ++ ++ bio = bio_alloc(GFP_ATOMIC, num_biovec); ++ if (!bio) { ++ /* Ouch. couldn't get a bio. Mark this I/O as ++ * in error, then decrement the outstanding bio. ++ * If there are still outstanding bio, they will send ++ * the error and free the IU. If there are none, we ++ * should do it here ++ */ ++ iue->req.ioerr = 1; ++ err("Not able to allocate a bio\n"); ++ send_rsp(iue, SENSE_DEVICE_FAULT); ++ free_iu(iue); ++ return; ++ } ++ ++ iue->aborted = 0; ++ spin_lock_irqsave(&iue->adapter->lock, flags); ++ list_add_tail(&iue->next, &iue->adapter->inflight); ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ ++ atomic_inc(&iue->adapter->bio_count); ++ bio->bi_size = len; ++ bio->bi_bdev = iue->req.vd->b.bdev; ++ bio->bi_sector = lba; ++ bio->bi_end_io = &ibmvscsis_end_io; ++ bio->bi_private = iue; ++ bio->bi_rw = (rw == WRITE) ? 1 : 0; ++ bio->bi_phys_segments = 1; ++ bio->bi_hw_segments = 1; ++ ++ /* This all assumes that the buffers we get are page-aligned */ ++ for (cur_biovec = 0; cur_biovec < num_biovec; cur_biovec++) { ++ long thislen; ++ ++ if (len > PAGE_CACHE_SIZE) { ++ thislen = PAGE_CACHE_SIZE; ++ } else { ++ thislen = len; ++ } ++ ++ bio->bi_io_vec[cur_biovec].bv_page = virt_to_page(buffer); ++ bio->bi_io_vec[cur_biovec].bv_len = thislen; ++ bio->bi_io_vec[cur_biovec].bv_offset = ++ (unsigned long)buffer & PAGE_OFFSET_MASK; ++ bio->bi_vcnt++; ++ ++ len -= thislen; ++ buffer += thislen; ++ } ++ generic_make_request(bio); ++} ++ ++/* ++ * Process a READ6 ++ */ ++static void processRead6(struct iu_entry *iue) ++{ ++ long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF; ++ long len = iue->iu->srp.cmd.cdb[4]; ++ ++ /* Length of 0 indicates 256 */ ++ if (len == 0) { ++ len = 256; ++ } ++ ++ len = len * iue->req.vd->b.blksize; ++ ++ process_rw("Read6", READ, iue, lba, len); ++} ++ ++/* ++ * Process a READ10 ++ */ ++static void processRead10(struct iu_entry *iue) ++{ ++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2)); ++ long len = ++ *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize; ++ ++ process_rw("Read10", READ, iue, lba, len); ++} ++ ++/* ++ * Process a READ10 ++ */ ++static void processRead12(struct iu_entry *iue) ++{ ++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2)); ++ long len = ++ *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize; ++ ++ process_rw("Read12", READ, iue, lba, len); ++} ++ ++static void processWrite6(struct iu_entry *iue) ++{ ++ long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF; ++ long len = iue->iu->srp.cmd.cdb[4]; ++ ++ /* Length of 0 indicates 256 */ ++ if (len == 0) { ++ len = 256; ++ } ++ ++ len = len * iue->req.vd->b.blksize; ++ ++ process_rw("Write6", WRITE, iue, lba, len); ++} ++ ++static void processWrite10(struct iu_entry *iue) ++{ ++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2)); ++ long len = ++ *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize; ++ ++ process_rw("Write10", WRITE, iue, lba, len); ++} ++ ++static void processWrite12(struct iu_entry *iue) ++{ ++ long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2)); ++ long len = ++ *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize; ++ ++ process_rw("Write12", WRITE, iue, lba, len); ++} ++ ++/* ++ * Handle Read Capacity ++ */ ++static void processReadCapacity(struct iu_entry *iue) ++{ ++ struct ReadCapacityData { ++ u32 blocks; ++ u32 blocksize; ++ } *cap; ++ dma_addr_t data_token; ++ int bytes; ++ ++ cap = (struct ReadCapacityData *)dma_alloc_coherent(iue->adapter->dev, ++ sizeof(*cap), ++ &data_token, 0); ++ ++ /* return block size and last valid block */ ++ cap->blocksize = iue->req.vd->b.blksize; ++ cap->blocks = iue->req.vd->b.bdev->bd_inode->i_size ++ / iue->req.vd->b.blksize ++ - 1; ++ ++ info("Reporting capacity as %u block of size %u\n", cap->blocks, ++ cap->blocksize); ++ ++ bytes = send_cmd_data(data_token, sizeof(*cap), iue); ++ ++ dma_free_coherent(iue->adapter->dev, sizeof(*cap), cap, data_token); ++ ++ if (bytes != sizeof(*cap)) { ++ err("Error sending read capacity data. bytes %d, wanted %ld\n", ++ bytes, sizeof(*cap)); ++ } ++ ++ send_rsp(iue, SENSE_SUCCESS); ++ ++ free_iu(iue); ++} ++ ++/* ++ * Process Mode Sense ++ * TODO: I know scsiinfo asks for a bunch of mode pages not implemented here. ++ * Also, we need to act differently for virtual disk and virtual CD ++ */ ++#define MODE_SENSE_BUFFER_SIZE (512) ++static void processModeSense(struct iu_entry *iue) ++{ ++ dma_addr_t data_token; ++ int bytes; ++ ++ u8 *mode = (u8 *) dma_alloc_coherent(iue->adapter->dev, ++ MODE_SENSE_BUFFER_SIZE, ++ &data_token, 0); ++ /* which page */ ++ switch (iue->iu->srp.cmd.cdb[2]) { ++ case 0: ++ case 0x3f: ++ mode[1] = 0x00; /* Default medium */ ++ if (iue->req.vd->b.ro) { ++ mode[2] = 0x80; /* device specific */ ++ } else { ++ mode[2] = 0x00; /* device specific */ ++ } ++ /* note the DPOFUA bit is set to zero! */ ++ mode[3] = 0x08; /* block descriptor length */ ++ *((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size / ++ iue->req.vd->b.blksize; ++ *((u32 *) & mode[8]) = iue->req.vd->b.blksize; ++ bytes = mode[0] = 12; /* length */ ++ break; ++ ++ case 0x08: /* Cache page */ ++ /* length should be 4 */ ++ if (iue->iu->srp.cmd.cdb[4] != 4 ++ && iue->iu->srp.cmd.cdb[4] != 0x20) { ++ send_rsp(iue, SENSE_INVALID_CMD); ++ dma_free_coherent(iue->adapter->dev, ++ MODE_SENSE_BUFFER_SIZE, ++ mode, data_token); ++ free_iu(iue); ++ return; ++ } ++ ++ mode[1] = 0x00; /* Default medium */ ++ if (iue->req.vd->b.ro) { ++ mode[2] = 0x80; /* device specific */ ++ } else { ++ mode[2] = 0x00; /* device specific */ ++ } ++ /* note the DPOFUA bit is set to zero! */ ++ mode[3] = 0x08; /* block descriptor length */ ++ *((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size / ++ iue->req.vd->b.blksize; ++ *((u32 *) & mode[8]) = iue->req.vd->b.blksize; ++ ++ /* Cache page */ ++ mode[12] = 0x08; /* page */ ++ mode[13] = 0x12; /* page length */ ++ mode[14] = 0x01; /* no cache (0x04 for read/write cache) */ ++ ++ bytes = mode[0] = 12 + mode[13]; /* length */ ++ break; ++ default: ++ warn("Request for unknown mode page %d\n", ++ iue->iu->srp.cmd.cdb[2]); ++ send_rsp(iue, SENSE_INVALID_CMD); ++ dma_free_coherent(iue->adapter->dev, ++ MODE_SENSE_BUFFER_SIZE, mode, data_token); ++ free_iu(iue); ++ return; ++ } ++ ++ bytes = send_cmd_data(data_token, bytes, iue); ++ ++ dma_free_coherent(iue->adapter->dev, ++ MODE_SENSE_BUFFER_SIZE, mode, data_token); ++ ++ send_rsp(iue, SENSE_SUCCESS); ++ ++ free_iu(iue); ++ return; ++} ++ ++/* ++ * Report LUNS command. ++ */ ++static void processReportLUNs(struct iu_entry *iue) ++{ ++ int listsize = did_len(&iue->iu->srp.cmd); ++ dma_addr_t data_token; ++ int index = 2; /* Start after the two entries (length and LUN0) */ ++ int bus; ++ int target; ++ int bytes; ++ unsigned long flags; ++ ++ u64 *lunlist = (u64 *) dma_alloc_coherent(iue->adapter->dev, ++ listsize, ++ &data_token, 0); ++ ++ memset(lunlist, 0x00, listsize); ++ ++ /* work out list size in units of u64 */ ++ listsize = listsize / 8; ++ ++ if (listsize < 1) { ++ send_rsp(iue, SENSE_INVALID_CMD); ++ free_iu(iue); ++ } ++ ++ spin_lock_irqsave(&iue->adapter->lock, flags); ++ ++ /* send lunlist of size 1 when requesting lun is not all zeros */ ++ if (iue->iu->srp.cmd.lun != 0x0LL) { ++ *lunlist = ((u64) 1 * 8) << 32; ++ goto send_lunlist; ++ } ++ ++ /* return the total number of luns plus LUN0 in bytes */ ++ *lunlist = (((u64) ((iue->adapter->nvdevs + 1) * 8)) << 32); ++ ++ dbg("reporting %d luns\n", iue->adapter->nvdevs + 1); ++ /* loop through the bus */ ++ for (bus = 0; bus < BUS_PER_ADAPTER; bus++) { ++ /* If this bus exists */ ++ if (iue->adapter->vbus[bus]) { ++ /* loop through the targets */ ++ for (target = 0; target < TARGETS_PER_BUS; target++) { ++ /* If the target exists */ ++ if (iue->adapter->vbus[bus]->vdev[target]) { ++ if ((index < listsize) && ++ (!iue->adapter->vbus[bus]-> ++ vdev[target]->disabled)) { ++ lunlist[index++] = ++ iue->adapter->vbus[bus]-> ++ vdev[target]->lun; ++ dbg(" lun %16.16lx\n", ++ iue->adapter->vbus[bus]-> ++ vdev[target]->lun); ++ } ++ } ++ } ++ } ++ } ++ ++ send_lunlist: ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ ++ bytes = send_cmd_data(data_token, (index * 8), iue); ++ ++ dma_free_coherent(iue->adapter->dev, listsize * 8, lunlist, data_token); ++ ++ if (bytes != (index * 8)) { ++ err("Error sending report luns data. bytes %d, wanted %d\n", ++ bytes, index * 4); ++ send_rsp(iue, SENSE_ABORT); ++ } else { ++ send_rsp(iue, SENSE_SUCCESS); ++ } ++ ++ free_iu(iue); ++ return; ++} ++ ++/* ++ * Process an IU. ++ * ++ * Note that THIS routine is responsible for returning the IU from the pool ++ * The current assumption is that all the process routines called from here ++ * are, in turn, responsible for freeing the IU ++ */ ++static void process_cmd(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = iue->iu; ++ ++ iue->req.vd = find_device(iue); ++ ++ if ((iue->req.vd == NULL) && ++ (iu->srp.cmd.cdb[0] != REPORT_LUNS) && ++ (iu->srp.cmd.cdb[0] != INQUIRY)) { ++ dbg("Cmd %2.2x for unknown LUN %16.16lx\n", ++ iu->srp.cmd.cdb[0], iue->iu->srp.cmd.lun); ++ send_rsp(iue, SENSE_INVALID_ID); ++ free_iu(iue); ++ return; ++ } ++ ++ iue->req.linked = getlink(iue); ++ ++ switch (iu->srp.cmd.cdb[0]) { ++ case READ_6: ++ processRead6(iue); ++ break; ++ case READ_10: ++ processRead10(iue); ++ break; ++ case READ_12: ++ processRead12(iue); ++ break; ++ case WRITE_6: ++ processWrite6(iue); ++ break; ++ case WRITE_10: ++ processWrite10(iue); ++ break; ++ case WRITE_12: ++ processWrite12(iue); ++ break; ++ case REPORT_LUNS: ++ dbg("REPORT LUNS lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ processReportLUNs(iue); ++ break; ++ case INQUIRY: ++ dbg("INQUIRY lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ process_inquiry(iue); ++ break; ++ case READ_CAPACITY: ++ dbg("READ CAPACITY lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ processReadCapacity(iue); ++ break; ++ case MODE_SENSE: ++ dbg("MODE SENSE lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ processModeSense(iue); ++ break; ++ case TEST_UNIT_READY: ++ /* we already know the device exists */ ++ dbg("TEST UNIT READY lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ send_rsp(iue, SENSE_SUCCESS); ++ free_iu(iue); ++ break; ++ case START_STOP: ++ /* just respond OK */ ++ dbg("START_STOP lun %16.16lx\n", iue->iu->srp.cmd.lun); ++ send_rsp(iue, SENSE_SUCCESS); ++ free_iu(iue); ++ break; ++ default: ++ warn("Unsupported SCSI Command 0x%2.2x\n", iu->srp.cmd.cdb[0]); ++ send_rsp(iue, SENSE_INVALID_CMD); ++ free_iu(iue); ++ } ++} ++ ++u16 send_adapter_info(struct iu_entry *iue, ++ dma_addr_t remote_buffer, u16 length) ++{ ++ dma_addr_t data_token; ++ struct mad_adapter_info_data *info = ++ (struct mad_adapter_info_data *)dma_alloc_coherent(iue->adapter-> ++ dev, ++ sizeof(*info), ++ &data_token, 0); ++ ++ dbg("in send_adapter_info\n "); ++ if ((info) && (!dma_mapping_error(data_token))) { ++ int rc; ++ memset(info, 0x00, sizeof(*info)); ++ ++ dbg("building adapter_info\n "); ++ strcpy(info->srp_version, "1.6a"); ++ strncpy(info->partition_name, partition_name, ++ sizeof(info->partition_name)); ++ info->partition_number = partition_number; ++ info->mad_version = 1; ++ info->os_type = 3; ++ ++ rc = h_copy_rdma(sizeof(*info), ++ iue->adapter->liobn, ++ data_token, ++ iue->adapter->riobn, ++ remote_buffer); ++ ++ dma_free_coherent(iue->adapter->dev, ++ sizeof(*info), info, data_token); ++ ++ if (rc != H_Success) { ++ err("Error sending adapter info rc %d\n",rc); ++ return 1; ++ } ++ } else { ++ dbg("bad dma_alloc_cohereint in adapter_info\n "); ++ return 1; ++ } ++ return 0; ++ ++} ++ ++/* ============================================================== ++ * SRP Processing Routines ++ * ============================================================== ++ */ ++/* ++ * Process an incoming SRP Login request ++ */ ++static void process_login(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = iue->iu; ++ u64 tag = iu->srp.generic.tag; ++ ++ /* TODO handle case that requested size is wrong and buffer format is wrong */ ++ memset(iu, 0x00, sizeof(struct srp_login_rsp)); ++ iu->srp.login_rsp.type = SRP_LOGIN_RSP_TYPE; ++ iu->srp.login_rsp.request_limit_delta = iue->adapter->pool.size; ++ iu->srp.login_rsp.tag = tag; ++ iu->srp.login_rsp.max_initiator_to_target_iulen = sizeof(union srp_iu); ++ iu->srp.login_rsp.max_target_to_initiator_iulen = sizeof(union srp_iu); ++ iu->srp.login_rsp.supported_buffer_formats = 0x0006; /* direct and indirect */ ++ iu->srp.login_rsp.multi_channel_result = 0x00; /* TODO fix if we were already logged in */ ++ ++ send_srp(iue, sizeof(iu->srp.login_rsp)); ++} ++ ++/* ++ * Send an SRP response that includes sense data ++ */ ++static long send_rsp(struct iu_entry *iue, int status) ++{ ++ u8 *sense = iue->iu->srp.rsp.sense_and_response_data; ++ u64 tag = iue->iu->srp.generic.tag; ++ union viosrp_iu *iu = iue->iu; ++ ++ if (status != SENSE_SUCCESS) { ++ atomic_inc(&iue->adapter->errors); ++ } ++ ++ /* If the linked bit is on and status is good */ ++ if ((iue->req.linked) && (status == SENSE_SUCCESS)) { ++ status = SENSE_INTERMEDIATE; ++ } ++ ++ memset(iu, 0x00, sizeof(struct srp_rsp)); ++ iu->srp.rsp.type = SRP_RSP_TYPE; ++ iu->srp.rsp.request_limit_delta = 1; ++ iu->srp.rsp.tag = tag; ++ ++ iu->srp.rsp.diunder = iue->req.diunder; ++ iu->srp.rsp.diover = iue->req.diover; ++ iu->srp.rsp.dounder = iue->req.dounder; ++ iu->srp.rsp.doover = iue->req.doover; ++ ++ iu->srp.rsp.data_in_residual_count = iue->req.data_in_residual_count; ++ iu->srp.rsp.data_out_residual_count = iue->req.data_out_residual_count; ++ ++ iu->srp.rsp.rspvalid = 0; ++ ++ iu->srp.rsp.response_data_list_length = 0; ++ ++ if (status) { ++ iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION; ++ iu->srp.rsp.snsvalid = 1; ++ iu->srp.rsp.sense_data_list_length = 18; /* TODO be smarter about this */ ++ ++ /* Valid bit and 'current errors' */ ++ sense[0] = (0x1 << 7 | 0x70); ++ ++ /* Sense key */ ++ sense[2] = ibmvscsis_sense_data[status][0]; ++ ++ /* Additional sense length */ ++ sense[7] = 0xa; /* 10 bytes */ ++ ++ /* Additional sense code */ ++ sense[12] = ibmvscsis_sense_data[status][1]; ++ ++ /* Additional sense code qualifier */ ++ sense[13] = ibmvscsis_sense_data[status][2]; ++ } else { ++ iu->srp.rsp.status = 0; ++ } ++ ++ send_srp(iue, sizeof(iu->srp.rsp)); ++ ++ return 0; ++} ++ ++static void process_device_reset(struct iu_entry *iue) ++{ ++ struct iu_entry *tmp_iue; ++ unsigned long flags; ++ union viosrp_iu *iu = iue->iu; ++ ++ info("device reset for lun %16.16lx\n", iu->srp.tsk_mgmt.lun); ++ ++ spin_lock_irqsave(&iue->adapter->lock, flags); ++ ++ list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) { ++ if (iu->srp.tsk_mgmt.lun == tmp_iue->iu->srp.cmd.lun) { ++ { ++ tmp_iue->aborted = 1; ++ } ++ } ++ ++ } ++ ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ send_rsp(iue, SENSE_SUCCESS); ++} ++ ++static void process_abort(struct iu_entry *iue) ++{ ++ struct iu_entry *tmp_iue; ++ unsigned long flags; ++ union viosrp_iu *iu = iue->iu; ++ ++ info("aborting task with tag %16.16lx, lun %16.16lx\n", ++ iu->srp.tsk_mgmt.managed_task_tag, iu->srp.tsk_mgmt.lun); ++ ++ spin_lock_irqsave(&iue->adapter->lock, flags); ++ ++ list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) { ++ if (tmp_iue->iu->srp.cmd.tag == ++ iu->srp.tsk_mgmt.managed_task_tag) { ++ { ++ tmp_iue->aborted = 1; ++ info("abort successful\n"); ++ spin_unlock_irqrestore(&iue->adapter->lock, ++ flags); ++ send_rsp(iue, SENSE_SUCCESS); ++ return; ++ } ++ } ++ } ++ info("unable to abort cmd\n"); ++ ++ spin_unlock_irqrestore(&iue->adapter->lock, flags); ++ send_rsp(iue, SENSE_INVALID_ID); ++} ++ ++static void process_tsk_mgmt(struct iu_entry *iue) ++{ ++ union viosrp_iu *iu = iue->iu; ++ ++ if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x01) { ++ process_abort(iue); ++ } else if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x08) { ++ process_device_reset(iue); ++ } else { ++ send_rsp(iue, SENSE_INVALID_CMD); ++ } ++} ++ ++static void process_iu(struct viosrp_crq *crq, struct server_adapter *adapter) ++{ ++ struct iu_entry *iue = get_iu(adapter); ++ union viosrp_iu *iu; ++ int queued = 0; ++ long rc; ++ ++ if (iue == NULL) { ++ /* TODO Yikes! */ ++ warn("Error getting IU from pool, other side exceeded limit\n"); ++ return; ++ } ++ ++ iue->req.remote_token = crq->IU_data_ptr; ++ ++ rc = h_copy_rdma(crq->IU_length, ++ iue->adapter->riobn, ++ iue->req.remote_token, adapter->liobn, iue->iu_token); ++ ++ iu = iue->iu; ++ ++ if (rc) { ++ err("Error %ld transferring data to client\n", rc); ++ } ++ ++ if (crq->format == VIOSRP_MAD_FORMAT) { ++ switch (iu->mad.empty_iu.common.type) { ++ case VIOSRP_EMPTY_IU_TYPE: ++ warn("Unsupported EMPTY MAD IU\n"); ++ break; ++ case VIOSRP_ERROR_LOG_TYPE: ++ warn("Unsupported ERROR LOG MAD IU\n"); ++ iu->mad.error_log.common.status = 1; ++ send_srp(iue, sizeof(iu->mad.error_log)); ++ break; ++ case VIOSRP_ADAPTER_INFO_TYPE: ++ iu->mad.adapter_info.common.status = ++ send_adapter_info(iue, ++ iu->mad.adapter_info.buffer, ++ iu->mad.adapter_info.common. ++ length); ++ ++ send_srp(iue, sizeof(iu->mad.adapter_info)); ++ break; ++ case VIOSRP_HOST_CONFIG_TYPE: ++ iu->mad.host_config.common.status = 1; ++ send_srp(iue, sizeof(iu->mad.host_config)); ++ break; ++ default: ++ warn("Unsupported MAD type %d\n", iu->srp.generic.type); ++ } ++ } else { ++ switch (iu->srp.generic.type) { ++ case SRP_LOGIN_REQ_TYPE: ++ dbg("SRP LOGIN\n"); ++ process_login(iue); ++ break; ++ case SRP_LOGIN_RSP_TYPE: ++ warn("Unsupported LOGIN_RSP SRP IU\n"); ++ break; ++ case SRP_I_LOGOUT_TYPE: ++ warn("Unsupported I_LOGOUT SRP IU\n"); ++ break; ++ case SRP_T_LOGOUT_TYPE: ++ warn("Unsupported T_LOGOUT SRP IU\n"); ++ break; ++ case SRP_TSK_MGMT_TYPE: ++ process_tsk_mgmt(iue); ++ break; ++ case SRP_CMD_TYPE: ++ process_cmd(iue); ++ queued = 1; ++ break; ++ case SRP_RSP_TYPE: ++ warn("Unsupported RSP SRP IU\n"); ++ break; ++ case SRP_CRED_REQ_TYPE: ++ warn("Unsupported CRED_REQ SRP IU\n"); ++ break; ++ case SRP_CRED_RSP_TYPE: ++ warn("Unsupported CRED_RSP SRP IU\n"); ++ break; ++ case SRP_AER_REQ_TYPE: ++ warn("Unsupported AER_REQ SRP IU\n"); ++ break; ++ case SRP_AER_RSP_TYPE: ++ warn("Unsupported AER_RSP SRP IU\n"); ++ break; ++ default: ++ warn("Unsupported SRP type %d\n", iu->srp.generic.type); ++ } ++ } ++ ++ /* ++ * If no one has queued the IU for further work, free it ++ * Note that this is kind of an ugly design based on setting ++ * this variable up above in cases where the routine we call ++ * is responsible for freeing the IU ++ */ ++ if (!queued) ++ free_iu(iue); ++} ++ ++/* ============================================================== ++ * CRQ Processing Routines ++ * ============================================================== ++ */ ++ ++/* ++ * Handle a CRQ event ++ */ ++static void handle_crq(struct viosrp_crq *crq, struct server_adapter *adapter) ++{ ++ switch (crq->valid) { ++ case 0xC0: /* initialization */ ++ switch (crq->format) { ++ case 0x01: ++ info("Client just initialized\n"); ++ plpar_hcall_norets(H_SEND_CRQ, ++ adapter->dma_dev->unit_address, ++ 0xC002000000000000, 0); ++ break; ++ case 0x02: ++ info("Client initialization complete\n"); ++ break; ++ default: ++ err("Client error: Unknwn msg format %d\n", ++ crq->format); ++ } ++ return; ++ case 0xFF: /* transport event */ ++ info("Client closed\n"); ++ return; ++ case 0x80: /* real payload */ ++ { ++ switch (crq->format) { ++ case VIOSRP_SRP_FORMAT: ++ case VIOSRP_MAD_FORMAT: ++ process_iu(crq, adapter); ++ break; ++ case VIOSRP_OS400_FORMAT: ++ warn("Unsupported OS400 format CRQ\n"); ++ break; ++ ++ case VIOSRP_AIX_FORMAT: ++ warn("Unsupported AIX format CRQ\n"); ++ break; ++ ++ case VIOSRP_LINUX_FORMAT: ++ warn("Unsupported LINUX format CRQ\n"); ++ break; ++ ++ case VIOSRP_INLINE_FORMAT: ++ warn("Unsupported _INLINE_ format CRQ\n"); ++ break; ++ ++ default: ++ err("Client error: Unsupported msg format %d\n", ++ crq->format); ++ } ++ } ++ break; ++ default: ++ err("Client error: unknown message type 0x%02x!?\n", ++ crq->valid); ++ return; ++ } ++ ++} ++ ++/* ++ * Task to handle CRQs and completions ++ */ ++static void crq_task(void *data) ++{ ++ struct server_adapter *adapter = (struct server_adapter *)data; ++ struct viosrp_crq *crq; ++ long rc; ++ int done = 0; ++ ++ while (!done) { ++ ++ /* Loop through and process CRQs */ ++ while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) { ++ atomic_inc(&adapter->crq_processed); ++ handle_crq(crq, adapter); ++ crq->valid = 0x00; ++ } ++ ++ rc = h_vio_signal(adapter->dma_dev->unit_address, 1); ++ if (rc != 0) { ++ err("Error %ld enabling interrupts!!!\n", rc); ++ } ++ if ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) { ++ rc = h_vio_signal(adapter->dma_dev->unit_address, 0); ++ if (rc != 0) { ++ err("Error %ld enabling interrupts!!!\n", rc); ++ } ++ handle_crq(crq, adapter); ++ crq->valid = 0x00; ++ } else { ++ done = 1; ++ } ++ } ++} ++ ++/* ++ * Handle the interrupt that occurs when something is placed on our CRQ ++ */ ++static irqreturn_t handle_interrupt(int irq, void *dev_instance, ++ struct pt_regs *regs) ++{ ++ struct server_adapter *adapter = (struct server_adapter *)dev_instance; ++ long rc; ++ ++ rc = h_vio_signal(adapter->dma_dev->unit_address, 0); ++ if (rc != 0) { ++ err(" Error %ld disabling interrupts!!!\n", rc); ++ } ++ ++ atomic_inc(&adapter->interrupts); ++ ++ kblockd_schedule_work(&adapter->crq_task); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Initialize our CRQ ++ * return zero on success, non-zero on failure ++ */ ++static int initialize_crq_queue(struct crq_queue *queue, ++ struct server_adapter *adapter) ++{ ++ int rc; ++ ++ queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); ++ if (!queue->msgs) ++ goto malloc_failed; ++ queue->size = PAGE_SIZE / sizeof(*queue->msgs); ++ ++ queue->msg_token = dma_map_single(adapter->dev, queue->msgs, ++ queue->size * sizeof(*queue->msgs), ++ DMA_BIDIRECTIONAL); ++ ++ if (dma_mapping_error(queue->msg_token)) ++ goto map_failed; ++ ++ rc = plpar_hcall_norets(H_REG_CRQ, adapter->dma_dev->unit_address, ++ queue->msg_token, PAGE_SIZE); ++ ++ if ((rc != 0) && (rc != 2)) { ++ err("Error 0x%x opening virtual adapter\n", rc); ++ goto reg_crq_failed; ++ } ++ ++ if (request_irq ++ (adapter->dma_dev->irq, &handle_interrupt, SA_INTERRUPT, ++ "ibmvscsis", adapter) != 0) ++ goto req_irq_failed; ++ ++ rc = h_vio_signal(adapter->dma_dev->unit_address, 1); ++ if (rc != 0) { ++ err("Error %d enabling interrupts!!!\n", rc); ++ goto req_irq_failed; ++ } ++ ++ plpar_hcall_norets(H_SEND_CRQ, adapter->dma_dev->unit_address, ++ 0xC001000000000000, 0); ++ ++ queue->cur = 0; ++ queue->lock = SPIN_LOCK_UNLOCKED; ++ ++ return 0; ++ ++ req_irq_failed: ++ do { ++ rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address); ++ } while ((rc == H_Busy) || (H_isLongBusy(rc))); ++ ++ reg_crq_failed: ++ dma_unmap_single(adapter->dev, queue->msg_token, ++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); ++ map_failed: ++ free_page((unsigned long)queue->msgs); ++ malloc_failed: ++ return -1; ++} ++ ++/* ++ * Release the CRQ ++ */ ++static void release_crq_queue(struct crq_queue *queue, ++ struct server_adapter *adapter) ++{ ++ int rc; ++ ++ info("releasing adapter\n"); ++ free_irq(adapter->dma_dev->irq, adapter); ++ do { ++ rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address); ++ } while ((rc == H_Busy) || (H_isLongBusy(rc))); ++ dma_unmap_single(adapter->dev, queue->msg_token, ++ queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); ++ free_page((unsigned long)queue->msgs); ++} ++ ++/* ============================================================== ++ * Module Management ++ * ============================================================== ++ */ ++/* ++ * Add a block device as a SCSI LUN ++ */ ++static int activate_block_device(struct vdev *vdev) ++{ ++ struct block_device *bdev; ++ char *name = vdev->b.device_name; ++ int ro = vdev->b.ro; ++ ++ bdev = open_bdev_excl(name, ro, activate_block_device); ++ if (IS_ERR(bdev)) ++ return PTR_ERR(bdev);; ++ ++ vdev->b.bdev = bdev; ++ vdev->disabled = 0; ++ ++ info("Activating block device %s as %sLUN 0x%lx\n", ++ name, ro ? "read only " : "", vdev->lun); ++ ++ return 0; ++} ++ ++static void deactivate_block_device(struct vdev *vdev) ++{ ++ info("Deactivating block device, LUN 0x%lx\n", vdev->lun); ++ ++ /* Wait while any users of this device finish. Note there should ++ * be no new users, since we have marked this disabled ++ * ++ * We just poll here, since we are blocking write ++ */ ++ while (atomic_read(&vdev->refcount)) { ++ schedule_timeout(HZ / 4); /* 1/4 second */ ++ } ++ ++ vdev->disabled = 1; ++ close_bdev_excl(vdev->b.bdev); ++} ++ ++ ++#define ATTR(_type, _name, _mode) \ ++struct attribute vscsi_##_type##_##_name##_attr = { \ ++.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE \ ++}; ++ ++static struct kobj_type ktype_vscsi_target; ++static struct kobj_type ktype_vscsi_bus; ++static struct kobj_type ktype_vscsi_stats; ++ ++static void set_num_targets(struct vbus* vbus, long value) ++{ ++ struct device *dev = ++ container_of(vbus->kobj.parent, struct device , kobj); ++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data; ++ int cur_num_targets = atomic_read(&vbus->num_targets); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ ++ if (cur_num_targets < value) { //growing ++ int i; ++ for (i = cur_num_targets; i < value; i++) { ++ vbus->vdev[i] = (struct vdev *) ++ kmalloc(sizeof(struct vdev), GFP_KERNEL); ++ if (!vbus->vdev[i]) { ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ err("Couldn't allocate target memory %d\n", i); ++ return; ++ } ++ memset(vbus->vdev[i], 0x00, sizeof(struct vdev)); ++ ++ vbus->vdev[i]->lun = make_lun(vbus->bus_num, i, 0); ++ vbus->vdev[i]->b.blksize = 512; ++ vbus->vdev[i]->disabled = 1; ++ ++ vbus->vdev[i]->kobj.parent = &vbus->kobj; ++ sprintf(vbus->vdev[i]->kobj.name, "target%d", i); ++ vbus->vdev[i]->kobj.ktype = &ktype_vscsi_target; ++ kobject_register(&vbus->vdev[i]->kobj); ++ adapter->nvdevs++; ++ atomic_inc(&vbus->num_targets); ++ } ++ } else { //shrinking ++ int i; ++ for (i = cur_num_targets - 1; i >= value; i--) ++ { ++ if (!vbus->vdev[i]->disabled) { ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ err("Can't remove active target %d\n", i); ++ return; ++ } ++ ++ kobject_unregister(&vbus->vdev[i]->kobj); ++ ++ kfree(vbus->vdev[i]); ++ ++ adapter->nvdevs--; ++ atomic_dec(&vbus->num_targets); ++ } ++ } ++ spin_unlock_irqrestore(&adapter->lock, flags); ++} ++ ++static void set_num_buses(struct device *dev, long value) ++{ ++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data; ++ int cur_num_buses = atomic_read(&adapter->num_buses); ++ unsigned long flags= 0L; ++ ++ ++ if (cur_num_buses < value) { // growing ++ int i; ++ for (i = cur_num_buses; i < value; i++) { ++ adapter->vbus[i] = (struct vbus *) ++ kmalloc(sizeof(struct vbus), GFP_KERNEL); ++ if (!adapter->vbus[i]) { ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ err("Couldn't allocate bus %d memory\n", i); ++ return; ++ } ++ memset(adapter->vbus[i], 0x00, sizeof(struct vbus)); ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ ++ adapter->vbus[i]->bus_num = i; ++ ++ adapter->vbus[i]->kobj.parent = &dev->kobj; ++ sprintf(adapter->vbus[i]->kobj.name, "bus%d", i); ++ adapter->vbus[i]->kobj.ktype = &ktype_vscsi_bus; ++ kobject_register(&adapter->vbus[i]->kobj); ++ ++ atomic_inc(&adapter->num_buses); ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ ++ set_num_targets(adapter->vbus[i], 1); ++ } ++ ++ } else if (cur_num_buses > value) { //shrinking ++ int i, j, active_target; ++ for (i = cur_num_buses - 1; i >= value; i--) { ++ active_target = -1; ++ for (j = 0; j < TARGETS_PER_BUS; j++) { ++ if (adapter->vbus[i]->vdev[j] && ++ !adapter->vbus[i]->vdev[j]->disabled) { ++ active_target = j; ++ break; ++ } ++ } ++ if (active_target != -1) { ++ err("Can't remove bus%d, target%d active\n", ++ i, active_target); ++ return ; ++ } ++ ++ set_num_targets(adapter->vbus[i], 0); ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ atomic_dec(&adapter->num_buses); ++ kobject_unregister(&adapter->vbus[i]->kobj); ++ kfree(adapter->vbus[i]); ++ adapter->vbus[i] = NULL; ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ } ++ } ++} ++ ++ ++/* Target sysfs stuff */ ++static ATTR(target, type, 0644); ++static ATTR(target, device, 0644); ++static ATTR(target, active, 0644); ++static ATTR(target, ro, 0644); ++ ++static ssize_t vscsi_target_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct vdev *vdev = container_of(kobj, struct vdev, kobj); ++ struct device *dev = container_of(kobj->parent->parent, struct device, kobj); ++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data; ++ unsigned long flags; ++ ssize_t returned= (ssize_t)0; ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ ++ if (attr == &vscsi_target_type_attr) ++ returned = sprintf(buf, "%c\n", vdev->type); ++ else if (attr == &vscsi_target_device_attr) ++ returned = sprintf(buf, "%s\n", vdev->b.device_name); ++ else if (attr == &vscsi_target_active_attr) ++ returned = sprintf(buf, "%d\n", !vdev->disabled); ++ else if (attr == &vscsi_target_ro_attr) ++ returned = sprintf(buf, "%d\n", vdev->b.ro); ++ else { ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ BUG(); ++ } ++ ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ ++ return returned; ++} ++ ++static ssize_t vscsi_target_store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count) ++{ ++ struct vdev *vdev = container_of(kobj, struct vdev, kobj); ++ struct device *dev = container_of(kobj->parent->parent, struct device, kobj); ++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data; ++ long flags; ++ long value = simple_strtol(buf, NULL, 10); ++ ++ if (attr != &vscsi_target_active_attr && !vdev->disabled) { ++ err("Error: Can't modify properties while target is active.\n"); ++ return -EPERM; ++ } ++ ++ if (attr == &vscsi_target_type_attr) { ++ if (buf[0] == 'B' || buf[0] == 'b') ++ vdev->type = 'B'; ++ else if (buf[0] == 'S' || buf[0] == 's') { ++ // TODO ++ err ("SCSI mode not supported yet\n"); ++ return -EINVAL; ++ } else ++ return -EINVAL; ++ } else if (attr == &vscsi_target_device_attr) { ++ int i; ++ spin_lock_irqsave(&adapter->lock, flags); ++ i = strlcpy(vdev->b.device_name, buf, TARGET_MAX_NAME_LEN); ++ for (; i >= 0; i--) ++ if (vdev->b.device_name[i] == '\n') ++ vdev->b.device_name[i] = '\0'; ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ } else if (attr == &vscsi_target_active_attr) { ++ if (value) { ++ int rc; ++ if (!vdev->disabled) { ++ warn("Warning: Target was already active\n"); ++ return -EINVAL; ++ } ++ if (vdev->type == '\0') { ++ err("Error: Type not specified\n"); ++ return -EPERM; ++ } ++ rc = activate_block_device(vdev); ++ if (rc) { ++ err("Error opening block device=%d\n", rc); ++ return rc; ++ } ++ } else { ++ if (!vdev->disabled) ++ deactivate_block_device(vdev); ++ } ++ } else if (attr == &vscsi_target_ro_attr) ++ vdev->b.ro = value > 0 ? 1 : 0; ++ else ++ BUG(); ++ ++ return count; ++} ++ ++static struct attribute * vscsi_target_attrs[] = { ++ &vscsi_target_type_attr, ++ &vscsi_target_device_attr, ++ &vscsi_target_active_attr, ++ &vscsi_target_ro_attr, ++ NULL, ++}; ++ ++static struct sysfs_ops vscsi_target_ops = { ++ .show = vscsi_target_show, ++ .store = vscsi_target_store, ++}; ++ ++static struct kobj_type ktype_vscsi_target = { ++ .release = NULL, ++ .sysfs_ops = &vscsi_target_ops, ++ .default_attrs = vscsi_target_attrs, ++}; ++ ++ ++ ++/* Bus sysfs stuff */ ++static ssize_t vscsi_bus_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct vbus *vbus = container_of(kobj, struct vbus, kobj); ++ return sprintf(buf, "%d\n", atomic_read(&vbus->num_targets)); ++} ++ ++static ssize_t vscsi_bus_store(struct kobject * kobj, struct attribute * attr, ++const char * buf, size_t count) ++{ ++ struct vbus *vbus = container_of(kobj, struct vbus, kobj); ++ long value = simple_strtol(buf, NULL, 10); ++ ++ if (value < 0 || value > TARGETS_PER_BUS) ++ return -EINVAL; ++ ++ set_num_targets(vbus, value); ++ ++ return count; ++} ++ ++ ++static ATTR(bus, num_targets, 0644); ++ ++static struct attribute * vscsi_bus_attrs[] = { ++ &vscsi_bus_num_targets_attr, ++ NULL, ++}; ++ ++static struct sysfs_ops vscsi_bus_ops = { ++ .show = vscsi_bus_show, ++ .store = vscsi_bus_store, ++}; ++ ++static struct kobj_type ktype_vscsi_bus = { ++ .release = NULL, ++ .sysfs_ops = &vscsi_bus_ops, ++ .default_attrs = vscsi_bus_attrs, ++}; ++ ++ ++/* Device attributes */ ++static ssize_t vscsi_dev_bus_show(struct device * dev, char * buf) ++{ ++ struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data; ++ ++ return sprintf(buf, "%d\n", atomic_read(&adapter->num_buses)); ++} ++ ++static ssize_t vscsi_dev_bus_store(struct device * dev, const char * buf, size_t count) ++{ ++ long value = simple_strtol(buf, NULL, 10); ++ ++ if (value < 0 || value > BUS_PER_ADAPTER) ++ return -EINVAL; ++ ++ set_num_buses(dev, value); ++ return count; ++} ++ ++static DEVICE_ATTR(num_buses, 0644, vscsi_dev_bus_show, vscsi_dev_bus_store); ++ ++ ++/* Stats kobj stuff */ ++ ++static ATTR(stats, interrupts, 0444); ++static ATTR(stats, read_ops, 0444); ++static ATTR(stats, write_ops, 0444); ++static ATTR(stats, crq_msgs, 0444); ++static ATTR(stats, iu_allocs, 0444); ++static ATTR(stats, bio_allocs, 0444); ++static ATTR(stats, buf_allocs, 0444); ++static ATTR(stats, errors, 0444); ++ ++static struct attribute * vscsi_stats_attrs[] = { ++ &vscsi_stats_interrupts_attr, ++ &vscsi_stats_read_ops_attr, ++ &vscsi_stats_write_ops_attr, ++ &vscsi_stats_crq_msgs_attr, ++ &vscsi_stats_iu_allocs_attr, ++ &vscsi_stats_bio_allocs_attr, ++ &vscsi_stats_buf_allocs_attr, ++ &vscsi_stats_errors_attr, ++ NULL, ++}; ++ ++ ++static ssize_t vscsi_stats_show(struct kobject * kobj, struct attribute * attr, char * buf) ++{ ++ struct server_adapter *adapter= container_of(kobj, struct server_adapter, stats_kobj); ++ if (attr == &vscsi_stats_interrupts_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->interrupts)); ++ if (attr == &vscsi_stats_read_ops_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->read_processed)); ++ if (attr == &vscsi_stats_write_ops_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->write_processed)); ++ if (attr == &vscsi_stats_crq_msgs_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->crq_processed)); ++ if (attr == &vscsi_stats_iu_allocs_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->iu_count)); ++ if (attr == &vscsi_stats_bio_allocs_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->bio_count)); ++ if (attr == &vscsi_stats_buf_allocs_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->buffers_allocated)); ++ if (attr == &vscsi_stats_errors_attr) ++ return sprintf(buf, "%d\n", ++ atomic_read(&adapter->errors)); ++ ++ BUG(); ++ return 0; ++} ++ ++static struct sysfs_ops vscsi_stats_ops = { ++ .show = vscsi_stats_show, ++ .store = NULL, ++}; ++ ++static struct kobj_type ktype_vscsi_stats = { ++ .release = NULL, ++ .sysfs_ops = &vscsi_stats_ops, ++ .default_attrs = vscsi_stats_attrs, ++}; ++ ++ ++static int ibmvscsis_probe(struct vio_dev *dev, const struct vio_device_id *id) ++{ ++ struct server_adapter *adapter; ++ int rc; ++ unsigned int *dma_window; ++ unsigned int dma_window_property_size; ++ ++ adapter = kmalloc(sizeof(*adapter), GFP_KERNEL); ++ if (!adapter) { ++ err("couldn't allocate adapter memory\n"); ++ return -1; ++ } ++ memset(adapter, 0x00, sizeof(*adapter)); ++ adapter->dma_dev = dev; ++ adapter->dev = &dev->dev; ++ dev->driver_data = adapter; ++ sprintf(adapter->name, "%x", dev->unit_address); ++ adapter->lock = SPIN_LOCK_UNLOCKED; ++ ++ dma_window = ++ (unsigned int *)vio_get_attribute(dev, "ibm,my-dma-window", ++ &dma_window_property_size); ++ if (!dma_window) { ++ warn("Couldn't find ibm,my-dma-window property\n"); ++ } ++ ++ adapter->liobn = dma_window[0]; ++ /* RPA docs say that #address-cells is always 1 for virtual ++ devices, but some older boxes' OF returns 2. This should ++ be removed by GA, unless there is legacy OFs that still ++ have 2 or 3 for #address-cells */ ++ /*adapter->riobn = dma_window[2+vio_num_address_cells]; */ ++ ++ /* This is just an ugly kludge. Remove as soon as the OF for all ++ machines actually follow the spec and encodes the offset field ++ as phys-encode (that is, #address-cells wide) */ ++ if (dma_window_property_size == 24) { ++ adapter->riobn = dma_window[3]; ++ } else if (dma_window_property_size == 40) { ++ adapter->riobn = dma_window[5]; ++ } else { ++ warn("Invalid size of ibm,my-dma-window=%i\n", ++ dma_window_property_size); ++ } ++ ++ INIT_WORK(&adapter->crq_task, crq_task, adapter); ++ ++ tasklet_init(&adapter->endio_tasklet, ++ endio_task, (unsigned long)adapter); ++ ++ INIT_LIST_HEAD(&adapter->inflight); ++ ++ /* Initialize the buffer cache */ ++ init_data_buffer(adapter); ++ ++ /* Arbitrarily support 16 IUs right now */ ++ rc = initialize_iu_pool(adapter, 16); ++ if (rc) { ++ kfree(adapter); ++ return rc; ++ } ++ ++ rc = initialize_crq_queue(&adapter->queue, adapter); ++ if (rc != 0) { ++ kfree(adapter); ++ return rc; ++ } ++ ++ set_num_buses(&dev->dev, 1); ++ device_create_file(&dev->dev, &dev_attr_num_buses); ++ ++ adapter->stats_kobj.parent = &dev->dev.kobj; ++ strcpy(adapter->stats_kobj.name, "stats"); ++ adapter->stats_kobj.ktype = & ktype_vscsi_stats; ++ kobject_register(&adapter->stats_kobj); ++ ++ return 0; ++} ++ ++static int ibmvscsis_remove(struct vio_dev *dev) ++{ ++ int bus; ++ int target; ++ unsigned long flags; ++ struct server_adapter *adapter = ++ (struct server_adapter *)dev->driver_data; ++ ++ spin_lock_irqsave(&adapter->lock, flags); ++ ++ /* ++ * Loop through the bus ++ */ ++ for (bus = 0; bus < BUS_PER_ADAPTER; bus++) { ++ /* If this bus exists */ ++ if (adapter->vbus[bus]) { ++ /* loop through the targets */ ++ for (target = 0; target < TARGETS_PER_BUS; target++) { ++ /* If the target exists */ ++ if (adapter->vbus[bus]->vdev[target] && ++ !adapter->vbus[bus]->vdev[target] ++ ->disabled) { ++ deactivate_block_device(adapter-> ++ vbus[bus]->vdev[target]); ++ } ++ } ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ set_num_targets(adapter->vbus[bus], 0); ++ spin_lock_irqsave(&adapter->lock, flags); ++ } ++ } ++ ++ spin_unlock_irqrestore(&adapter->lock, flags); ++ set_num_buses(adapter->dev, 0); ++ release_crq_queue(&adapter->queue, adapter); ++ ++ release_iu_pool(adapter); ++ ++ release_data_buffer(adapter); ++ ++ kobject_unregister(&adapter->stats_kobj); ++ device_remove_file(&dev->dev, &dev_attr_num_buses); ++ ++ kfree(adapter); ++ ++ return 0; ++} ++ ++static struct vio_device_id ibmvscsis_device_table[] __devinitdata = { ++ {"v-scsi-host", "IBM,v-scsi-host"}, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table); ++ ++static struct vio_driver ibmvscsis_driver = { ++ .name = "ibmvscsis", ++ .id_table = ibmvscsis_device_table, ++ .probe = ibmvscsis_probe, ++ .remove = ibmvscsis_remove, ++}; ++ ++static int mod_init(void) ++{ ++ struct device_node *rootdn; ++ char *ppartition_name; ++ char *psystem_id; ++ char *pmodel; ++ unsigned int *p_number_ptr; ++ int rc; ++ ++ /* Retrieve information about this partition */ ++ rootdn = find_path_device("/"); ++ if (rootdn) { ++ pmodel = get_property(rootdn, "model", NULL); ++ psystem_id = get_property(rootdn, "system-id", NULL); ++ if (pmodel && psystem_id) ++ snprintf(system_id,sizeof(system_id), ++ "%s-%s", ++ pmodel, psystem_id); ++ ppartition_name = ++ get_property(rootdn, "ibm,partition-name", NULL); ++ if (ppartition_name) ++ strncpy(partition_name, ppartition_name, ++ sizeof(partition_name)); ++ p_number_ptr = ++ (unsigned int *)get_property(rootdn, "ibm,partition-no", ++ NULL); ++ if (p_number_ptr) ++ partition_number = *p_number_ptr; ++ } ++ ++ info("initialized version "IBMVSCSIS_VERSION"\n"); ++ ++ rc = vio_register_driver(&ibmvscsis_driver); ++ ++ if (rc) { ++ warn("rc %d from vio_register_driver\n", rc); ++ } ++ ++ return rc; ++} ++ ++static void mod_exit(void) ++{ ++ info("terminated\n"); ++ ++ vio_unregister_driver(&ibmvscsis_driver); ++} ++ ++module_init(mod_init); ++module_exit(mod_exit); +diff -aurN a/include/asm-ppc64/vio.h b/include/asm-ppc64/vio.h +--- a/include/asm-ppc64/vio.h 2005-06-17 15:48:29.000000000 -0400 ++++ b/include/asm-ppc64/vio.h 2005-06-18 12:02:58.000000000 -0400 +@@ -91,6 +91,7 @@ + char *type; + uint32_t unit_address; + unsigned int irq; ++ void *driver_data; + + struct device dev; + }; diff --git a/debian/patches-debian/powerpc-serial.patch b/debian/patches-debian/powerpc-serial.patch new file mode 100644 index 000000000..1a5e5e3e5 --- /dev/null +++ b/debian/patches-debian/powerpc-serial.patch @@ -0,0 +1,48 @@ +#! /bin/sh -e +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Description: Disables legacy serial driver on powermacs. +## DP: Patch author: Sven Luther +## DP: Patch author: adapted from the SuSE kernel tree. +## DP: Upstream status: workaround hack waiting for a clean legacy device solution. + +diff -aurN a/drivers/serial/8250.c b/drivers/serial/8250.c +--- a/drivers/serial/8250.c 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/serial/8250.c 2005-06-18 12:05:39.000000000 -0400 +@@ -46,6 +46,10 @@ + + #include "8250.h" + ++#ifdef CONFIG_PPC_MULTIPLATFORM ++#include ++#endif ++ + /* + * Configuration: + * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option +@@ -2188,6 +2192,12 @@ + + static int __init serial8250_console_init(void) + { ++#ifdef CONFIG_PPC_MULTIPLATFORM ++ if(_machine == _MACH_Pmac) { ++ printk("%s: nothing to do on PowerMac\n",__FUNCTION__); ++ return -ENODEV; ++ } ++#endif + serial8250_isa_init_ports(); + register_console(&serial8250_console); + return 0; +@@ -2491,6 +2501,12 @@ + { + int ret, i; + ++#ifdef CONFIG_PPC_MULTIPLATFORM ++ if(_machine == _MACH_Pmac) { ++ printk("%s: nothing to do on PowerMac\n",__FUNCTION__); ++ return -ENODEV; ++ } ++#endif + printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " + "%d ports, IRQ sharing %sabled\n", (int) UART_NR, + share_irqs ? "en" : "dis"); diff --git a/debian/patches-debian/qla2xxx-removed.patch b/debian/patches-debian/qla2xxx-removed.patch new file mode 100644 index 000000000..55c296847 --- /dev/null +++ b/debian/patches-debian/qla2xxx-removed.patch @@ -0,0 +1,23 @@ +diff -aurN a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig +--- a/drivers/scsi/Kconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/scsi/Kconfig 2005-06-18 15:19:35.000000000 -0400 +@@ -1312,8 +1312,6 @@ + To compile this driver as a module, choose M here: the + module will be called qlogicpti. + +-source "drivers/scsi/qla2xxx/Kconfig" +- + config SCSI_LPFC + tristate "Emulex LightPulse Fibre Channel Support" + depends on PCI && SCSI +diff -aurN a/drivers/scsi/Makefile b/drivers/scsi/Makefile +--- a/drivers/scsi/Makefile 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/scsi/Makefile 2005-06-18 15:19:15.000000000 -0400 +@@ -79,7 +79,6 @@ + obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o + obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o + obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o +-obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/ + obj-$(CONFIG_SCSI_LPFC) += lpfc/ + obj-$(CONFIG_SCSI_PAS16) += pas16.o + obj-$(CONFIG_SCSI_SEAGATE) += seagate.o diff --git a/debian/patches-debian/remove-references-to-removed-drivers.patch b/debian/patches-debian/remove-references-to-removed-drivers.patch new file mode 100644 index 000000000..0afdc433d --- /dev/null +++ b/debian/patches-debian/remove-references-to-removed-drivers.patch @@ -0,0 +1,410 @@ +diff -aurN a/arch/alpha/defconfig b/arch/alpha/defconfig +--- a/arch/alpha/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/alpha/defconfig 2005-06-18 16:11:08.000000000 -0400 +@@ -512,7 +512,6 @@ + # CONFIG_B44 is not set + # CONFIG_FORCEDETH is not set + # CONFIG_CS89x0 is not set +-# CONFIG_DGRS is not set + # CONFIG_EEPRO100 is not set + # CONFIG_E100 is not set + # CONFIG_LNE390 is not set +@@ -533,7 +532,6 @@ + # + # Ethernet (1000 Mbit) + # +-# CONFIG_ACENIC is not set + # CONFIG_DL2K is not set + # CONFIG_E1000 is not set + # CONFIG_NS83820 is not set +diff -aurN a/arch/arm/configs/lpd7a404_defconfig b/arch/arm/configs/lpd7a404_defconfig +--- a/arch/arm/configs/lpd7a404_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/arm/configs/lpd7a404_defconfig 2005-06-18 16:30:01.000000000 -0400 +@@ -676,8 +676,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/arch/i386/defconfig b/arch/i386/defconfig +--- a/arch/i386/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/i386/defconfig 2005-06-18 16:22:33.000000000 -0400 +@@ -624,7 +624,6 @@ + # CONFIG_B44 is not set + # CONFIG_FORCEDETH is not set + # CONFIG_CS89x0 is not set +-# CONFIG_DGRS is not set + # CONFIG_EEPRO100 is not set + # CONFIG_E100 is not set + # CONFIG_FEALNX is not set +@@ -646,7 +645,6 @@ + # + # Ethernet (1000 Mbit) + # +-# CONFIG_ACENIC is not set + # CONFIG_DL2K is not set + # CONFIG_E1000 is not set + # CONFIG_NS83820 is not set +@@ -1026,7 +1024,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + + # + # Video4Linux support is needed for USB Multimedia device support +@@ -1054,8 +1051,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ia64/configs/zx1_defconfig b/arch/ia64/configs/zx1_defconfig +--- a/arch/ia64/configs/zx1_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ia64/configs/zx1_defconfig 2005-06-18 16:27:49.000000000 -0400 +@@ -1014,8 +1014,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ia64/defconfig b/arch/ia64/defconfig +--- a/arch/ia64/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ia64/defconfig 2005-06-18 16:28:03.000000000 -0400 +@@ -482,7 +482,6 @@ + # CONFIG_ADAPTEC_STARFIRE is not set + # CONFIG_B44 is not set + # CONFIG_FORCEDETH is not set +-# CONFIG_DGRS is not set + CONFIG_EEPRO100=m + # CONFIG_EEPRO100_PIO is not set + CONFIG_E100=m +@@ -500,7 +499,6 @@ + # + # Ethernet (1000 Mbit) + # +-# CONFIG_ACENIC is not set + # CONFIG_DL2K is not set + CONFIG_E1000=y + # CONFIG_E1000_NAPI is not set +@@ -879,7 +877,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + + # + # Video4Linux support is needed for USB Multimedia device support +@@ -906,8 +903,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig +--- a/arch/mips/configs/rm200_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/mips/configs/rm200_defconfig 2005-06-18 16:27:28.000000000 -0400 +@@ -1098,8 +1098,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + CONFIG_USB_AUERSWALD=m + CONFIG_USB_RIO500=m + CONFIG_USB_LEGOTOWER=m +diff -aurN a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig +--- a/arch/parisc/configs/c3000_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/parisc/configs/c3000_defconfig 2005-06-18 16:30:26.000000000 -0400 +@@ -892,8 +892,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/parisc/defconfig b/arch/parisc/defconfig +--- a/arch/parisc/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/parisc/defconfig 2005-06-18 16:30:45.000000000 -0400 +@@ -345,7 +345,6 @@ + # CONFIG_AC3200 is not set + # CONFIG_B44 is not set + # CONFIG_FORCEDETH is not set +-# CONFIG_DGRS is not set + # CONFIG_EEPRO100 is not set + # CONFIG_E100 is not set + # CONFIG_LNE390 is not set +@@ -365,7 +364,6 @@ + # + # Ethernet (1000 Mbit) + # +-# CONFIG_ACENIC is not set + CONFIG_DL2K=y + # CONFIG_E1000 is not set + # CONFIG_NS83820 is not set +@@ -702,7 +700,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + + # + # Video4Linux support is needed for USB Multimedia device support +@@ -730,8 +727,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ppc/configs/common_defconfig b/arch/ppc/configs/common_defconfig +--- a/arch/ppc/configs/common_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc/configs/common_defconfig 2005-06-18 16:24:38.000000000 -0400 +@@ -1213,8 +1213,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ppc/configs/lopec_defconfig b/arch/ppc/configs/lopec_defconfig +--- a/arch/ppc/configs/lopec_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc/configs/lopec_defconfig 2005-06-18 16:25:01.000000000 -0400 +@@ -681,8 +681,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ppc/configs/pmac_defconfig b/arch/ppc/configs/pmac_defconfig +--- a/arch/ppc/configs/pmac_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc/configs/pmac_defconfig 2005-06-18 16:25:19.000000000 -0400 +@@ -1278,8 +1278,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/arch/ppc/configs/sandpoint_defconfig b/arch/ppc/configs/sandpoint_defconfig +--- a/arch/ppc/configs/sandpoint_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc/configs/sandpoint_defconfig 2005-06-18 16:24:50.000000000 -0400 +@@ -600,8 +600,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_TIGL is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set +diff -aurN a/arch/ppc64/configs/g5_defconfig b/arch/ppc64/configs/g5_defconfig +--- a/arch/ppc64/configs/g5_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc64/configs/g5_defconfig 2005-06-18 16:28:43.000000000 -0400 +@@ -1059,8 +1059,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/arch/ppc64/configs/pSeries_defconfig b/arch/ppc64/configs/pSeries_defconfig +--- a/arch/ppc64/configs/pSeries_defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc64/configs/pSeries_defconfig 2005-06-18 16:29:21.000000000 -0400 +@@ -1014,8 +1014,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/arch/ppc64/defconfig b/arch/ppc64/defconfig +--- a/arch/ppc64/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/ppc64/defconfig 2005-06-18 16:29:36.000000000 -0400 +@@ -591,7 +591,6 @@ + # CONFIG_ADAPTEC_STARFIRE is not set + # CONFIG_B44 is not set + # CONFIG_FORCEDETH is not set +-# CONFIG_DGRS is not set + # CONFIG_EEPRO100 is not set + CONFIG_E100=y + # CONFIG_FEALNX is not set +@@ -607,8 +606,6 @@ + # + # Ethernet (1000 Mbit) + # +-CONFIG_ACENIC=y +-CONFIG_ACENIC_OMIT_TIGON_I=y + # CONFIG_DL2K is not set + CONFIG_E1000=y + # CONFIG_E1000_NAPI is not set +@@ -1040,7 +1037,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + + # + # Video4Linux support is needed for USB Multimedia device support +@@ -1069,8 +1065,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/arch/sparc64/defconfig b/arch/sparc64/defconfig +--- a/arch/sparc64/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/sparc64/defconfig 2005-06-18 16:23:32.000000000 -0400 +@@ -904,7 +904,6 @@ + CONFIG_ADAPTEC_STARFIRE_NAPI=y + CONFIG_B44=m + CONFIG_FORCEDETH=m +-CONFIG_DGRS=m + CONFIG_EEPRO100=m + CONFIG_E100=m + CONFIG_FEALNX=m +@@ -926,8 +925,6 @@ + # + # Ethernet (1000 Mbit) + # +-CONFIG_ACENIC=m +-# CONFIG_ACENIC_OMIT_TIGON_I is not set + CONFIG_DL2K=m + CONFIG_E1000=m + CONFIG_E1000_NAPI=y +@@ -1723,7 +1720,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + # CONFIG_USB_VICAM is not set + # CONFIG_USB_DSBR is not set + # CONFIG_USB_IBMCAM is not set +@@ -1817,8 +1813,6 @@ + # + # USB Miscellaneous drivers + # +-CONFIG_USB_EMI62=m +-CONFIG_USB_EMI26=m + CONFIG_USB_AUERSWALD=m + CONFIG_USB_RIO500=m + CONFIG_USB_LEGOTOWER=m +diff -aurN a/arch/x86_64/defconfig b/arch/x86_64/defconfig +--- a/arch/x86_64/defconfig 2005-06-17 15:48:29.000000000 -0400 ++++ b/arch/x86_64/defconfig 2005-06-18 16:31:01.000000000 -0400 +@@ -530,7 +530,6 @@ + # CONFIG_ADAPTEC_STARFIRE is not set + # CONFIG_B44 is not set + CONFIG_FORCEDETH=y +-# CONFIG_DGRS is not set + # CONFIG_EEPRO100 is not set + # CONFIG_E100 is not set + # CONFIG_FEALNX is not set +@@ -550,7 +549,6 @@ + # + # Ethernet (1000 Mbit) + # +-# CONFIG_ACENIC is not set + # CONFIG_DL2K is not set + CONFIG_E1000=y + # CONFIG_E1000_NAPI is not set +@@ -862,7 +860,6 @@ + # + # USB Multimedia devices + # +-# CONFIG_USB_DABUSB is not set + + # + # Video4Linux support is needed for USB Multimedia device support +@@ -890,8 +887,6 @@ + # + # USB Miscellaneous drivers + # +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set + # CONFIG_USB_AUERSWALD is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_LEGOTOWER is not set +diff -aurN a/drivers/usb/Makefile b/drivers/usb/Makefile +--- a/drivers/usb/Makefile 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/usb/Makefile 2005-06-18 16:32:12.000000000 -0400 +@@ -59,8 +59,6 @@ + + obj-$(CONFIG_USB_AUERSWALD) += misc/ + obj-$(CONFIG_USB_CYTHERM) += misc/ +-obj-$(CONFIG_USB_EMI26) += misc/ +-obj-$(CONFIG_USB_EMI62) += misc/ + obj-$(CONFIG_USB_IDMOUSE) += misc/ + obj-$(CONFIG_USB_LCD) += misc/ + obj-$(CONFIG_USB_LED) += misc/ +diff -aurN a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig +--- a/drivers/usb/misc/Kconfig 2005-06-18 11:23:01.000000000 -0400 ++++ b/drivers/usb/misc/Kconfig 2005-06-18 16:31:32.000000000 -0400 +@@ -4,19 +4,6 @@ + comment "USB Miscellaneous drivers" + depends on USB + +-config USB_EMI26 +- tristate "EMI 2|6 USB Audio interface support" +- depends on USB +- ---help--- +- This driver loads firmware to Emagic EMI 2|6 low latency USB +- Audio interface. +- +- After firmware load the device is handled with standard linux +- USB Audio driver. +- +- To compile this driver as a module, choose M here: the +- module will be called emi26. +- + config USB_AUERSWALD + tristate "USB Auerswald ISDN support (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL +diff -aurN a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile +--- a/drivers/usb/misc/Makefile 2005-06-18 11:23:01.000000000 -0400 ++++ b/drivers/usb/misc/Makefile 2005-06-18 16:31:47.000000000 -0400 +@@ -5,7 +5,6 @@ + + obj-$(CONFIG_USB_AUERSWALD) += auerswald.o + obj-$(CONFIG_USB_CYTHERM) += cytherm.o +-obj-$(CONFIG_USB_EMI26) += emi26.o + obj-$(CONFIG_USB_IDMOUSE) += idmouse.o + obj-$(CONFIG_USB_LCD) += usblcd.o + obj-$(CONFIG_USB_LED) += usbled.o +@@ -16,4 +15,4 @@ + obj-$(CONFIG_USB_TEST) += usbtest.o + obj-$(CONFIG_USB_USS720) += uss720.o + +-obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ +\ No newline at end of file ++obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ diff --git a/debian/patches-debian/series/2.6.12-1 b/debian/patches-debian/series/2.6.12-1 new file mode 100644 index 000000000..7fd39d0f4 --- /dev/null +++ b/debian/patches-debian/series/2.6.12-1 @@ -0,0 +1,21 @@ ++ amd64-int3-fix.patch ++ amd64-outs.patch ++ drivers-add-scsi_changer.patch ++ drivers-ide-__devinit.patch ++ drivers-ide-dma-blacklist-toshiba.patch ++ drivers-scsi-megaraid_splitup.patch ++ fbdev-radeon-noaccel.patch ++ fs-asfs-2.patch ++ ia64-irq-affinity-upfix.patch ++ modular-ide-pnp.patch ++ modular-ide.patch ++ modular-vesafb.patch ++ powerpc-calibrate-tau.patch ++ powerpc-fix-power3-ftbfs.patch ++ powerpc-g3-750cxe.patch ++ powerpc-pmac-cache-power34-fix.patch ++ powerpc-ppc64-ibmvscsi.patch ++ powerpc-serial.patch ++ qla2xxx-removed.patch ++ remove-references-to-removed-drivers.patch ++ tty-locking-fixes9.patch diff --git a/debian/patches-debian/sparc64-hme-lockup.dpatch b/debian/patches-debian/sparc64-hme-lockup.dpatch new file mode 100644 index 000000000..8750d3f5d --- /dev/null +++ b/debian/patches-debian/sparc64-hme-lockup.dpatch @@ -0,0 +1,16 @@ +# origin: Debian (bcollins) +# cset: n/a +# inclusion: not suitable for upstream +# revision date: 2004-10-08 + +diff -aurN a/drivers/net/sunhme.c b/drivers/net/sunhme.c +--- a/drivers/net/sunhme.c 2005-06-17 15:48:29.000000000 -0400 ++++ b/drivers/net/sunhme.c 2005-06-18 12:12:18.000000000 -0400 +@@ -1996,6 +1996,7 @@ + } + hp->tx_old = elem; + TXD((">")); ++ udelay(1); + + if (netif_queue_stopped(dev) && + TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1)) diff --git a/debian/patches-debian/tty-locking-fixes9.patch b/debian/patches-debian/tty-locking-fixes9.patch new file mode 100644 index 000000000..c9210a35e --- /dev/null +++ b/debian/patches-debian/tty-locking-fixes9.patch @@ -0,0 +1,12 @@ +--- kernel-source-2.6.11-2.6.11-orig/drivers/serial/serial_core.c 2005-03-02 08:37:50.000000000 +0100 ++++ kernel-source-2.6.11-2.6.11/drivers/serial/serial_core.c 2005-03-05 10:46:43.417109240 +0100 +@@ -108,7 +108,8 @@ + static void uart_tasklet_action(unsigned long data) + { + struct uart_state *state = (struct uart_state *)data; +- tty_wakeup(state->info->tty); ++ if (state->info->tty) ++ tty_wakeup(state->info->tty); + } + + static inline void