r3353 - in branches/kernel-image-2.6.12/debian: . patches-debian patches-debian/series
maximilian attems
maks-guest@costa.debian.org
Sat, 18 Jun 2005 21:59:56 +0000
Author: maks-guest
Date: 2005-06-18 21:59:46 +0000 (Sat, 18 Jun 2005)
New Revision: 3353
Added:
branches/kernel-image-2.6.12/debian/patches-debian/
branches/kernel-image-2.6.12/debian/patches-debian/amd64-int3-fix.patch
branches/kernel-image-2.6.12/debian/patches-debian/amd64-outs.patch
branches/kernel-image-2.6.12/debian/patches-debian/drivers-add-scsi_changer.patch
branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-__devinit.patch
branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch
branches/kernel-image-2.6.12/debian/patches-debian/drivers-scsi-megaraid_splitup.patch
branches/kernel-image-2.6.12/debian/patches-debian/fbdev-radeon-noaccel.patch
branches/kernel-image-2.6.12/debian/patches-debian/fs-asfs-2.patch
branches/kernel-image-2.6.12/debian/patches-debian/ia64-irq-affinity-upfix.patch
branches/kernel-image-2.6.12/debian/patches-debian/modular-ide-pnp.patch
branches/kernel-image-2.6.12/debian/patches-debian/modular-ide.patch
branches/kernel-image-2.6.12/debian/patches-debian/modular-vesafb.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-calibrate-tau.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-fix-power3-ftbfs.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-g3-750cxe.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch
branches/kernel-image-2.6.12/debian/patches-debian/powerpc-serial.patch
branches/kernel-image-2.6.12/debian/patches-debian/qla2xxx-removed.patch
branches/kernel-image-2.6.12/debian/patches-debian/remove-references-to-removed-drivers.patch
branches/kernel-image-2.6.12/debian/patches-debian/series/
branches/kernel-image-2.6.12/debian/patches-debian/series/2.6.12-1
branches/kernel-image-2.6.12/debian/patches-debian/sparc64-hme-lockup.dpatch
branches/kernel-image-2.6.12/debian/patches-debian/tty-locking-fixes9.patch
Log:
add all the remaining patches to the new branch
Added: branches/kernel-image-2.6.12/debian/patches-debian/amd64-int3-fix.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/amd64-int3-fix.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/amd64-int3-fix.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,18 @@
+## amd64-int3-fix.dpatch by <fschueler@gmx.net>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Revert int3 handling
+## DP: Patch author: Andi Kleen <ak@muc.de>
+## 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;
Added: branches/kernel-image-2.6.12/debian/patches-debian/amd64-outs.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/amd64-outs.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/amd64-outs.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,24 @@
+#! /bin/sh -e
+## <PATCHNAME>.dpatch by <PATCH_AUTHOR@EMAI>
+##
+## 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 <micah@riseup.net> (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)
Added: branches/kernel-image-2.6.12/debian/patches-debian/drivers-add-scsi_changer.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/drivers-add-scsi_changer.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/drivers-add-scsi_changer.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,1482 @@
+From: Gerd Knorr <kraxel@bytesex.org>
+
+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 <kraxel@bytesex.org>
+Signed-off-by: Andrew Morton <akpm@osdl.org>
+---
+
+ 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=<seconds>
++ timeout for the INITIALIZE ELEMENT STATUS command
++ (default: 3600).
++
++timeout_move=<seconds>
++ timeout for all other commands (default: 120).
++
++dt_id=<id1>,<id2>,...
++dt_lun=<lun1>,<lun2>,...
++ 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 <moehwald@hdg.de>
++ Dane Jasper <dane@sonic.net>
++ R. Scott Bailey <sbailey@dsddi.eds.com>
++ Jonathan Corbet <corbet@atd.ucar.edu>
++
++Special thanks go to
++ Martin Kuehne <martin.kuehne@bnbt.de>
++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 <kraxel@bytesex.org>
+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 <file:Documentation/scsi-changer.txt> 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 <file:Documentation/modules.txt> and
++ <file:Documentation/scsi.txt>. 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 <kraxel@bytesex.org>
++ *
++ */
++
++#define VERSION "0.24"
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/blkdev.h>
++#include <linux/completion.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/ioctl32.h>
++#include <linux/compat.h>
++#include <linux/chio.h> /* here are all the ioctls */
++
++#include <scsi/scsi.h>
++#include <scsi/scsi_cmnd.h>
++#include <scsi/scsi_driver.h>
++#include <scsi/scsi_ioctl.h>
++#include <scsi/scsi_host.h>
++#include <scsi/scsi_device.h>
++#include <scsi/scsi_request.h>
++#include <scsi/scsi_dbg.h>
++
++#define CH_DT_MAX 16
++#define CH_TYPES 8
++
++MODULE_DESCRIPTION("device driver for scsi media changer devices");
++MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
++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
Added: branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-__devinit.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-__devinit.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-__devinit.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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;
+
Added: branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/drivers-ide-dma-blacklist-toshiba.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,21 @@
+#! /bin/sh -e
+## <PATCHNAME>.dpatch by <PATCH_AUTHOR@EMAI>
+##
+## 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" },
Added: branches/kernel-image-2.6.12/debian/patches-debian/drivers-scsi-megaraid_splitup.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/drivers-scsi-megaraid_splitup.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/drivers-scsi-megaraid_splitup.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,110 @@
+# From Fabio M. Di Nitto <fabbione@ubuntu.com>
+# 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: */
Added: branches/kernel-image-2.6.12/debian/patches-debian/fbdev-radeon-noaccel.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/fbdev-radeon-noaccel.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/fbdev-radeon-noaccel.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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;
+ }
Added: branches/kernel-image-2.6.12/debian/patches-debian/fs-asfs-2.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/fs-asfs-2.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/fs-asfs-2.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,5262 @@
+## fs-asfs.dpatch by Sven Luther <luther@debian.org>
+##
+## 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 <marek@amiga.pl>
+## 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/<volume> (where
++<volume> 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 <marek@amiga.pl>.
++Mail me if you have any suggestions or found a bug.
++
++Copyright (C) 2003,2004,2005 Marek 'March' Szyprowski <marek@amiga.pl>
++
++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 <file:Documentation/filesystems/asfs.txt>
++
++ 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 <file:Documentation/filesystems/asfs.txt>
++
++ 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 <marek@amiga.pl>
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++#include "bitfuncs.h"
++
++#include <asm/byteorder.h>
++
++#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 <linux/types.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <asm/byteorder.h>
++#include <linux/amigasfs.h>
++
++#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 <linux/types.h>
++#include <asm/byteorder.h>
++#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 <linux/types.h>
++#include <asm/byteorder.h>
++
++#include <asm/bitops.h>
++#include <linux/bitops.h>
++
++/* 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 <marek@amiga.pl>
++ *
++ *
++ * 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 <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++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 <marek@amiga.pl>
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++ /* 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 <marek@amiga.pl>
++ *
++ *
++ * 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 <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/pagemap.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++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 <marek@amiga.pl>
++ *
++ *
++ * 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 <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/smp_lock.h>
++#include <linux/time.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/dirent.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++/* 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 <marek@amiga.pl>
++ *
++ *
++ * 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 <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/string.h>
++#include <linux/nls.h>
++#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 <marek@amiga.pl>
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++/* 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 <marek@amiga.pl>
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++
++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 <marek@amiga.pl>
++ *
++ * 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 <chexum@shadow.banki.hu>
++ *
++ * 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/smp_lock.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/parser.h>
++#include <linux/nls.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++#include <asm/uaccess.h>
++
++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 <marek@amiga.pl>");
++
++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 <marek@amiga.pl>
++ *
++ *
++ * 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 <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/pagemap.h>
++#include <linux/nls.h>
++#include "asfs_fs.h"
++
++#include <asm/byteorder.h>
++#include <asm/uaccess.h>
++
++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 <linux/types.h>
++
++/* 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<<FIBB_SCRIPT)
++#define FIBF_PURE (1<<FIBB_PURE)
++#define FIBF_ARCHIVE (1<<FIBB_ARCHIVE)
++#define FIBF_READ (1<<FIBB_READ)
++#define FIBF_WRITE (1<<FIBB_WRITE)
++#define FIBF_EXECUTE (1<<FIBB_EXECUTE)
++#define FIBF_DELETE (1<<FIBB_DELETE)
++
++/* name hashing macro */
++
++#define HASHCHAIN(x) (u16)(x % (u16)(((sb->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
Added: branches/kernel-image-2.6.12/debian/patches-debian/ia64-irq-affinity-upfix.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/ia64-irq-affinity-upfix.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/ia64-irq-affinity-upfix.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -0,0 +1,19 @@
+## DP: Description: Add a no-op set_irq_affinity_info() for non-SMP
+## DP: Patch author: dann frazier <dannf@hp.com>
+## 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
Added: branches/kernel-image-2.6.12/debian/patches-debian/modular-ide-pnp.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/modular-ide-pnp.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/modular-ide-pnp.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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);
+
+ /*
Added: branches/kernel-image-2.6.12/debian/patches-debian/modular-ide.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/modular-ide.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/modular-ide.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/ide.h>
++#include <linux/config.h>
+
+ 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 *);
Added: branches/kernel-image-2.6.12/debian/patches-debian/modular-vesafb.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/modular-vesafb.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/modular-vesafb.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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");
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-calibrate-tau.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-calibrate-tau.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-calibrate-tau.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <nd@bplan-gmbh.de>
+## 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 */
+
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-fix-power3-ftbfs.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-fix-power3-ftbfs.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-fix-power3-ftbfs.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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;
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-g3-750cxe.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-g3-750cxe.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-g3-750cxe.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <nd@bplan-gmbh.de>
+## 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,
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-pmac-cache-power34-fix.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <luther@debian.org>
+## 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
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-ppc64-ibmvscsi.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/pagemap.h>
++#include <linux/dma-mapping.h>
++#include <linux/sched.h>
++#include <linux/blkdev.h>
++#include <linux/fs.h>
++#include <linux/bio.h>
++
++#include <asm/hvcall.h>
++#include <asm/vio.h>
++#include <asm/iommu.h>
++
++#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;
+ };
Added: branches/kernel-image-2.6.12/debian/patches-debian/powerpc-serial.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/powerpc-serial.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/powerpc-serial.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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 <luther@debian.org>
+## 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 <asm/processor.h>
++#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");
Added: branches/kernel-image-2.6.12/debian/patches-debian/qla2xxx-removed.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/qla2xxx-removed.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/qla2xxx-removed.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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
Added: branches/kernel-image-2.6.12/debian/patches-debian/remove-references-to-removed-drivers.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/remove-references-to-removed-drivers.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/remove-references-to-removed-drivers.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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/
Added: branches/kernel-image-2.6.12/debian/patches-debian/series/2.6.12-1
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/series/2.6.12-1 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/series/2.6.12-1 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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
Added: branches/kernel-image-2.6.12/debian/patches-debian/sparc64-hme-lockup.dpatch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/sparc64-hme-lockup.dpatch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/sparc64-hme-lockup.dpatch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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))
Added: branches/kernel-image-2.6.12/debian/patches-debian/tty-locking-fixes9.patch
===================================================================
--- branches/kernel-image-2.6.12/debian/patches-debian/tty-locking-fixes9.patch 2005-06-18 21:47:41 UTC (rev 3352)
+++ branches/kernel-image-2.6.12/debian/patches-debian/tty-locking-fixes9.patch 2005-06-18 21:59:46 UTC (rev 3353)
@@ -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