diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/alpha/kernel/entry.S linux.21rc1-ac2/arch/alpha/kernel/entry.S --- linux.21rc1/arch/alpha/kernel/entry.S 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/alpha/kernel/entry.S 2003-04-22 16:44:36.000000000 +0100 @@ -690,6 +690,7 @@ .end entSys .globl ret_from_fork +#if CONFIG_SMP .align 3 .ent ret_from_fork ret_from_fork: @@ -697,6 +698,9 @@ mov $17,$16 jsr $31,schedule_tail .end ret_from_fork +#else +ret_from_fork = ret_from_sys_call +#endif .align 3 .ent reschedule diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/alpha/kernel/process.c linux.21rc1-ac2/arch/alpha/kernel/process.c --- linux.21rc1/arch/alpha/kernel/process.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/alpha/kernel/process.c 2003-04-22 16:44:36.000000000 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -74,9 +75,6 @@ cpu_idle(void) { /* An endless idle loop with no priority at all. */ - current->nice = 20; - current->counter = -100; - while (1) { /* FIXME -- EV6 and LCA45 know how to power down the CPU. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/alpha/kernel/smp.c linux.21rc1-ac2/arch/alpha/kernel/smp.c --- linux.21rc1/arch/alpha/kernel/smp.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/alpha/kernel/smp.c 2003-04-22 16:44:36.000000000 +0100 @@ -81,6 +81,7 @@ int smp_num_probed; /* Internal processor count */ int smp_num_cpus = 1; /* Number that came online. */ int smp_threads_ready; /* True once the per process idle is forked. */ +unsigned long cache_decay_ticks; int __cpu_number_map[NR_CPUS]; int __cpu_logical_map[NR_CPUS]; @@ -155,11 +156,6 @@ { int cpuid = hard_smp_processor_id(); - if (current != init_tasks[cpu_number_map(cpuid)]) { - printk("BUG: smp_calling: cpu %d current %p init_tasks[cpu_number_map(cpuid)] %p\n", - cpuid, current, init_tasks[cpu_number_map(cpuid)]); - } - DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state)); /* Turn on machine checks. */ @@ -217,9 +213,6 @@ DBGS(("smp_callin: commencing CPU %d current %p\n", cpuid, current)); - /* Setup the scheduler for this processor. */ - init_idle(); - /* ??? This should be in init_idle. */ atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; @@ -449,14 +442,11 @@ if (idle == &init_task) panic("idle process is init_task for CPU %d", cpuid); - idle->processor = cpuid; - idle->cpus_runnable = 1 << cpuid; /* we schedule the first task manually */ + init_idle(idle, cpuid); + unhash_process(idle); + __cpu_logical_map[cpunum] = cpuid; __cpu_number_map[cpuid] = cpunum; - - del_from_runqueue(idle); - unhash_process(idle); - init_tasks[cpunum] = idle; DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", cpuid, idle->state, idle->flags)); @@ -563,13 +553,10 @@ __cpu_number_map[boot_cpuid] = 0; __cpu_logical_map[0] = boot_cpuid; - current->processor = boot_cpuid; smp_store_cpu_info(boot_cpuid); smp_setup_percpu_timer(boot_cpuid); - init_idle(); - /* ??? This should be in init_idle. */ atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/alpha/kernel/srmcons.c linux.21rc1-ac2/arch/alpha/kernel/srmcons.c --- linux.21rc1/arch/alpha/kernel/srmcons.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/alpha/kernel/srmcons.c 2003-04-22 16:44:36.000000000 +0100 @@ -260,7 +260,7 @@ spin_lock_irqsave(&srmconsp->lock, flags); - if (tty->count == 1) { + if (atomic_read(&tty->count) == 1) { srmconsp->tty = NULL; del_timer(&srmconsp->timer); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/alpha/mm/fault.c linux.21rc1-ac2/arch/alpha/mm/fault.c --- linux.21rc1/arch/alpha/mm/fault.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/alpha/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -122,8 +122,6 @@ goto bad_area; if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (expand_stack(vma, address)) goto bad_area; /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/arm/config.in linux.21rc1-ac2/arch/arm/config.in --- linux.21rc1/arch/arm/config.in 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/arm/config.in 2003-04-22 16:44:36.000000000 +0100 @@ -646,6 +646,7 @@ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL dep_bool ' Debug memory allocations' CONFIG_DEBUG_SLAB $CONFIG_DEBUG_KERNEL dep_bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_DEBUG_KERNEL +dep_bool ' Morse code panics' CONFIG_PANIC_MORSE $CONFIG_DEBUG_KERNEL $CONFIG_PC_KEYB dep_bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK $CONFIG_DEBUG_KERNEL dep_bool ' Wait queue debugging' CONFIG_DEBUG_WAITQ $CONFIG_DEBUG_KERNEL dep_bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE $CONFIG_DEBUG_KERNEL diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/arm/mm/fault-common.c linux.21rc1-ac2/arch/arm/mm/fault-common.c --- linux.21rc1/arch/arm/mm/fault-common.c 2003-04-22 16:38:57.000000000 +0100 +++ linux.21rc1-ac2/arch/arm/mm/fault-common.c 2003-04-22 16:44:36.000000000 +0100 @@ -229,7 +229,7 @@ goto survive; check_stack: - if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) + if (!expand_stack(vma, addr)) goto good_area; out: return fault; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/cris/drivers/serial.c linux.21rc1-ac2/arch/cris/drivers/serial.c --- linux.21rc1/arch/cris/drivers/serial.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/cris/drivers/serial.c 2003-04-22 16:44:36.000000000 +0100 @@ -3293,7 +3293,7 @@ printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/boot/setup.S linux.21rc1-ac2/arch/i386/boot/setup.S --- linux.21rc1/arch/i386/boot/setup.S 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/boot/setup.S 2003-04-22 16:44:36.000000000 +0100 @@ -45,6 +45,10 @@ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes * by Robert Schwebel, December 2001 * + * BIOS Enhanced Disk Drive support + * by Matt Domsch October 2002 + * conformant to T13 Committee www.t13.org + * projects 1572D, 1484D, 1386D, 1226DT */ #include @@ -53,6 +57,7 @@ #include #include #include +#include #include /* Signature words to ensure LILO loaded us right */ @@ -543,6 +548,70 @@ done_apm_bios: #endif +#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) +# Do the BIOS Enhanced Disk Drive calls +# This consists of two calls: +# int 13h ah=41h "Check Extensions Present" +# int 13h ah=48h "Get Device Parameters" +# +# A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use +# in the empty_zero_page at EDDBUF. The first four bytes of which are +# used to store the device number, interface support map and version +# results from fn41. The following 74 bytes are used to store +# the results from fn48. Starting from device 80h, fn41, then fn48 +# are called and their results stored in EDDBUF+n*(EDDEXTSIZE+EDDPARMIZE). +# Then the pointer is incremented to store the data for the next call. +# This repeats until either a device doesn't exist, or until EDDMAXNR +# devices have been stored. +# The one tricky part is that ds:si always points four bytes into +# the structure, and the fn41 results are stored at offsets +# from there. This removes the need to increment the pointer for +# every store, and leaves it ready for the fn48 call. +# A second one-byte buffer, EDDNR, in the empty_zero_page stores +# the number of BIOS devices which exist, up to EDDMAXNR. +# In setup.c, copy_edd() stores both empty_zero_page buffers away +# for later use, as they would get overwritten otherwise. +# This code is sensitive to the size of the structs in edd.h +edd_start: + # %ds points to the bootsector + # result buffer for fn48 + movw $EDDBUF+EDDEXTSIZE, %si # in ds:si, fn41 results + # kept just before that + movb $0, (EDDNR) # zero value at EDDNR + movb $0x80, %dl # BIOS device 0x80 + +edd_check_ext: + movb $CHECKEXTENSIONSPRESENT, %ah # Function 41 + movw $EDDMAGIC1, %bx # magic + int $0x13 # make the call + jc edd_done # no more BIOS devices + + cmpw $EDDMAGIC2, %bx # is magic right? + jne edd_next # nope, next... + + movb %dl, %ds:-4(%si) # store device number + movb %ah, %ds:-3(%si) # store version + movw %cx, %ds:-2(%si) # store extensions + incb (EDDNR) # note that we stored something + +edd_get_device_params: + movw $EDDPARMSIZE, %ds:(%si) # put size + movb $GETDEVICEPARAMETERS, %ah # Function 48 + int $0x13 # make the call + # Don't check for fail return + # it doesn't matter. + movw %si, %ax # increment si + addw $EDDPARMSIZE+EDDEXTSIZE, %ax + movw %ax, %si + +edd_next: + incb %dl # increment to next device + cmpb $EDDMAXNR, (EDDNR) # Out of space? + jb edd_check_ext # keep looping + +edd_done: +#endif + # Now we want to move to protected mode ... cmpw $0, %cs:realmode_swtch jz rmodeswtch_normal diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/config.in linux.21rc1-ac2/arch/i386/config.in --- linux.21rc1/arch/i386/config.in 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/config.in 2003-04-25 13:33:12.000000000 +0100 @@ -194,6 +194,23 @@ bool 'Machine Check Exception' CONFIG_X86_MCE +mainmenu_option next_comment +comment 'CPU Frequency scaling' +dep_bool 'CPU Frequency scaling (EXPERIMENTAL)' CONFIG_CPU_FREQ $CONFIG_EXPERIMENTAL +if [ "$CONFIG_CPU_FREQ" = "y" ]; then + bool ' /proc/sys/cpu/ interface (2.4. / OLD)' CONFIG_CPU_FREQ_24_API + tristate ' AMD Mobile K6-2/K6-3 PowerNow!' CONFIG_X86_POWERNOW_K6 + if [ "$CONFIG_MELAN" = "y" ]; then + tristate ' AMD Elan' CONFIG_ELAN_CPUFREQ + fi + tristate ' VIA Cyrix III Longhaul' CONFIG_X86_LONGHAUL + tristate ' Intel Speedstep' CONFIG_X86_SPEEDSTEP + tristate ' Intel Pentium 4 clock modulation' CONFIG_X86_P4_CLOCKMOD + tristate ' Transmeta LongRun' CONFIG_X86_LONGRUN + tristate ' Cyrix MediaGX/NatSemi Geode Suspend Modulation' CONFIG_X86_GX_SUSPMOD +fi +endmenu + tristate 'Toshiba Laptop support' CONFIG_TOSHIBA tristate 'Dell laptop support' CONFIG_I8K @@ -201,6 +218,10 @@ tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'BIOS Enhanced Disk Drive calls determine boot disk (EXPERIMENTAL)' CONFIG_EDD +fi + choice 'High Memory Support' \ "off CONFIG_NOHIGHMEM \ 4GB CONFIG_HIGHMEM4G \ @@ -231,6 +252,7 @@ define_bool CONFIG_X86_IO_APIC y fi else + bool 'Clustered APIC support' CONFIG_X86_CLUSTERED_APIC bool 'Multi-node NUMA system support' CONFIG_X86_NUMA if [ "$CONFIG_X86_NUMA" = "y" ]; then #Platform Choices @@ -292,6 +314,8 @@ bool 'ISA bus support' CONFIG_ISA fi +tristate 'NatSemi SCx200 support' CONFIG_SCx200 + source drivers/pci/Config.in bool 'EISA support' CONFIG_EISA @@ -324,6 +348,8 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Kernel .config support' CONFIG_IKCONFIG + bool 'Power Management support' CONFIG_PM if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -474,12 +500,13 @@ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then bool ' Check for stack overflows' CONFIG_DEBUG_STACKOVERFLOW + bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM bool ' Debug memory allocations' CONFIG_DEBUG_SLAB bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ + bool ' Morse code panics' CONFIG_PANIC_MORSE bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK - bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER fi endmenu diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/defconfig linux.21rc1-ac2/arch/i386/defconfig --- linux.21rc1/arch/i386/defconfig 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/defconfig 2003-04-22 16:44:36.000000000 +0100 @@ -56,6 +56,7 @@ # CONFIG_MICROCODE is not set # CONFIG_X86_MSR is not set # CONFIG_X86_CPUID is not set +# CONFIG_EDD is not set CONFIG_NOHIGHMEM=y # CONFIG_HIGHMEM4G is not set # CONFIG_HIGHMEM64G is not set diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/acpitable.c linux.21rc1-ac2/arch/i386/kernel/acpitable.c --- linux.21rc1/arch/i386/kernel/acpitable.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/acpitable.c 2003-04-25 13:33:12.000000000 +0100 @@ -314,12 +314,15 @@ int have_acpi_tables; extern void __init MP_processor_info(struct mpc_config_processor *); +extern unsigned int xapic_support; static void __init acpi_parse_lapic(struct acpi_table_lapic *local_apic) { struct mpc_config_processor proc_entry; int ix = 0; + static unsigned long apic_ver; + static int first_time = 1; if (!local_apic) return; @@ -357,7 +360,16 @@ proc_entry.mpc_featureflag = boot_cpu_data.x86_capability[0]; proc_entry.mpc_reserved[0] = 0; proc_entry.mpc_reserved[1] = 0; - proc_entry.mpc_apicver = 0x10; /* integrated APIC */ + if (first_time) { + first_time = 0; + set_fixmap(FIX_APIC_BASE, APIC_DEFAULT_PHYS_BASE); + Dprintk("Local APIC ID %lx\n", apic_read(APIC_ID)); + apic_ver = apic_read(APIC_LVR); + Dprintk("Local APIC Version %lx\n", apic_ver); + if (APIC_XAPIC_SUPPORT(apic_ver)) + xapic_support = 1; + } + proc_entry.mpc_apicver = apic_ver; MP_processor_info(&proc_entry); } else { printk(" disabled"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/dmi_scan.c linux.21rc1-ac2/arch/i386/kernel/dmi_scan.c --- linux.21rc1/arch/i386/kernel/dmi_scan.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/dmi_scan.c 2003-04-23 14:38:13.000000000 +0100 @@ -415,8 +415,10 @@ */ extern int skip_ioapic_setup; +#ifdef CONFIG_PCI extern int broken_440gx_bios; extern unsigned int pci_probe; +#endif static __init int broken_pirq(struct dmi_blacklist *d) { printk(KERN_INFO " *** Possibly defective BIOS detected (irqtable)\n"); @@ -483,6 +485,18 @@ } /* + * Exploding PnPBIOS. Don't yet know if its the BIOS or us for + * some entries + */ + +static __init int exploding_pnp_bios(struct dmi_blacklist *d) +{ + printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident); + dmi_broken |= BROKEN_PNP_BIOS; + return 0; +} + +/* * Simple "print if true" callback */ @@ -692,6 +706,13 @@ NO_MATCH, NO_MATCH } }, + { exploding_pnp_bios, "Higraded P14H", { /* BIOSPnP problem */ + MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + MATCH(DMI_BIOS_VERSION, "07.00T"), + MATCH(DMI_SYS_VENDOR, "Higraded"), + MATCH(DMI_PRODUCT_NAME, "P14H") + } }, + /* Machines which have problems handling enabled local APICs */ { local_apic_kills_bios, "Dell Inspiron", { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/edd.c linux.21rc1-ac2/arch/i386/kernel/edd.c --- linux.21rc1/arch/i386/kernel/edd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/edd.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,672 @@ +/* + * linux/arch/i386/kernel/edd.c + * Copyright (C) 2002 Dell Computer Corporation + * by Matt Domsch + * + * BIOS Enhanced Disk Drive Services (EDD) + * conformant to T13 Committee www.t13.org + * projects 1572D, 1484D, 1386D, 1226DT + * + * This code takes information provided by BIOS EDD calls + * fn41 - Check Extensions Present and + * fn48 - Get Device Parametes with EDD extensions + * made in setup.S, copied to safe structures in setup.c, + * and presents it in /proc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * 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. + * + */ + +/* + * TODO: + * - move edd.[ch] to better locations if/when one is decided + * - keep current with 2.5 EDD code changes + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matt Domsch "); +MODULE_DESCRIPTION("proc interface to BIOS EDD information"); +MODULE_LICENSE("GPL"); + +#define EDD_VERSION "0.09 2003-Jan-22" +#define EDD_DEVICE_NAME_SIZE 16 +#define REPORT_URL "http://domsch.com/linux/edd30/results.html" + +#define left (count - (p - page) - 1) + +static struct proc_dir_entry *bios_dir; + +struct attr_entry { + struct proc_dir_entry *entry; + struct list_head node; +}; + +struct edd_device { + char name[EDD_DEVICE_NAME_SIZE]; + struct edd_info *info; + struct proc_dir_entry *dir; + struct list_head attr_list; +}; + +static struct edd_device *edd_devices[EDDMAXNR]; + +struct edd_attribute { + char *name; + int (*show)(char *page, char **start, off_t off, + int count, int *eof, void *data); + int (*test) (struct edd_device * edev); +}; + +#define EDD_DEVICE_ATTR(_name,_show,_test) \ +struct edd_attribute edd_attr_##_name = { \ + .name = __stringify(_name), \ + .show = _show, \ + .test = _test, \ +}; + +static inline struct edd_info * +edd_dev_get_info(struct edd_device *edev) +{ + return edev->info; +} + +static inline void +edd_dev_set_info(struct edd_device *edev, struct edd_info *info) +{ + edev->info = info; +} + +static int +proc_calc_metrics(char *page, char **start, off_t off, + int count, int *eof, int len) +{ + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int +edd_dump_raw_data(char *b, int count, void *data, int length) +{ + char *orig_b = b; + char hexbuf[80], ascbuf[20], *h, *a, c; + unsigned char *p = data; + unsigned long column = 0; + int length_printed = 0, d; + const char maxcolumn = 16; + while (length_printed < length && count > 0) { + h = hexbuf; + a = ascbuf; + for (column = 0; + column < maxcolumn && length_printed < length; column++) { + h += sprintf(h, "%02x ", (unsigned char) *p); + if (!isprint(*p)) + c = '.'; + else + c = *p; + a += sprintf(a, "%c", c); + p++; + length_printed++; + } + /* pad out the line */ + for (; column < maxcolumn; column++) { + h += sprintf(h, " "); + a += sprintf(a, " "); + } + d = snprintf(b, count, "%s\t%s\n", hexbuf, ascbuf); + b += d; + count -= d; + } + return (b - orig_b); +} + +static int +edd_show_host_bus(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + int i; + + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + for (i = 0; i < 4; i++) { + if (isprint(info->params.host_bus_type[i])) { + p += snprintf(p, left, "%c", info->params.host_bus_type[i]); + } else { + p += snprintf(p, left, " "); + } + } + + if (!strncmp(info->params.host_bus_type, "ISA", 3)) { + p += snprintf(p, left, "\tbase_address: %x\n", + info->params.interface_path.isa.base_address); + } else if (!strncmp(info->params.host_bus_type, "PCIX", 4) || + !strncmp(info->params.host_bus_type, "PCI", 3)) { + p += snprintf(p, left, + "\t%02x:%02x.%d channel: %u\n", + info->params.interface_path.pci.bus, + info->params.interface_path.pci.slot, + info->params.interface_path.pci.function, + info->params.interface_path.pci.channel); + } else if (!strncmp(info->params.host_bus_type, "IBND", 4) || + !strncmp(info->params.host_bus_type, "XPRS", 4) || + !strncmp(info->params.host_bus_type, "HTPT", 4)) { + p += snprintf(p, left, + "\tTBD: %llx\n", + info->params.interface_path.ibnd.reserved); + + } else { + p += snprintf(p, left, "\tunknown: %llx\n", + info->params.interface_path.unknown.reserved); + } + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_interface(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + int i; + + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + for (i = 0; i < 8; i++) { + if (isprint(info->params.interface_type[i])) { + p += snprintf(p, left, "%c", info->params.interface_type[i]); + } else { + p += snprintf(p, left, " "); + } + } + if (!strncmp(info->params.interface_type, "ATAPI", 5)) { + p += snprintf(p, left, "\tdevice: %u lun: %u\n", + info->params.device_path.atapi.device, + info->params.device_path.atapi.lun); + } else if (!strncmp(info->params.interface_type, "ATA", 3)) { + p += snprintf(p, left, "\tdevice: %u\n", + info->params.device_path.ata.device); + } else if (!strncmp(info->params.interface_type, "SCSI", 4)) { + p += snprintf(p, left, "\tid: %u lun: %llu\n", + info->params.device_path.scsi.id, + info->params.device_path.scsi.lun); + } else if (!strncmp(info->params.interface_type, "USB", 3)) { + p += snprintf(p, left, "\tserial_number: %llx\n", + info->params.device_path.usb.serial_number); + } else if (!strncmp(info->params.interface_type, "1394", 4)) { + p += snprintf(p, left, "\teui: %llx\n", + info->params.device_path.i1394.eui); + } else if (!strncmp(info->params.interface_type, "FIBRE", 5)) { + p += snprintf(p, left, "\twwid: %llx lun: %llx\n", + info->params.device_path.fibre.wwid, + info->params.device_path.fibre.lun); + } else if (!strncmp(info->params.interface_type, "I2O", 3)) { + p += snprintf(p, left, "\tidentity_tag: %llx\n", + info->params.device_path.i2o.identity_tag); + } else if (!strncmp(info->params.interface_type, "RAID", 4)) { + p += snprintf(p, left, "\tidentity_tag: %x\n", + info->params.device_path.raid.array_number); + } else if (!strncmp(info->params.interface_type, "SATA", 4)) { + p += snprintf(p, left, "\tdevice: %u\n", + info->params.device_path.sata.device); + } else { + p += snprintf(p, left, "\tunknown: %llx %llx\n", + info->params.device_path.unknown.reserved1, + info->params.device_path.unknown.reserved2); + } + + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +/** + * edd_show_raw_data() - unparses EDD information, returned to user-space + * + * Returns: number of bytes written, or 0 on failure + */ +static int +edd_show_raw_data(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + int i, warn_padding = 0, nonzero_path = 0, + len = sizeof (*info) - 4; + uint8_t checksum = 0, c = 0; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) + len = info->params.length; + + p += snprintf(p, left, "int13 fn48 returned data:\n\n"); + p += edd_dump_raw_data(p, left, ((char *) info) + 4, len); + + /* Spec violation. Adaptec AIC7899 returns 0xDDBE + here, when it should be 0xBEDD. + */ + p += snprintf(p, left, "\n"); + if (info->params.key == 0xDDBE) { + p += snprintf(p, left, + "Warning: Spec violation. Key should be 0xBEDD, is 0xDDBE\n"); + } + + if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { + goto out; + } + + for (i = 30; i <= 73; i++) { + c = *(((uint8_t *) info) + i + 4); + if (c) + nonzero_path++; + checksum += c; + } + + if (checksum) { + p += snprintf(p, left, + "Warning: Spec violation. Device Path checksum invalid.\n"); + } + + if (!nonzero_path) { + p += snprintf(p, left, "Error: Spec violation. Empty device path.\n"); + goto out; + } + + for (i = 0; i < 4; i++) { + if (!isprint(info->params.host_bus_type[i])) { + warn_padding++; + } + } + for (i = 0; i < 8; i++) { + if (!isprint(info->params.interface_type[i])) { + warn_padding++; + } + } + + if (warn_padding) { + p += snprintf(p, left, + "Warning: Spec violation. Padding should be 0x20.\n"); + } + +out: + p += snprintf(p, left, "\nPlease check %s\n", REPORT_URL); + p += snprintf(p, left, "to see if this device has been reported. If not,\n"); + p += snprintf(p, left, "please send the information requested there.\n"); + + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_version(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + p += snprintf(p, left, "0x%02x\n", info->version); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_extensions(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + if (info->interface_support & EDD_EXT_FIXED_DISK_ACCESS) { + p += snprintf(p, left, "Fixed disk access\n"); + } + if (info->interface_support & EDD_EXT_DEVICE_LOCKING_AND_EJECTING) { + p += snprintf(p, left, "Device locking and ejecting\n"); + } + if (info->interface_support & EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT) { + p += snprintf(p, left, "Enhanced Disk Drive support\n"); + } + if (info->interface_support & EDD_EXT_64BIT_EXTENSIONS) { + p += snprintf(p, left, "64-bit extensions\n"); + } + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_info_flags(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + if (info->params.info_flags & EDD_INFO_DMA_BOUNDRY_ERROR_TRANSPARENT) + p += snprintf(p, left, "DMA boundry error transparent\n"); + if (info->params.info_flags & EDD_INFO_GEOMETRY_VALID) + p += snprintf(p, left, "geometry valid\n"); + if (info->params.info_flags & EDD_INFO_REMOVABLE) + p += snprintf(p, left, "removable\n"); + if (info->params.info_flags & EDD_INFO_WRITE_VERIFY) + p += snprintf(p, left, "write verify\n"); + if (info->params.info_flags & EDD_INFO_MEDIA_CHANGE_NOTIFICATION) + p += snprintf(p, left, "media change notification\n"); + if (info->params.info_flags & EDD_INFO_LOCKABLE) + p += snprintf(p, left, "lockable\n"); + if (info->params.info_flags & EDD_INFO_NO_MEDIA_PRESENT) + p += snprintf(p, left, "no media present\n"); + if (info->params.info_flags & EDD_INFO_USE_INT13_FN50) + p += snprintf(p, left, "use int13 fn50\n"); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_default_cylinders(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + p += snprintf(p, left, "0x%x\n", info->params.num_default_cylinders); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_default_heads(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + p += snprintf(p, left, "0x%x\n", info->params.num_default_heads); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_default_sectors_per_track(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + p += snprintf(p, left, "0x%x\n", info->params.sectors_per_track); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_show_sectors(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct edd_info *info = data; + char *p = page; + if (!info || !page || off) { + return proc_calc_metrics(page, start, off, count, eof, 0); + } + + p += snprintf(p, left, "0x%llx\n", info->params.number_of_sectors); + return proc_calc_metrics(page, start, off, count, eof, (p - page)); +} + +static int +edd_has_default_cylinders(struct edd_device *edev) +{ + struct edd_info *info = edd_dev_get_info(edev); + if (!edev || !info) + return 0; + return info->params.num_default_cylinders > 0; +} + +static int +edd_has_default_heads(struct edd_device *edev) +{ + struct edd_info *info = edd_dev_get_info(edev); + if (!edev || !info) + return 0; + return info->params.num_default_heads > 0; +} + +static int +edd_has_default_sectors_per_track(struct edd_device *edev) +{ + struct edd_info *info = edd_dev_get_info(edev); + if (!edev || !info) + return 0; + return info->params.sectors_per_track > 0; +} + +static int +edd_has_edd30(struct edd_device *edev) +{ + struct edd_info *info = edd_dev_get_info(edev); + int i, nonzero_path = 0; + char c; + + if (!edev || !info) + return 0; + + if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { + return 0; + } + + for (i = 30; i <= 73; i++) { + c = *(((uint8_t *) info) + i + 4); + if (c) { + nonzero_path++; + break; + } + } + if (!nonzero_path) { + return 0; + } + + return 1; +} + +static EDD_DEVICE_ATTR(raw_data, edd_show_raw_data, NULL); +static EDD_DEVICE_ATTR(version, edd_show_version, NULL); +static EDD_DEVICE_ATTR(extensions, edd_show_extensions, NULL); +static EDD_DEVICE_ATTR(info_flags, edd_show_info_flags, NULL); +static EDD_DEVICE_ATTR(sectors, edd_show_sectors, NULL); +static EDD_DEVICE_ATTR(default_cylinders, edd_show_default_cylinders, + edd_has_default_cylinders); +static EDD_DEVICE_ATTR(default_heads, edd_show_default_heads, + edd_has_default_heads); +static EDD_DEVICE_ATTR(default_sectors_per_track, + edd_show_default_sectors_per_track, + edd_has_default_sectors_per_track); +static EDD_DEVICE_ATTR(interface, edd_show_interface,edd_has_edd30); +static EDD_DEVICE_ATTR(host_bus, edd_show_host_bus, edd_has_edd30); + +static struct edd_attribute *def_attrs[] = { + &edd_attr_raw_data, + &edd_attr_version, + &edd_attr_extensions, + &edd_attr_info_flags, + &edd_attr_sectors, + &edd_attr_default_cylinders, + &edd_attr_default_heads, + &edd_attr_default_sectors_per_track, + &edd_attr_interface, + &edd_attr_host_bus, + NULL, +}; + +static inline void +edd_device_unregister(struct edd_device *edev) +{ + struct list_head *pos, *next; + struct attr_entry *ae; + + list_for_each_safe(pos, next, &edev->attr_list) { + ae = list_entry(pos, struct attr_entry, node); + remove_proc_entry(ae->entry->name, edev->dir); + list_del(&ae->node); + kfree(ae); + } + + remove_proc_entry(edev->dir->name, bios_dir); +} + +static int +edd_populate_dir(struct edd_device *edev) +{ + struct edd_attribute *attr; + struct attr_entry *ae; + int i; + int error = 0; + + for (i = 0; (attr=def_attrs[i]); i++) { + if (!attr->test || (attr->test && attr->test(edev))) { + ae = kmalloc(sizeof (*ae), GFP_KERNEL); + if (ae == NULL) { + error = 1; + break; + } + INIT_LIST_HEAD(&ae->node); + ae->entry = + create_proc_read_entry(attr->name, 0444, + edev->dir, attr->show, + edd_dev_get_info(edev)); + if (ae->entry == NULL) { + error = 1; + break; + } + list_add(&ae->node, &edev->attr_list); + } + } + + if (error) + return error; + + return 0; +} + +static int +edd_make_dir(struct edd_device *edev) +{ + int error=1; + + edev->dir = proc_mkdir(edev->name, bios_dir); + if (edev->dir != NULL) { + edev->dir->mode = (S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); + error = edd_populate_dir(edev); + } + return error; +} + +static int +edd_device_register(struct edd_device *edev, int i) +{ + int error; + + if (!edev) + return 1; + memset(edev, 0, sizeof (*edev)); + INIT_LIST_HEAD(&edev->attr_list); + edd_dev_set_info(edev, &edd[i]); + snprintf(edev->name, EDD_DEVICE_NAME_SIZE, "int13_dev%02x", + edd[i].device); + error = edd_make_dir(edev); + return error; +} + +/** + * edd_init() - creates /proc tree of EDD data + * + * This assumes that eddnr and edd were + * assigned in setup.c already. + */ +static int __init +edd_init(void) +{ + unsigned int i; + int rc = 0; + struct edd_device *edev; + + printk(KERN_INFO "BIOS EDD facility v%s, %d devices found\n", + EDD_VERSION, eddnr); + + if (!eddnr) { + printk(KERN_INFO "EDD information not available.\n"); + return 1; + } + + bios_dir = proc_mkdir("bios", NULL); + if (bios_dir == NULL) + return 1; + + for (i = 0; i < eddnr && i < EDDMAXNR && !rc; i++) { + edev = kmalloc(sizeof (*edev), GFP_KERNEL); + if (!edev) { + rc = 1; + break; + } + + rc = edd_device_register(edev, i); + if (rc) { + break; + } + edd_devices[i] = edev; + } + + if (rc) { + for (i = 0; i < eddnr && i < EDDMAXNR; i++) { + if ((edev = edd_devices[i])) { + edd_device_unregister(edev); + kfree(edev); + } + } + + remove_proc_entry(bios_dir->name, NULL); + } + + return rc; +} + +static void __exit +edd_exit(void) +{ + int i; + struct edd_device *edev; + + for (i = 0; i < eddnr && i < EDDMAXNR; i++) { + if ((edev = edd_devices[i])) { + edd_device_unregister(edev); + kfree(edev); + } + } + + remove_proc_entry(bios_dir->name, NULL); +} + +module_init(edd_init); +module_exit(edd_exit); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/elanfreq.c linux.21rc1-ac2/arch/i386/kernel/elanfreq.c --- linux.21rc1/arch/i386/kernel/elanfreq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/elanfreq.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,297 @@ +/* + * elanfreq: cpufreq driver for the AMD ELAN family + * + * (c) Copyright 2002 Robert Schwebel + * + * Parts of this code are (c) Sven Geggus + * + * All Rights Reserved. + * + * 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. + * + * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */ +#define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */ + +static struct cpufreq_driver *elanfreq_driver; + +/* Module parameter */ +static int max_freq; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Schwebel , Sven Geggus "); +MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs"); + +struct s_elan_multiplier { + int clock; /* frequency in kHz */ + int val40h; /* PMU Force Mode register */ + int val80h; /* CPU Clock Speed Register */ +}; + +/* + * It is important that the frequencies + * are listed in ascending order here! + */ +struct s_elan_multiplier elan_multiplier[] = { + {1000, 0x02, 0x18}, + {2000, 0x02, 0x10}, + {4000, 0x02, 0x08}, + {8000, 0x00, 0x00}, + {16000, 0x00, 0x02}, + {33000, 0x00, 0x04}, + {66000, 0x01, 0x04}, + {99000, 0x01, 0x05} +}; + +static struct cpufreq_frequency_table elanfreq_table[] = { + {0, 1000}, + {1, 2000}, + {2, 4000}, + {3, 8000}, + {4, 16000}, + {5, 33000}, + {6, 66000}, + {7, 99000}, + {0, CPUFREQ_TABLE_END}, +}; + + +/** + * elanfreq_get_cpu_frequency: determine current cpu speed + * + * Finds out at which frequency the CPU of the Elan SOC runs + * at the moment. Frequencies from 1 to 33 MHz are generated + * the normal way, 66 and 99 MHz are called "Hyperspeed Mode" + * and have the rest of the chip running with 33 MHz. + */ + +static unsigned int elanfreq_get_cpu_frequency(void) +{ + u8 clockspeed_reg; /* Clock Speed Register */ + + local_irq_disable(); + outb_p(0x80,REG_CSCIR); + clockspeed_reg = inb_p(REG_CSCDR); + local_irq_enable(); + + if ((clockspeed_reg & 0xE0) == 0xE0) { return 0; } + + /* Are we in CPU clock multiplied mode (66/99 MHz)? */ + if ((clockspeed_reg & 0xE0) == 0xC0) { + if ((clockspeed_reg & 0x01) == 0) { + return 66000; + } else { + return 99000; + } + } + + /* 33 MHz is not 32 MHz... */ + if ((clockspeed_reg & 0xE0)==0xA0) + return 33000; + + return ((1<<((clockspeed_reg & 0xE0) >> 5)) * 1000); +} + + +/** + * elanfreq_set_cpu_frequency: Change the CPU core frequency + * @cpu: cpu number + * @freq: frequency in kHz + * + * This function takes a frequency value and changes the CPU frequency + * according to this. Note that the frequency has to be checked by + * elanfreq_validatespeed() for correctness! + * + * There is no return value. + */ + +static void elanfreq_set_cpu_state (unsigned int state) { + + struct cpufreq_freqs freqs; + + if (!elanfreq_driver) { + printk(KERN_ERR "cpufreq: initialization problem or invalid target frequency\n"); + return; + } + + freqs.old = elanfreq_get_cpu_frequency(); + freqs.new = elan_multiplier[state].clock; + freqs.cpu = 0; /* elanfreq.c is UP only driver */ + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",elan_multiplier[state].clock); + + + /* + * Access to the Elan's internal registers is indexed via + * 0x22: Chip Setup & Control Register Index Register (CSCI) + * 0x23: Chip Setup & Control Register Data Register (CSCD) + * + */ + + /* + * 0x40 is the Power Management Unit's Force Mode Register. + * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency) + */ + + local_irq_disable(); + outb_p(0x40,REG_CSCIR); /* Disable hyperspeed mode */ + outb_p(0x00,REG_CSCDR); + local_irq_enable(); /* wait till internal pipelines and */ + udelay(1000); /* buffers have cleaned up */ + + local_irq_disable(); + + /* now, set the CPU clock speed register (0x80) */ + outb_p(0x80,REG_CSCIR); + outb_p(elan_multiplier[state].val80h,REG_CSCDR); + + /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */ + outb_p(0x40,REG_CSCIR); + outb_p(elan_multiplier[state].val40h,REG_CSCDR); + udelay(10000); + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + + +/** + * elanfreq_validatespeed: test if frequency range is valid + * + * This function checks if a given frequency range in kHz is valid + * for the hardware supported by the driver. + */ + +static int elanfreq_verify (struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]); +} + +static int elanfreq_setpolicy (struct cpufreq_policy *policy) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_setpolicy(policy, &elanfreq_table[0], &newstate)) + return -EINVAL; + + elanfreq_set_cpu_state(newstate); + + return 0; +} + + +/* + * Module init and exit code + */ + +#ifndef MODULE +/** + * elanfreq_setup - elanfreq command line parameter parsing + * + * elanfreq command line parameter. Use: + * elanfreq=66000 + * to set the maximum CPU frequency to 66 MHz. Note that in + * case you do not give this boot parameter, the maximum + * frequency will fall back to _current_ CPU frequency which + * might be lower. If you build this as a module, use the + * max_freq module parameter instead. + */ +static int __init elanfreq_setup(char *str) +{ + max_freq = simple_strtoul(str, &str, 0); + return 1; +} +__setup("elanfreq=", elanfreq_setup); +#endif + +static int __init elanfreq_init(void) +{ + struct cpuinfo_x86 *c = cpu_data; + struct cpufreq_driver *driver; + int ret, i; + + /* Test if we have the right hardware */ + if ((c->x86_vendor != X86_VENDOR_AMD) || + (c->x86 != 4) || (c->x86_model!=10)) + { + printk(KERN_INFO "elanfreq: error: no Elan processor found!\n"); + return -ENODEV; + } + + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->policy = (struct cpufreq_policy *) (driver + 1); + + if (!max_freq) + max_freq = elanfreq_get_cpu_frequency(); + + /* table init */ + for (i=0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) { + if (elanfreq_table[i].frequency > max_freq) + elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID; + } + +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = elanfreq_get_cpu_frequency(); +#endif + + driver->verify = &elanfreq_verify; + driver->setpolicy = &elanfreq_setpolicy; + + driver->policy[0].cpu = 0; + ret = cpufreq_frequency_table_cpuinfo(&driver->policy[0], &elanfreq_table[0]); + if (ret) { + kfree(driver); + return ret; + } + driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + + elanfreq_driver = driver; + + ret = cpufreq_register(driver); + if (ret) { + elanfreq_driver = NULL; + kfree(driver); + } + + return ret; +} + + +static void __exit elanfreq_exit(void) +{ + if (elanfreq_driver) { + cpufreq_unregister(); + kfree(elanfreq_driver); + } +} + +module_init(elanfreq_init); +module_exit(elanfreq_exit); + +MODULE_PARM (max_freq, "i"); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/entry.S linux.21rc1-ac2/arch/i386/kernel/entry.S --- linux.21rc1/arch/i386/kernel/entry.S 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/entry.S 2003-04-22 16:44:36.000000000 +0100 @@ -79,7 +79,7 @@ exec_domain = 16 need_resched = 20 tsk_ptrace = 24 -processor = 52 +cpu = 32 ENOSYS = 38 @@ -184,9 +184,11 @@ ENTRY(ret_from_fork) +#if CONFIG_SMP pushl %ebx call SYMBOL_NAME(schedule_tail) addl $4, %esp +#endif GET_CURRENT(%ebx) testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS jne tracesys_exit @@ -645,8 +647,8 @@ .long SYMBOL_NAME(sys_tkill) .long SYMBOL_NAME(sys_sendfile64) .long SYMBOL_NAME(sys_ni_syscall) /* 240 reserved for futex */ - .long SYMBOL_NAME(sys_ni_syscall) /* reserved for sched_setaffinity */ - .long SYMBOL_NAME(sys_ni_syscall) /* reserved for sched_getaffinity */ + .long SYMBOL_NAME(sys_sched_setaffinity) + .long SYMBOL_NAME(sys_sched_getaffinity) .long SYMBOL_NAME(sys_ni_syscall) /* sys_set_thread_area */ .long SYMBOL_NAME(sys_ni_syscall) /* sys_get_thread_area */ .long SYMBOL_NAME(sys_ni_syscall) /* 245 sys_io_setup */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/gx-suspmod.c linux.21rc1-ac2/arch/i386/kernel/gx-suspmod.c --- linux.21rc1/arch/i386/kernel/gx-suspmod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/gx-suspmod.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,514 @@ +/* + * Cyrix MediaGX and NatSemi Geode Suspend Modulation + * (C) 2002 Zwane Mwaikambo + * (C) 2002 Hiroshi Miura + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation + * + * The author(s) of this software shall not be held liable for damages + * of any nature resulting due to the use of this software. This + * software is provided AS-IS with no warranties. + * + * Theoritical note: + * + * (see Geode(tm) CS5530 manual (rev.4.1) page.56) + * + * CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0 + * are based on Suspend Moduration. + * + * Suspend Modulation works by asserting and de-asserting the SUSP# pin + * to CPU(GX1/GXLV) for configurable durations. When asserting SUSP# + * the CPU enters an idle state. GX1 stops its core clock when SUSP# is + * asserted then power consumption is reduced. + * + * Suspend Modulation's OFF/ON duration are configurable + * with 'Suspend Modulation OFF Count Register' + * and 'Suspend Modulation ON Count Register'. + * These registers are 8bit counters that represent the number of + * 32us intervals which the SUSP# pin is asserted/de-asserted to the + * processor. + * + * These counters define a ratio which is the effective frequency + * of operation of the system. + * + * On Count + * F_eff = Fgx * ---------------------- + * On Count + Off Count + * + * 0 <= On Count, Off Count <= 255 + * + * From these limits, we can get register values + * + * on_duration + off_duration <= MAX_DURATION + * off_duration = on_duration * (stock_freq - freq) / freq + * + * on_duration = (freq * DURATION) / stock_freq + * off_duration = DURATION - on_duration + * + * + *--------------------------------------------------------------------------- + * + * ChangeLog: + * Dec. 11, 2002 Hiroshi Miura + * - rewrite for Cyrix MediaGX Cx5510/5520 and + * NatSemi Geode Cs5530(A). + * + * Jul. ??, 2002 Zwane Mwaikambo + * - cs5530_mod patch for 2.4.19-rc1. + * + *--------------------------------------------------------------------------- + * + * Todo + * Test on machines with 5510, 5530, 5530A + */ + +/************************************************************************ + * Suspend Modulation - Definitions * + ************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PCI config registers, all at F0 */ +#define PCI_PMER1 0x80 /* power management enable register 1 */ +#define PCI_PMER2 0x81 /* power management enable register 2 */ +#define PCI_PMER3 0x82 /* power management enable register 3 */ +#define PCI_IRQTC 0x8c /* irq speedup timer counter register:typical 2 to 4ms */ +#define PCI_VIDTC 0x8d /* video speedup timer counter register: typical 50 to 100ms */ +#define PCI_MODOFF 0x94 /* suspend modulation OFF counter register, 1 = 32us */ +#define PCI_MODON 0x95 /* suspend modulation ON counter register */ +#define PCI_SUSCFG 0x96 /* suspend configuration register */ + +/* PMER1 bits */ +#define GPM (1<<0) /* global power management */ +#define GIT (1<<1) /* globally enable PM device idle timers */ +#define GTR (1<<2) /* globally enable IO traps */ +#define IRQ_SPDUP (1<<3) /* disable clock throttle during interrupt handling */ +#define VID_SPDUP (1<<4) /* disable clock throttle during vga video handling */ + +/* SUSCFG bits */ +#define SUSMOD (1<<0) /* enable/disable suspend modulation */ +/* the belows support only with cs5530 (after rev.1.2)/cs5530A */ +#define SMISPDUP (1<<1) /* select how SMI re-enable suspend modulation: */ + /* IRQTC timer or read SMI speedup disable reg.(F1BAR[08-09h]) */ +#define SUSCFG (1<<2) /* enable powering down a GXLV processor. "Special 3Volt Suspend" mode */ +/* the belows support only with cs5530A */ +#define PWRSVE_ISA (1<<3) /* stop ISA clock */ +#define PWRSVE (1<<4) /* active idle */ + +struct gxfreq_params { + u8 on_duration; + u8 off_duration; + u8 pci_suscfg; + u8 pci_pmer1; + u8 pci_pmer2; + u8 pci_rev; + struct pci_dev *cs55x0; +}; + +static struct cpufreq_driver *gx_driver; +static struct gxfreq_params *gx_params; +static int stock_freq; + +/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */ +static int pci_busclk = 0; +MODULE_PARM(pci_busclk, "i"); + +/* maximum duration for which the cpu may be suspended + * (32us * MAX_DURATION). If no parameter is given, this defaults + * to 255. + * Note that this leads to a maximum of 8 ms(!) where the CPU clock + * is suspended -- processing power is just 0.39% of what it used to be, + * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */ +static int max_duration = 255; +MODULE_PARM(max_duration, "i"); + +/* For the default policy, we want at least some processing power + * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV) + */ +#define POLICY_MIN_DIV 20 + + +/* DEBUG + * Define it if you want verbose debug output + */ + +#define SUSPMOD_DEBUG 1 + +#ifdef SUSPMOD_DEBUG +#define dprintk(msg...) printk(KERN_DEBUG "cpufreq:" msg) +#else +#define dprintk(msg...) do { } while(0); +#endif + +/** + * we can detect a core multipiler from dir0_lsb + * from GX1 datasheet p.56, + * MULT[3:0]: + * 0000 = SYSCLK multiplied by 4 (test only) + * 0001 = SYSCLK multiplied by 10 + * 0010 = SYSCLK multiplied by 4 + * 0011 = SYSCLK multiplied by 6 + * 0100 = SYSCLK multiplied by 9 + * 0101 = SYSCLK multiplied by 5 + * 0110 = SYSCLK multiplied by 7 + * 0111 = SYSCLK multiplied by 8 + * of 33.3MHz + **/ +static int gx_freq_mult[16] = { + 4, 10, 4, 6, 9, 5, 7, 8, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/**************************************************************** + * Low Level chipset interface * + ****************************************************************/ +static struct pci_device_id gx_chipset_tbl[] __initdata = { + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID }, + { 0, }, +}; + +/** + * gx_detect_chipset: + * + **/ +static __init struct pci_dev *gx_detect_chipset(void) +{ + struct pci_dev *gx_pci; + + /* check if CPU is a MediaGX or a Geode. */ + if ((current_cpu_data.x86_vendor != X86_VENDOR_NSC) && + (current_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) { + printk(KERN_INFO "gx-suspmod: error: no MediaGX/Geode processor found!\n"); + return NULL; + } + + /* detect which companion chip is used */ + pci_for_each_dev(gx_pci) { + if ((pci_match_device (gx_chipset_tbl, gx_pci)) != NULL) { + return gx_pci; + } + } + + dprintk(KERN_INFO "gx-suspmod: error: no supported chipset found!\n"); + return NULL; +} + +/** + * gx_get_cpuspeed: + * + * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi Geode CPU runs. + */ +static int gx_get_cpuspeed(void) +{ + if ((gx_params->pci_suscfg & SUSMOD) == 0) + return stock_freq; + + return (stock_freq * gx_params->on_duration) + / (gx_params->on_duration + gx_params->off_duration); +} + +/** + * gx_validate_speed: + * determine current cpu speed + * +**/ + +static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration, u8 *off_duration) +{ + unsigned int i; + u8 tmp_on, tmp_off; + int old_tmp_freq = stock_freq; + int tmp_freq; + + *on_duration=1; + *off_duration=0; + + for (i=max_duration; i>0; i--) { + tmp_on = ((khz * i) / stock_freq) & 0xff; + tmp_off = i - tmp_on; + tmp_freq = (stock_freq * tmp_on) / i; + /* if this relation is closer to khz, use this. If it's equal, + * prefer it, too - lower latency */ + if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) { + *on_duration = tmp_on; + *off_duration = tmp_off; + old_tmp_freq = tmp_freq; + } + } + + return old_tmp_freq; +} + + +/** + * gx_set_cpuspeed: + * set cpu speed in khz. + **/ + +static void gx_set_cpuspeed(unsigned int khz) +{ + u8 suscfg, pmer1; + unsigned int new_khz; + unsigned long flags; + struct cpufreq_freqs freqs; + + + freqs.cpu = 0; + freqs.old = gx_get_cpuspeed(); + + new_khz = gx_validate_speed(khz, &gx_params->on_duration, &gx_params->off_duration); + + freqs.new = new_khz; + + if (new_khz == stock_freq) { /* if new khz == 100% of CPU speed, it is special case */ + local_irq_save(flags); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, (gx_params->pci_suscfg & ~(SUSMOD))); + pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &(gx_params->pci_suscfg)); + local_irq_restore(flags); + dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n"); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + return; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_save(flags); + switch (gx_params->cs55x0->device) { + case PCI_DEVICE_ID_CYRIX_5530_LEGACY: + pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP; + /* FIXME: need to test other values -- Zwane,Miura */ + pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 4); /* typical 2 to 4ms */ + pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/* typical 50 to 100ms */ + pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1); + + if (gx_params->pci_rev < 0x10) { /* CS5530(rev 1.2, 1.3) */ + suscfg = gx_params->pci_suscfg | SUSMOD; + } else { /* CS5530A,B.. */ + suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE; + } + break; + case PCI_DEVICE_ID_CYRIX_5520: + case PCI_DEVICE_ID_CYRIX_5510: + suscfg = gx_params->pci_suscfg | SUSMOD; + break; + default: + local_irq_restore(flags); + dprintk("fatal: try to set unknown chipset.\n"); + return; + } + + pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF, gx_params->off_duration); + pci_write_config_byte(gx_params->cs55x0, PCI_MODON, gx_params->on_duration); + + pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg); + pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg); + + local_irq_restore(flags); + + gx_params->pci_suscfg = suscfg; + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n", + gx_params->on_duration * 32, gx_params->off_duration * 32); + dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new); +} + +/**************************************************************** + * High level functions * + ****************************************************************/ + +/* + * cpufreq_gx_verify: test if frequency range is valid + * + * This function checks if a given frequency range in kHz is valid + * for the hardware supported by the driver. + */ + +static int cpufreq_gx_verify(struct cpufreq_policy *policy) +{ + unsigned int tmp_freq = 0; + u8 tmp1, tmp2; + + if (!gx_driver || !stock_freq || !policy) + return -EINVAL; + + policy->cpu = 0; + cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq); + + /* it needs to be assured that at least one supported frequency is + * within policy->min and policy->max. If it is not, policy->max + * needs to be increased until one freuqency is supported. + * policy->min may not be decreased, though. This way we guarantee a + * specific processing capacity. + */ + tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2); + if (tmp_freq < policy->min) + tmp_freq += stock_freq / max_duration; + policy->min = tmp_freq; + if (policy->min > policy->max) + policy->max = tmp_freq; + tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2); + if (tmp_freq > policy->max) + tmp_freq -= stock_freq / max_duration; + policy->max = tmp_freq; + if (policy->max < policy->min) + policy->max = policy->min; + cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq); + + return 0; +} + +/* + * cpufreq_gx_setpolicy: + * + */ +static int cpufreq_gx_setpolicy(struct cpufreq_policy *policy) +{ + + if (!gx_driver || !stock_freq || !policy) + return -EINVAL; + + policy->cpu = 0; + + if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + /* here we need to make sure that we don't set the + * frequency below policy->min (see comment in + * cpufreq_gx_verify() - guarantee of processing + * capacity. + */ + u8 tmp1, tmp2; + unsigned int tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2); + while (tmp_freq < policy->min) { + tmp_freq += stock_freq / max_duration; + tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2); + } + gx_set_cpuspeed(tmp_freq); + } else { + gx_set_cpuspeed(policy->max); + } + return 0; +} + +/* + * cpufreq_gx_init: + * MediaGX/Geode GX initilize cpufreq driver + */ + +static int __init cpufreq_gx_init(void) +{ + int maxfreq,ret,curfreq; + struct cpufreq_driver *driver; + struct gxfreq_params *params; + struct pci_dev *gx_pci; + u32 class_rev; + + /* Test if we have the right hardware */ + if ((gx_pci = gx_detect_chipset()) == NULL) + return -ENODEV; + + /* check whether module parameters are sane */ + if (max_duration > 0xff) + max_duration = 0xff; + + dprintk("geode suspend modulation available.\n"); + + driver = kmalloc(sizeof(struct cpufreq_driver) + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (driver == NULL) + return -ENOMEM; + params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL); + if (params == NULL) { + kfree(driver); + return -ENOMEM; + } + + driver->policy = (struct cpufreq_policy *)(driver + 1); + params->cs55x0 = gx_pci; + + /* keep cs55x0 configurations */ + pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg)); + pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1)); + pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2)); + pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration)); + pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration)); + pci_read_config_dword(params->cs55x0, PCI_CLASS_REVISION, &class_rev); + params->pci_rev = class_rev && 0xff; + + gx_params = params; + + /* determine maximum frequency */ + if (pci_busclk) { + maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f]; + } else if (cpu_khz) { + maxfreq = cpu_khz; + } else { + maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f]; + } + stock_freq = maxfreq; + curfreq = gx_get_cpuspeed(); + + dprintk("cpu max frequency is %d.\n", maxfreq); + dprintk("cpu current frequency is %dkHz.\n",curfreq); + + /* setup basic struct for cpufreq API */ +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = curfreq; +#endif + driver->policy[0].cpu = 0; + + if (max_duration < POLICY_MIN_DIV) + driver->policy[0].min = maxfreq / max_duration; + else + driver->policy[0].min = maxfreq / POLICY_MIN_DIV; + driver->policy[0].max = maxfreq; + driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; + driver->policy[0].cpuinfo.min_freq = maxfreq / max_duration; + driver->policy[0].cpuinfo.max_freq = maxfreq; + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + driver->verify = &cpufreq_gx_verify; + driver->setpolicy = &cpufreq_gx_setpolicy; + + gx_driver = driver; + + if ((ret = cpufreq_register(driver))) { + kfree(driver); + kfree(params); + return ret; /* register error! */ + } + + return 0; +} + +static void __exit cpufreq_gx_exit(void) +{ + if (gx_driver) { + /* disable throttling */ + gx_set_cpuspeed(stock_freq); + cpufreq_unregister(); + kfree(gx_driver); + kfree(gx_params); + } +} + +MODULE_AUTHOR ("Hiroshi Miura "); +MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_gx_init); +module_exit(cpufreq_gx_exit); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/head.S linux.21rc1-ac2/arch/i386/kernel/head.S --- linux.21rc1/arch/i386/kernel/head.S 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/head.S 2003-04-22 16:44:36.000000000 +0100 @@ -445,4 +445,15 @@ .quad 0x00409a0000000000 /* 0x48 APM CS code */ .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ + /* Segments used for calling PnP BIOS */ + .quad 0x00c09a0000000000 /* 0x60 32-bit code */ + .quad 0x00809a0000000000 /* 0x68 16-bit code */ + .quad 0x0080920000000000 /* 0x70 16-bit data */ + .quad 0x0080920000000000 /* 0x78 16-bit data */ + .quad 0x0080920000000000 /* 0x80 16-bit data */ + .quad 0x0000000000000000 /* 0x88 not used */ + .quad 0x0000000000000000 /* 0x90 not used */ + .quad 0x0000000000000000 /* 0x98 not used */ + /* Per CPU segments */ .fill NR_CPUS*4,8,0 /* space for TSS's and LDT's */ + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/i386_ksyms.c linux.21rc1-ac2/arch/i386/kernel/i386_ksyms.c --- linux.21rc1/arch/i386/kernel/i386_ksyms.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/i386_ksyms.c 2003-04-22 16:44:36.000000000 +0100 @@ -28,6 +28,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -49,6 +50,7 @@ EXPORT_SYMBOL(drive_info); #endif +extern unsigned long cpu_khz; extern unsigned long get_cmos_time(void); /* platform dependent support */ @@ -71,6 +73,7 @@ EXPORT_SYMBOL(pm_idle); EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(cpu_khz); EXPORT_SYMBOL(apm_info); EXPORT_SYMBOL(gdt); EXPORT_SYMBOL(empty_zero_page); @@ -130,6 +133,7 @@ EXPORT_SYMBOL(cpu_data); EXPORT_SYMBOL(kernel_flag_cacheline); EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(smp_num_siblings); EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL_NOVERS(__write_lock_failed); EXPORT_SYMBOL_NOVERS(__read_lock_failed); @@ -180,3 +184,8 @@ #ifdef CONFIG_MULTIQUAD EXPORT_SYMBOL(xquad_portio); #endif + +#ifdef CONFIG_EDD_MODULE +EXPORT_SYMBOL(edd); +EXPORT_SYMBOL(eddnr); +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/i387.c linux.21rc1-ac2/arch/i386/kernel/i387.c --- linux.21rc1/arch/i386/kernel/i387.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/i387.c 2003-04-22 16:44:36.000000000 +0100 @@ -248,7 +248,7 @@ * FXSR floating point environment conversions. */ -static inline int convert_fxsr_to_user( struct _fpstate *buf, +static int convert_fxsr_to_user( struct _fpstate *buf, struct i387_fxsave_struct *fxsave ) { unsigned long env[7]; @@ -270,13 +270,18 @@ to = &buf->_st[0]; from = (struct _fpxreg *) &fxsave->st_space[0]; for ( i = 0 ; i < 8 ; i++, to++, from++ ) { - if ( __copy_to_user( to, from, sizeof(*to) ) ) + unsigned long *t = (unsigned long *)to; + unsigned long *f = (unsigned long *)from; + + if (__put_user(*f, t) || + __put_user(*(f + 1), t + 1) || + __put_user(from->exponent, &to->exponent)) return 1; } return 0; } -static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, +static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, struct _fpstate *buf ) { unsigned long env[7]; @@ -299,7 +304,12 @@ to = (struct _fpxreg *) &fxsave->st_space[0]; from = &buf->_st[0]; for ( i = 0 ; i < 8 ; i++, to++, from++ ) { - if ( __copy_from_user( to, from, sizeof(*from) ) ) + unsigned long *t = (unsigned long *)to; + unsigned long *f = (unsigned long *)from; + + if (__get_user(*t, f) || + __get_user(*(t + 1), f + 1) || + __get_user(to->exponent, &from->exponent)) return 1; } return 0; @@ -321,7 +331,7 @@ return 1; } -static inline int save_i387_fxsave( struct _fpstate *buf ) +static int save_i387_fxsave( struct _fpstate *buf ) { struct task_struct *tsk = current; int err = 0; @@ -371,7 +381,7 @@ sizeof(struct i387_fsave_struct) ); } -static inline int restore_i387_fxsave( struct _fpstate *buf ) +static int restore_i387_fxsave( struct _fpstate *buf ) { struct task_struct *tsk = current; clear_fpu( tsk ); @@ -389,7 +399,7 @@ if ( HAVE_HWFP ) { if ( cpu_has_fxsr ) { - err = restore_i387_fxsave( buf ); + err = restore_i387_fxsave( buf ); } else { err = restore_i387_fsave( buf ); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/io_apic.c linux.21rc1-ac2/arch/i386/kernel/io_apic.c --- linux.21rc1/arch/i386/kernel/io_apic.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/io_apic.c 2003-04-25 13:33:12.000000000 +0100 @@ -42,7 +42,7 @@ unsigned int int_dest_addr_mode = APIC_DEST_LOGICAL; unsigned char int_delivery_mode = dest_LowestPrio; - +extern unsigned int xapic_support; /* * # of IRQ routing registers @@ -188,6 +188,86 @@ clear_IO_APIC_pin(apic, pin); } +static void set_ioapic_affinity (unsigned int irq, unsigned long mask) +{ + unsigned long flags; + + /* + * Only the first 8 bits are valid. + */ + mask = mask << 24; + spin_lock_irqsave(&ioapic_lock, flags); + __DO_ACTION(1, = mask, ) + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +#if CONFIG_SMP + +typedef struct { + unsigned int cpu; + unsigned long timestamp; +} ____cacheline_aligned irq_balance_t; + +static irq_balance_t irq_balance[NR_IRQS] __cacheline_aligned + = { [ 0 ... NR_IRQS-1 ] = { 0, 0 } }; + +extern unsigned long irq_affinity [NR_IRQS]; + +#endif + +#define IDLE_ENOUGH(cpu,now) \ + (idle_cpu(cpu) && ((now) - irq_stat[(cpu)].idle_timestamp > 1)) + +#define IRQ_ALLOWED(cpu,allowed_mask) \ + ((1 << cpu) & (allowed_mask)) + +static unsigned long move(int curr_cpu, unsigned long allowed_mask, unsigned long now, int direction) +{ + int search_idle = 1; + int cpu = curr_cpu; + + goto inside; + + do { + if (unlikely(cpu == curr_cpu)) + search_idle = 0; +inside: + if (direction == 1) { + cpu++; + if (cpu >= smp_num_cpus) + cpu = 0; + } else { + cpu--; + if (cpu == -1) + cpu = smp_num_cpus-1; + } + } while (!IRQ_ALLOWED(cpu,allowed_mask) || + (search_idle && !IDLE_ENOUGH(cpu,now))); + + return cpu; +} + +static inline void balance_irq(int irq) +{ +#if CONFIG_SMP + irq_balance_t *entry = irq_balance + irq; + unsigned long now = jiffies; + + if (unlikely(entry->timestamp != now)) { + unsigned long allowed_mask; + int random_number; + + rdtscl(random_number); + random_number &= 1; + + allowed_mask = cpu_online_map & irq_affinity[irq]; + entry->timestamp = now; + entry->cpu = move(entry->cpu, allowed_mask, now, random_number); + set_ioapic_affinity(irq, apicid_to_phys_cpu_present(entry->cpu)); + } +#endif +} + /* * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to * specific CPU-side IRQs. @@ -1072,7 +1152,8 @@ old_id = mp_ioapics[apic].mpc_apicid; - if (mp_ioapics[apic].mpc_apicid >= apic_broadcast_id) { + if (!xapic_support && + (mp_ioapics[apic].mpc_apicid >= apic_broadcast_id)) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", apic, mp_ioapics[apic].mpc_apicid); printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", @@ -1086,7 +1167,8 @@ * 'stuck on smp_invalidate_needed IPI wait' messages. * I/O APIC IDs no longer have any meaning for xAPICs and SAPICs. */ - if ((clustered_apic_mode != CLUSTERED_APIC_XAPIC) && + if (!xapic_support && + (clustered_apic_mode != CLUSTERED_APIC_XAPIC) && (phys_id_present_map & (1 << mp_ioapics[apic].mpc_apicid))) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", apic, mp_ioapics[apic].mpc_apicid); @@ -1221,6 +1303,7 @@ */ static void ack_edge_ioapic_irq(unsigned int irq) { + balance_irq(irq); if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) == (IRQ_PENDING | IRQ_DISABLED)) mask_IO_APIC_irq(irq); @@ -1260,6 +1343,7 @@ unsigned long v; int i; + balance_irq(irq); /* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various @@ -1316,19 +1400,6 @@ static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ } -static void set_ioapic_affinity (unsigned int irq, unsigned long mask) -{ - unsigned long flags; - /* - * Only the first 8 bits are valid. - */ - mask = mask << 24; - - spin_lock_irqsave(&ioapic_lock, flags); - __DO_ACTION(1, = mask, ) - spin_unlock_irqrestore(&ioapic_lock, flags); -} - /* * Level and edge triggered IO-APIC interrupts need different handling, * so we use two separate IRQ descriptors. Edge triggered IRQs can be diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/irq.c linux.21rc1-ac2/arch/i386/kernel/irq.c --- linux.21rc1/arch/i386/kernel/irq.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/irq.c 2003-04-22 16:44:36.000000000 +0100 @@ -1090,7 +1090,7 @@ static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; -static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; static int irq_affinity_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/ldt.c linux.21rc1-ac2/arch/i386/kernel/ldt.c --- linux.21rc1/arch/i386/kernel/ldt.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/ldt.c 2003-04-22 16:44:36.000000000 +0100 @@ -12,37 +12,139 @@ #include #include #include +#include #include #include #include #include +#ifdef CONFIG_SMP /* avoids "defined but not used" warnig */ +static void flush_ldt(void *mm) +{ + if (current->active_mm) + load_LDT(¤t->active_mm->context); +} +#endif + +static int alloc_ldt(mm_context_t *pc, int mincount, int reload) +{ + void *oldldt; + void *newldt; + int oldsize; + + if (mincount <= pc->size) + return 0; + oldsize = pc->size; + mincount = (mincount+511)&(~511); + if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE) + newldt = vmalloc(mincount*LDT_ENTRY_SIZE); + else + newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL); + + if (!newldt) + return -ENOMEM; + + if (oldsize) + memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE); + + oldldt = pc->ldt; + memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE); + wmb(); + pc->ldt = newldt; + pc->size = mincount; + if (reload) { + load_LDT(pc); +#ifdef CONFIG_SMP + if (current->mm->cpu_vm_mask != (1< PAGE_SIZE) + vfree(oldldt); + else + kfree(oldldt); + } + return 0; +} + +static inline int copy_ldt(mm_context_t *new, mm_context_t *old) +{ + int err = alloc_ldt(new, old->size, 0); + if (err < 0) { + printk(KERN_WARNING "ldt allocation failed\n"); + new->size = 0; + return err; + } + memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE); + return 0; +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +int init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + struct mm_struct * old_mm; + int retval = 0; + + init_MUTEX(&mm->context.sem); + mm->context.size = 0; + old_mm = current->mm; + if (old_mm && old_mm->context.size > 0) { + down(&old_mm->context.sem); + retval = copy_ldt(&mm->context, &old_mm->context); + up(&old_mm->context.sem); + } + return retval; +} + /* - * read_ldt() is not really atomic - this is not a problem since - * synchronization of reads and writes done to the LDT has to be - * assured by user-space anyway. Writes are atomic, to protect - * the security checks done on new descriptors. + * No need to lock the MM as we are the last user + * Do not touch the ldt register, we are already + * in the next thread. */ +void destroy_context(struct mm_struct *mm) +{ + if (mm->context.size) { + if (mm->context.size*LDT_ENTRY_SIZE > PAGE_SIZE) + vfree(mm->context.ldt); + else + kfree(mm->context.ldt); + mm->context.size = 0; + } +} + static int read_ldt(void * ptr, unsigned long bytecount) { int err; unsigned long size; struct mm_struct * mm = current->mm; - err = 0; - if (!mm->context.segments) - goto out; + if (!mm->context.size) + return 0; + if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES) + bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES; - size = LDT_ENTRIES*LDT_ENTRY_SIZE; + down(&mm->context.sem); + size = mm->context.size*LDT_ENTRY_SIZE; if (size > bytecount) size = bytecount; - err = size; - if (copy_to_user(ptr, mm->context.segments, size)) + err = 0; + if (copy_to_user(ptr, mm->context.ldt, size)) err = -EFAULT; -out: - return err; + up(&mm->context.sem); + if (err < 0) + return err; + if (size != bytecount) { + /* zero-fill the rest */ + clear_user(ptr+size, bytecount-size); + } + return bytecount; } static int read_default_ldt(void * ptr, unsigned long bytecount) @@ -53,7 +155,7 @@ err = 0; address = &default_ldt[0]; - size = sizeof(struct desc_struct); + size = 5*sizeof(struct desc_struct); if (size > bytecount) size = bytecount; @@ -88,24 +190,14 @@ goto out; } - /* - * the GDT index of the LDT is allocated dynamically, and is - * limited by MAX_LDT_DESCRIPTORS. - */ - down_write(&mm->mmap_sem); - if (!mm->context.segments) { - void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); - error = -ENOMEM; - if (!segments) + down(&mm->context.sem); + if (ldt_info.entry_number >= mm->context.size) { + error = alloc_ldt(¤t->mm->context, ldt_info.entry_number+1, 1); + if (error < 0) goto out_unlock; - memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE); - wmb(); - mm->context.segments = segments; - mm->context.cpuvalid = 1UL << smp_processor_id(); - load_LDT(mm); } - lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments); + lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.ldt); /* Allow LDTs to be cleared by the user. */ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { @@ -143,7 +235,7 @@ error = 0; out_unlock: - up_write(&mm->mmap_sem); + up(&mm->context.sem); out: return error; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/longhaul.c linux.21rc1-ac2/arch/i386/kernel/longhaul.c --- linux.21rc1/arch/i386/kernel/longhaul.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/longhaul.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,813 @@ +/* + * $Id: longhaul.c,v 1.83 2003/01/02 22:16:26 db Exp $ + * + * (C) 2001 Dave Jones. + * (C) 2002 Padraig Brady. + * + * Licensed under the terms of the GNU GPL License version 2. + * Based upon datasheets & sample CPUs kindly provided by VIA. + * + * VIA have currently 3 different versions of Longhaul. + * + * +---------------------+----------+---------------------------------+ + * | Marketing name | Codename | longhaul version / features. | + * +---------------------+----------+---------------------------------+ + * | Samuel/CyrixIII | C5A | v1 : multipliers only | + * | Samuel2/C3 | C3E/C5B | v1 : multiplier only | + * | Ezra | C5C | v2 : multipliers & voltage | + * | Ezra-T | C5M/C5N | v3 : multipliers, voltage & FSB | + * +---------------------+----------+---------------------------------+ + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEBUG + +#ifdef DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0); +#endif + +static int numscales=16, numvscales; +static int minvid, maxvid; +static int can_scale_voltage; +static int can_scale_fsb; +static int vrmrev; + + +/* Module parameters */ +static int dont_scale_voltage; +static int dont_scale_fsb; +static int current_fsb; + +#define __hlt() __asm__ __volatile__("hlt": : :"memory") + +/* + * Clock ratio tables. + * The eblcr ones specify the ratio read from the CPU. + * The clock_ratio ones specify what to write to the CPU. + */ + +/* VIA C3 Samuel 1 & Samuel 2 (stepping 0)*/ +static int __initdata longhaul1_clock_ratio[16] = { + -1, /* 0000 -> RESERVED */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + -1, /* 0011 -> RESERVED */ + -1, /* 0100 -> RESERVED */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 55, /* 0111 -> 5.5x */ + 60, /* 1000 -> 6.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 50, /* 1011 -> 5.0x */ + 65, /* 1100 -> 6.5x */ + 75, /* 1101 -> 7.5x */ + -1, /* 1110 -> RESERVED */ + -1, /* 1111 -> RESERVED */ +}; + +static int __initdata samuel1_eblcr[16] = { + 50, /* 0000 -> RESERVED */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + -1, /* 0011 -> RESERVED */ + 55, /* 0100 -> 5.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + -1, /* 0111 -> RESERVED */ + -1, /* 1000 -> RESERVED */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 60, /* 1011 -> 6.0x */ + -1, /* 1100 -> RESERVED */ + 75, /* 1101 -> 7.5x */ + -1, /* 1110 -> RESERVED */ + 65, /* 1111 -> 6.5x */ +}; + +/* VIA C3 Samuel2 Stepping 1->15 & VIA C3 Ezra */ +static int __initdata longhaul2_clock_ratio[16] = { + 100, /* 0000 -> 10.0x */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + 90, /* 0011 -> 9.0x */ + 95, /* 0100 -> 9.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 55, /* 0111 -> 5.5x */ + 60, /* 1000 -> 6.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 50, /* 1011 -> 5.0x */ + 65, /* 1100 -> 6.5x */ + 75, /* 1101 -> 7.5x */ + 85, /* 1110 -> 8.5x */ + 120, /* 1111 -> 12.0x */ +}; + +static int __initdata samuel2_eblcr[16] = { + 50, /* 0000 -> 5.0x */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + 100, /* 0011 -> 10.0x */ + 55, /* 0100 -> 5.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 110, /* 0111 -> 11.0x */ + 90, /* 1000 -> 9.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 60, /* 1011 -> 6.0x */ + 120, /* 1100 -> 12.0x */ + 75, /* 1101 -> 7.5x */ + 130, /* 1110 -> 13.0x */ + 65, /* 1111 -> 6.5x */ +}; + +static int __initdata ezra_eblcr[16] = { + 50, /* 0000 -> 5.0x */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + 100, /* 0011 -> 10.0x */ + 55, /* 0100 -> 5.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 95, /* 0111 -> 9.5x */ + 90, /* 1000 -> 9.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 60, /* 1011 -> 6.0x */ + 120, /* 1100 -> 12.0x */ + 75, /* 1101 -> 7.5x */ + 85, /* 1110 -> 8.5x */ + 65, /* 1111 -> 6.5x */ +}; + +/* VIA C5M. */ +static int __initdata longhaul3_clock_ratio[32] = { + 100, /* 0000 -> 10.0x */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + 90, /* 0011 -> 9.0x */ + 95, /* 0100 -> 9.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 55, /* 0111 -> 5.5x */ + 60, /* 1000 -> 6.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 50, /* 1011 -> 5.0x */ + 65, /* 1100 -> 6.5x */ + 75, /* 1101 -> 7.5x */ + 85, /* 1110 -> 8.5x */ + 120, /* 1111 -> 12.0x */ + + -1, /* 0000 -> RESERVED (10.0x) */ + 110, /* 0001 -> 11.0x */ + 120, /* 0010 -> 12.0x */ + -1, /* 0011 -> RESERVED (9.0x)*/ + 105, /* 0100 -> 10.5x */ + 115, /* 0101 -> 11.5x */ + 125, /* 0110 -> 12.5x */ + 135, /* 0111 -> 13.5x */ + 140, /* 1000 -> 14.0x */ + 150, /* 1001 -> 15.0x */ + 160, /* 1010 -> 16.0x */ + 130, /* 1011 -> 13.0x */ + 145, /* 1100 -> 14.5x */ + 155, /* 1101 -> 15.5x */ + -1, /* 1110 -> RESERVED (13.0x) */ + -1, /* 1111 -> RESERVED (12.0x) */ +}; + +static int __initdata c5m_eblcr[32] = { + 50, /* 0000 -> 5.0x */ + 30, /* 0001 -> 3.0x */ + 40, /* 0010 -> 4.0x */ + 100, /* 0011 -> 10.0x */ + 55, /* 0100 -> 5.5x */ + 35, /* 0101 -> 3.5x */ + 45, /* 0110 -> 4.5x */ + 95, /* 0111 -> 9.5x */ + 90, /* 1000 -> 9.0x */ + 70, /* 1001 -> 7.0x */ + 80, /* 1010 -> 8.0x */ + 60, /* 1011 -> 6.0x */ + 120, /* 1100 -> 12.0x */ + 75, /* 1101 -> 7.5x */ + 85, /* 1110 -> 8.5x */ + 65, /* 1111 -> 6.5x */ + + -1, /* 0000 -> RESERVED (9.0x) */ + 110, /* 0001 -> 11.0x */ + 120, /* 0010 -> 12.0x */ + -1, /* 0011 -> RESERVED (10.0x)*/ + 135, /* 0100 -> 13.5x */ + 115, /* 0101 -> 11.5x */ + 125, /* 0110 -> 12.5x */ + 105, /* 0111 -> 10.5x */ + 130, /* 1000 -> 13.0x */ + 150, /* 1001 -> 15.0x */ + 160, /* 1010 -> 16.0x */ + 140, /* 1011 -> 14.0x */ + -1, /* 1100 -> RESERVED (12.0x) */ + 155, /* 1101 -> 15.5x */ + -1, /* 1110 -> RESERVED (13.0x) */ + 145, /* 1111 -> 14.5x */ +}; + +/* fsb values as defined in CPU */ +static unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 }; +/* fsb values to favour low fsb speed (lower power) */ +static unsigned int power_fsb_table[] = { 66, 100, 133, -1 }; +/* fsb values to favour high fsb speed (for e.g. if lowering CPU + freq because of heat, but want to maintain highest performance possible) */ +static unsigned int perf_fsb_table[] = { 133, 100, 66, -1 }; +static unsigned int *fsb_search_table; + +/* Voltage scales. Div by 1000 to get actual voltage. */ +static int __initdata vrm85scales[32] = { + 1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700, + 1650, 1600, 1550, 1500, 1450, 1400, 1350, 1300, + 1275, 1225, 1175, 1125, 1075, 1825, 1775, 1725, + 1675, 1625, 1575, 1525, 1475, 1425, 1375, 1325, +}; + +static int __initdata mobilevrmscales[32] = { + 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, + 1600, 1550, 1500, 1450, 1500, 1350, 1300, -1, + 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, + 1075, 1050, 1025, 1000, 975, 950, 925, -1, +}; + +/* Clock ratios multiplied by 10 */ +static int clock_ratio[32]; +static int eblcr_table[32]; +static int voltage_table[32]; +static int highest_speed, lowest_speed; /* kHz */ +static int longhaul; /* version. */ +static struct cpufreq_driver *longhaul_driver; + + +static int longhaul_get_cpu_fsb (void) +{ + unsigned long invalue=0,lo, hi; + + if (current_fsb == 0) { + rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); + invalue = (lo & (1<<18|1<<19)) >>18; + return eblcr_fsb_table[invalue]; + } else { + return current_fsb; + } +} + + +static int longhaul_get_cpu_mult (void) +{ + unsigned long invalue=0,lo, hi; + + rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); + invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; + if (longhaul==3) { + if (lo & (1<<27)) + invalue+=16; + } + return eblcr_table[invalue]; +} + + +/** + * longhaul_set_cpu_frequency() + * @clock_ratio_index : index of clock_ratio[] for new frequency + * @newfsb: the new FSB + * + * Sets a new clock ratio, and -if applicable- a new Front Side Bus + */ + +static void longhaul_setstate (unsigned int clock_ratio_index, unsigned int newfsb) +{ + unsigned long lo, hi; + unsigned int bits; + int revkey; + int vidindex, i; + struct cpufreq_freqs freqs; + + if (!newfsb || (clock_ratio[clock_ratio_index] == -1)) + return; + + if ((!can_scale_fsb) && (newfsb != current_fsb)) + return; + + if (((clock_ratio[clock_ratio_index] * newfsb * 100) > highest_speed) || + ((clock_ratio[clock_ratio_index] * newfsb * 100) < lowest_speed)) + return; + + freqs.old = longhaul_get_cpu_mult() * longhaul_get_cpu_fsb() * 100; + freqs.new = clock_ratio[clock_ratio_index] * newfsb * 100; + freqs.cpu = 0; /* longhaul.c is UP only driver */ + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + dprintk (KERN_INFO "longhaul: New FSB:%d Mult(x10):%d\n", + newfsb, clock_ratio[clock_ratio_index]); + + bits = clock_ratio_index; + /* "bits" contains the bitpattern of the new multiplier. + we now need to transform it to the desired format. */ + + switch (longhaul) { + case 1: + rdmsr (MSR_VIA_BCR2, lo, hi); + revkey = (lo & 0xf)<<4; /* Rev key. */ + lo &= ~(1<<23|1<<24|1<<25|1<<26); + lo |= (1<<19); /* Enable software clock multiplier */ + lo |= (bits<<23); /* desired multiplier */ + lo |= revkey; + wrmsr (MSR_VIA_BCR2, lo, hi); + + __hlt(); + + /* Disable software clock multiplier */ + rdmsr (MSR_VIA_BCR2, lo, hi); + lo &= ~(1<<19); + lo |= revkey; + wrmsr (MSR_VIA_BCR2, lo, hi); + break; + + case 2: + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + revkey = (lo & 0xf)<<4; /* Rev key. */ + lo &= 0xfff0bf0f; /* reset [19:16,14](bus ratio) and [7:4](rev key) to 0 */ + lo |= (bits<<16); + lo |= (1<<8); /* EnableSoftBusRatio */ + lo |= revkey; + + if (can_scale_voltage) { + if (can_scale_fsb==1) { + dprintk (KERN_INFO "longhaul: Voltage scaling + FSB scaling not done yet.\n"); + goto bad_voltage; + } else { + /* PB: TODO fix this up */ + vidindex = (((highest_speed-lowest_speed) / (newfsb/2)) - + ((highest_speed-((clock_ratio[clock_ratio_index] * newfsb * 100)/1000)) / (newfsb/2))); + } + for (i=0;i<32;i++) { + dprintk (KERN_INFO "VID hunting. Looking for %d, found %d\n", + minvid+(vidindex*25), voltage_table[i]); + if (voltage_table[i]==(minvid + (vidindex * 25))) + break; + } + if (i==32) + goto bad_voltage; + + dprintk (KERN_INFO "longhaul: Desired vid index=%d\n", i); +#if 0 + lo &= 0xfe0fffff;/* reset [24:20](voltage) to 0 */ + lo |= (i<<20); /* set voltage */ + lo |= (1<<9); /* EnableSoftVID */ +#endif + } + +bad_voltage: + wrmsr (MSR_VIA_LONGHAUL, lo, hi); + __hlt(); + + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + lo &= ~(1<<8); + if (can_scale_voltage) + lo &= ~(1<<9); + lo |= revkey; + wrmsr (MSR_VIA_LONGHAUL, lo, hi); + break; + + case 3: + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + revkey = (lo & 0xf)<<4; /* Rev key. */ + lo &= 0xfff0bf0f; /* reset longhaul[19:16,14] to 0 */ + lo |= (bits<<16); + lo |= (1<<8); /* EnableSoftBusRatio */ + lo |= revkey; + + /* Set FSB */ + if (can_scale_fsb==1) { + lo &= ~(1<<28|1<<29); + switch (newfsb) { + case 66: lo |= (1<<28|1<<29); /* 11 */ + break; + case 100: lo |= 1<<28; /* 01 */ + break; + case 133: break; /* 00*/ + } + } + wrmsr (MSR_VIA_LONGHAUL, lo, hi); + __hlt(); + + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + lo &= ~(1<<8); + lo |= revkey; + wrmsr (MSR_VIA_LONGHAUL, lo, hi); + break; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +} + + +static void __init longhaul_get_ranges (void) +{ + unsigned long lo, hi, invalue; + unsigned int minmult=0, maxmult=0, minfsb=0, maxfsb=0; + unsigned int multipliers[32]= { + 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, + -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; + unsigned int fsb_table[4] = { 133, 100, -1, 66 }; + + switch (longhaul) { + case 1: + /* Ugh, Longhaul v1 didn't have the min/max MSRs. + Assume min=3.0x & max = whatever we booted at. */ + minmult = 30; + maxmult = longhaul_get_cpu_mult(); + minfsb = maxfsb = current_fsb; + break; + + case 2 ... 3: + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + + invalue = (hi & (1<<0|1<<1|1<<2|1<<3)); + if (hi & (1<<11)) + invalue += 16; + maxmult=multipliers[invalue]; + +#if 0 /* This is MaxMhz @ Min Voltage. Ignore for now */ + invalue = (hi & (1<<16|1<<17|1<<18|1<<19)) >> 16; + if (hi & (1<<27)) + invalue += 16; + minmult = multipliers[invalue]; +#else + minmult = 30; /* as per spec */ +#endif + + if (can_scale_fsb==1) { + invalue = (hi & (1<<9|1<<10)) >> 9; + maxfsb = fsb_table[invalue]; + + invalue = (hi & (1<<25|1<<26)) >> 25; + minfsb = fsb_table[invalue]; + + dprintk (KERN_INFO "longhaul: Min FSB=%d Max FSB=%d\n", + minfsb, maxfsb); + } else { + minfsb = maxfsb = current_fsb; + } + break; + } + + highest_speed = maxmult * maxfsb * 100; + lowest_speed = minmult * minfsb * 100; + + dprintk (KERN_INFO "longhaul: MinMult(x10)=%d MaxMult(x10)=%d\n", + minmult, maxmult); + dprintk (KERN_INFO "longhaul: Lowestspeed=%d Highestspeed=%d\n", + lowest_speed, highest_speed); +} + + +static void __init longhaul_setup_voltagescaling (unsigned long lo, unsigned long hi) +{ + int revkey; + + can_scale_voltage = 1; + + minvid = (hi & (1<<20|1<<21|1<<22|1<<23|1<<24)) >> 20; /* 56:52 */ + maxvid = (hi & (1<<4|1<<5|1<<6|1<<7|1<<8)) >> 4; /* 40:36 */ + vrmrev = (lo & (1<<15))>>15; + + if (vrmrev==0) { + dprintk (KERN_INFO "longhaul: VRM 8.5 : "); + memcpy (voltage_table, vrm85scales, sizeof(voltage_table)); + numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25; + } else { + dprintk (KERN_INFO "longhaul: Mobile VRM : "); + memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table)); + numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5; + } + + /* Current voltage isn't readable at first, so we need to + set it to a known value. The spec says to use maxvid */ + revkey = (lo & 0xf)<<4; /* Rev key. */ + lo &= 0xfe0fff0f; /* Mask unneeded bits */ + lo |= (1<<9); /* EnableSoftVID */ + lo |= revkey; /* Reinsert key */ + lo |= maxvid << 20; + wrmsr (MSR_VIA_LONGHAUL, lo, hi); + minvid = voltage_table[minvid]; + maxvid = voltage_table[maxvid]; + dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n", + maxvid/1000, maxvid%1000, minvid/1000, minvid%1000, numvscales); +} + + +static inline unsigned int longhaul_statecount_fsb(struct cpufreq_policy *policy, unsigned int fsb) { + unsigned int i, count = 0; + + for(i=0; imax) && + ((clock_ratio[i] * fsb * 100) >= policy->min)) + count++; + } + + return count; +} + + +static int longhaul_verify(struct cpufreq_policy *policy) +{ + unsigned int number_states = 0; + unsigned int i; + unsigned int fsb_index = 0; + unsigned int tmpfreq = 0; + unsigned int newmax = -1; + + if (!policy || !longhaul_driver) + return -EINVAL; + + policy->cpu = 0; + cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); + + if (can_scale_fsb==1) { + for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) + number_states += longhaul_statecount_fsb(policy, fsb_search_table[fsb_index]); + } else + number_states = longhaul_statecount_fsb(policy, current_fsb); + + if (number_states) + return 0; + + /* get frequency closest above current policy->max */ + if (can_scale_fsb==1) { + for (fsb_index=0; fsb_search_table[fsb_index] != -1; fsb_index++) + for(i=0; i policy->max) && + (tmpfreq < newmax)) + newmax = tmpfreq; + } + } else { + for(i=0; i policy->max) && + (tmpfreq < newmax)) + newmax = tmpfreq; + } + } + + policy->max = newmax; + + cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); + + return 0; +} + + +static int longhaul_get_best_freq_for_fsb(struct cpufreq_policy *policy, + unsigned int min_mult, + unsigned int max_mult, + unsigned int fsb, + unsigned int *new_mult) +{ + unsigned int optimal = 0; + unsigned int found_optimal = 0; + unsigned int i; + + switch(policy->policy) { + case CPUFREQ_POLICY_POWERSAVE: + optimal = max_mult; + break; + case CPUFREQ_POLICY_PERFORMANCE: + optimal = min_mult; + } + + for(i=0; i policy->max) || + (freq < policy->min)) + continue; + switch(policy->policy) { + case CPUFREQ_POLICY_POWERSAVE: + if (clock_ratio[i] < clock_ratio[optimal]) { + found_optimal = 1; + optimal = i; + } + break; + case CPUFREQ_POLICY_PERFORMANCE: + if (clock_ratio[i] > clock_ratio[optimal]) { + found_optimal = 1; + optimal = i; + } + break; + } + } + + if (found_optimal) { + *new_mult = optimal; + return 1; + } + return 0; +} + + +static int longhaul_setpolicy (struct cpufreq_policy *policy) +{ + unsigned int i; + unsigned int fsb_index = 0; + unsigned int new_fsb = 0; + unsigned int new_clock_ratio = 0; + unsigned int min_mult = 0; + unsigned int max_mult = 0; + + + if (!longhaul_driver) + return -EINVAL; + + if (policy->policy==CPUFREQ_POLICY_PERFORMANCE) + fsb_search_table = perf_fsb_table; + else + fsb_search_table = power_fsb_table; + + for(i=0;i clock_ratio[i]) + min_mult = i; + } + + if (can_scale_fsb==1) { + unsigned int found = 0; + for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) + { + if (longhaul_get_best_freq_for_fsb(policy, + min_mult, max_mult, + fsb_search_table[fsb_index], + &new_clock_ratio)) { + new_fsb = fsb_search_table[fsb_index]; + break; + } + } + if (!found) + return -EINVAL; + } else { + new_fsb = current_fsb; + if (!longhaul_get_best_freq_for_fsb(policy, min_mult, + max_mult, new_fsb, &new_clock_ratio)) + return -EINVAL; + } + + longhaul_setstate(new_clock_ratio, new_fsb); + + return 0; +} + + +static int __init longhaul_init (void) +{ + struct cpuinfo_x86 *c = cpu_data; + unsigned int currentspeed; + static int currentmult; + unsigned long lo, hi; + int ret; + struct cpufreq_driver *driver; + + if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) ) + return -ENODEV; + + switch (c->x86_model) { + case 6: /* VIA C3 Samuel C5A */ + longhaul=1; + memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); + memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); + break; + + case 7: /* C5B / C5C */ + switch (c->x86_mask) { + case 0: + longhaul=1; + memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); + memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); + break; + case 1 ... 15: + longhaul=2; + memcpy (clock_ratio, longhaul2_clock_ratio, sizeof(longhaul2_clock_ratio)); + memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); + break; + } + break; + + case 8: /* C5M/C5N */ + return -ENODEV; // Waiting on updated docs from VIA before this is usable + longhaul=3; + numscales=32; + memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio)); + memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr)); + break; + + default: + printk (KERN_INFO "longhaul: Unknown VIA CPU. Contact davej@suse.de\n"); + return -ENODEV; + } + + printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul); + + current_fsb = longhaul_get_cpu_fsb(); + currentmult = longhaul_get_cpu_mult(); + currentspeed = currentmult * current_fsb * 100; + + dprintk (KERN_INFO "longhaul: CPU currently at %dMHz (%d x %d.%d)\n", + (currentspeed/1000), current_fsb, currentmult/10, currentmult%10); + + if (longhaul==2 || longhaul==3) { + rdmsr (MSR_VIA_LONGHAUL, lo, hi); + if ((lo & (1<<0)) && (dont_scale_voltage==0)) + longhaul_setup_voltagescaling (lo, hi); + + if ((lo & (1<<1)) && (dont_scale_fsb==0) && (current_fsb==0)) + can_scale_fsb = 1; + } + + longhaul_get_ranges(); + + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->policy = (struct cpufreq_policy *) (driver + 1); + +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = currentspeed; +#endif + + driver->verify = &longhaul_verify; + driver->setpolicy = &longhaul_setpolicy; + + driver->policy[0].cpu = 0; + driver->policy[0].min = (unsigned int) lowest_speed; + driver->policy[0].max = (unsigned int) highest_speed; + driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; + driver->policy[0].cpuinfo.min_freq = (unsigned int) lowest_speed; + driver->policy[0].cpuinfo.max_freq = (unsigned int) highest_speed; + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + + longhaul_driver = driver; + + ret = cpufreq_register(driver); + if (ret) { + longhaul_driver = NULL; + kfree(driver); + } + + return ret; +} + + +static void __exit longhaul_exit (void) +{ + if (longhaul_driver) { + cpufreq_unregister(); + kfree(longhaul_driver); + } +} + +MODULE_PARM (dont_scale_fsb, "i"); +MODULE_PARM (dont_scale_voltage, "i"); +MODULE_PARM (current_fsb, "i"); + +MODULE_AUTHOR ("Dave Jones "); +MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); +MODULE_LICENSE ("GPL"); + +module_init(longhaul_init); +module_exit(longhaul_exit); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/longrun.c linux.21rc1-ac2/arch/i386/kernel/longrun.c --- linux.21rc1/arch/i386/kernel/longrun.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/longrun.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,292 @@ +/* + * $Id: longrun.c,v 1.18 2003/01/02 22:16:26 db Exp $ + * + * (C) 2002 Dominik Brodowski + * + * Licensed under the terms of the GNU GPL License version 2. + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct cpufreq_driver *longrun_driver; + +/** + * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz + * values into per cent values. In TMTA microcode, the following is valid: + * performance_pctg = (current_freq - low_freq)/(high_freq - low_freq) + */ +static unsigned int longrun_low_freq, longrun_high_freq; + + +/** + * longrun_get_policy - get the current LongRun policy + * @policy: struct cpufreq_policy where current policy is written into + * + * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS + * and MSR_TMTA_LONGRUN_CTRL + */ +static void longrun_get_policy(struct cpufreq_policy *policy) +{ + u32 msr_lo, msr_hi; + + if (!longrun_driver) + return; + + rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi); + if (msr_lo & 0x01) + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + else + policy->policy = CPUFREQ_POLICY_POWERSAVE; + + rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); + msr_lo &= 0x0000007F; + msr_hi &= 0x0000007F; + + policy->min = longrun_low_freq + msr_lo * + ((longrun_high_freq - longrun_low_freq) / 100); + policy->min = longrun_low_freq + msr_hi * + ((longrun_high_freq - longrun_low_freq) / 100); + policy->cpu = 0; +} + + +/** + * longrun_set_policy - sets a new CPUFreq policy + * @policy - new policy + * + * Sets a new CPUFreq policy on LongRun-capable processors. This function + * has to be called with cpufreq_driver locked. + */ +static int longrun_set_policy(struct cpufreq_policy *policy) +{ + u32 msr_lo, msr_hi; + u32 pctg_lo, pctg_hi; + + if (!longrun_driver || !policy) + return -EINVAL; + + pctg_lo = (policy->min - longrun_low_freq) / + ((longrun_high_freq - longrun_low_freq) / 100); + pctg_hi = (policy->max - longrun_low_freq) / + ((longrun_high_freq - longrun_low_freq) / 100); + + if (pctg_hi > 100) + pctg_hi = 100; + if (pctg_lo > pctg_hi) + pctg_lo = pctg_hi; + + /* performance or economy mode */ + rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi); + msr_lo &= 0xFFFFFFFE; + switch (policy->policy) { + case CPUFREQ_POLICY_PERFORMANCE: + msr_lo |= 0x00000001; + break; + case CPUFREQ_POLICY_POWERSAVE: + break; + } + wrmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi); + + /* lower and upper boundary */ + rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); + msr_lo &= 0xFFFFFF80; + msr_hi &= 0xFFFFFF80; + msr_lo |= pctg_lo; + msr_hi |= pctg_hi; + wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); + + return 0; +} + + +/** + * longrun_verify_poliy - verifies a new CPUFreq policy + * + * Validates a new CPUFreq policy. This function has to be called with + * cpufreq_driver locked. + */ +static int longrun_verify_policy(struct cpufreq_policy *policy) +{ + if (!policy || !longrun_driver) + return -EINVAL; + + policy->cpu = 0; + cpufreq_verify_within_limits(policy, + longrun_driver->policy[0].cpuinfo.min_freq, + longrun_driver->policy[0].cpuinfo.max_freq); + + return 0; +} + + +/** + * longrun_determine_freqs - determines the lowest and highest possible core frequency + * + * Determines the lowest and highest possible core frequencies on this CPU. + * This is neccessary to calculate the performance percentage according to + * TMTA rules: + * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq) + */ +static unsigned int __init longrun_determine_freqs(unsigned int *low_freq, + unsigned int *high_freq) +{ + u32 msr_lo, msr_hi; + u32 save_lo, save_hi; + u32 eax, ebx, ecx, edx; + struct cpuinfo_x86 *c = cpu_data; + + if (!low_freq || !high_freq) + return -EINVAL; + + if (cpu_has(c, X86_FEATURE_LRTI)) { + /* if the LongRun Table Interface is present, the + * detection is a bit easier: + * For minimum frequency, read out the maximum + * level (msr_hi), write that into "currently + * selected level", and read out the frequency. + * For maximum frequency, read out level zero. + */ + /* minimum */ + rdmsr(MSR_TMTA_LRTI_READOUT, msr_lo, msr_hi); + wrmsr(MSR_TMTA_LRTI_READOUT, msr_hi, msr_hi); + rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi); + *low_freq = msr_lo * 1000; /* to kHz */ + + /* maximum */ + wrmsr(MSR_TMTA_LRTI_READOUT, 0, msr_hi); + rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi); + *high_freq = msr_lo * 1000; /* to kHz */ + + if (*low_freq > *high_freq) + *low_freq = *high_freq; + return 0; + } + + /* set the upper border to the value determined during TSC init */ + *high_freq = (cpu_khz / 1000); + *high_freq = *high_freq * 1000; + + /* get current borders */ + rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); + save_lo = msr_lo & 0x0000007F; + save_hi = msr_hi & 0x0000007F; + + /* if current perf_pctg is larger than 90%, we need to decrease the + * upper limit to make the calculation more accurate. + */ + cpuid(0x80860007, &eax, &ebx, &ecx, &edx); + if (ecx > 90) { + /* set to 0 to 80 perf_pctg */ + msr_lo &= 0xFFFFFF80; + msr_hi &= 0xFFFFFF80; + msr_lo |= 0; + msr_hi |= 80; + wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); + + /* read out current core MHz and current perf_pctg */ + cpuid(0x80860007, &eax, &ebx, &ecx, &edx); + + /* restore values */ + wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi); + } + + /* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq) + * eqals + * low_freq * ( 1 - perf_pctg) = (cur_freq - high_freq * perf_pctg) + * + * high_freq * perf_pctg is stored tempoarily into "ebx". + */ + ebx = (((cpu_khz / 1000) * ecx) / 100); /* to MHz */ + + if ((ecx > 95) || (ecx == 0) || (eax < ebx)) + return -EIO; + + edx = (eax - ebx) / (100 - ecx); + *low_freq = edx * 1000; /* back to kHz */ + + if (*low_freq > *high_freq) + *low_freq = *high_freq; + + return 0; +} + + +/** + * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver + * + * Initializes the LongRun support. + */ +static int __init longrun_init(void) +{ + int result; + struct cpufreq_driver *driver; + struct cpuinfo_x86 *c = cpu_data; + + if (c->x86_vendor != X86_VENDOR_TRANSMETA || + !cpu_has(c, X86_FEATURE_LONGRUN)) + return 0; + + /* initialization of main "cpufreq" code*/ + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->policy = (struct cpufreq_policy *) (driver + 1); + + if (longrun_determine_freqs(&longrun_low_freq, &longrun_high_freq)) { + kfree(driver); + return -EIO; + } + driver->policy[0].cpuinfo.min_freq = longrun_low_freq; + driver->policy[0].cpuinfo.max_freq = longrun_high_freq; + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + + longrun_get_policy(&driver->policy[0]); + +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = longrun_high_freq; /* dummy value */ +#endif + + driver->verify = &longrun_verify_policy; + driver->setpolicy = &longrun_set_policy; + + longrun_driver = driver; + + result = cpufreq_register(driver); + if (result) { + longrun_driver = NULL; + kfree(driver); + } + + return result; +} + + +/** + * longrun_exit - unregisters LongRun support + */ +static void __exit longrun_exit(void) +{ + if (longrun_driver) { + cpufreq_unregister(); + kfree(longrun_driver); + } +} + + +MODULE_AUTHOR ("Dominik Brodowski "); +MODULE_DESCRIPTION ("LongRun driver for Transmeta Crusoe processors."); +MODULE_LICENSE ("GPL"); +module_init(longrun_init); +module_exit(longrun_exit); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/Makefile linux.21rc1-ac2/arch/i386/kernel/Makefile --- linux.21rc1/arch/i386/kernel/Makefile 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/Makefile 2003-04-22 16:44:36.000000000 +0100 @@ -40,5 +40,14 @@ obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +obj-$(CONFIG_EDD) += edd.o +obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o +obj-$(CONFIG_X86_LONGHAUL) += longhaul.o +obj-$(CONFIG_X86_SPEEDSTEP) += speedstep.o +obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o +obj-$(CONFIG_X86_LONGRUN) += longrun.o +obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o +obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o + include $(TOPDIR)/Rules.make diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/mpparse.c linux.21rc1-ac2/arch/i386/kernel/mpparse.c --- linux.21rc1/arch/i386/kernel/mpparse.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/mpparse.c 2003-04-25 13:33:12.000000000 +0100 @@ -74,6 +74,7 @@ unsigned char clustered_apic_mode = CLUSTERED_APIC_NONE; unsigned int apic_broadcast_id = APIC_BROADCAST_ID_APIC; #endif +unsigned int xapic_support = 0; unsigned char raw_phys_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID }; /* @@ -240,6 +241,8 @@ return; } ver = m->mpc_apicver; + if (APIC_XAPIC_SUPPORT(ver)) + xapic_support = 1; logical_cpu_present_map |= 1 << (num_processors-1); phys_cpu_present_map |= apicid_to_phys_cpu_present(m->mpc_apicid); @@ -589,15 +592,6 @@ } - printk("Enabling APIC mode: "); - if(clustered_apic_mode == CLUSTERED_APIC_NUMAQ) - printk("Clustered Logical. "); - else if(clustered_apic_mode == CLUSTERED_APIC_XAPIC) - printk("Physical. "); - else - printk("Flat. "); - printk("Using %d I/O APICs\n",nr_ioapics); - if (!num_processors) printk(KERN_ERR "SMP mptable: no processors registered!\n"); return num_processors; @@ -832,6 +826,34 @@ BUG(); printk("Processors: %d\n", num_processors); + printk("xAPIC support %s present\n", (xapic_support?"is":"is not")); + +#ifdef CONFIG_X86_CLUSTERED_APIC + /* + * Switch to Physical destination mode in case of generic + * more than 8 CPU system, which has xAPIC support + */ +#define FLAT_APIC_CPU_MAX 8 + if ((clustered_apic_mode == CLUSTERED_APIC_NONE) && + (xapic_support) && + (num_processors > FLAT_APIC_CPU_MAX)) { + clustered_apic_mode = CLUSTERED_APIC_XAPIC; + apic_broadcast_id = APIC_BROADCAST_ID_XAPIC; + int_dest_addr_mode = APIC_DEST_PHYSICAL; + int_delivery_mode = dest_Fixed; + esr_disable = 1; + } +#endif + + printk("Enabling APIC mode: "); + if (clustered_apic_mode == CLUSTERED_APIC_NUMAQ) + printk("Clustered Logical. "); + else if (clustered_apic_mode == CLUSTERED_APIC_XAPIC) + printk("Physical. "); + else + printk("Flat. "); + printk("Using %d I/O APICs\n",nr_ioapics); + /* * Only use the first configuration found. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/p4-clockmod.c linux.21rc1-ac2/arch/i386/kernel/p4-clockmod.c --- linux.21rc1/arch/i386/kernel/p4-clockmod.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/p4-clockmod.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,287 @@ +/* + * Pentium 4/Xeon CPU on demand clock modulation/speed scaling + * (C) 2002 Zwane Mwaikambo + * (C) 2002 Arjan van de Ven + * (C) 2002 Tora T. Engstad + * All Rights Reserved + * + * 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. + * + * The author(s) of this software shall not be held liable for damages + * of any nature resulting due to the use of this software. This + * software is provided AS-IS with no warranties. + * + * Date Errata Description + * 20020525 N44, O17 12.5% or 25% DC causes lockup + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PFX "cpufreq: " + +/* + * Duty Cycle (3bits), note DC_DISABLE is not specified in + * intel docs i just use it to mean disable + */ +enum { + DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT, + DC_64PT, DC_75PT, DC_88PT, DC_DISABLE +}; + +#define DC_ENTRIES 8 + + +static int has_N44_O17_errata; +static int stock_freq; +MODULE_PARM(stock_freq, "i"); + +static struct cpufreq_driver *cpufreq_p4_driver; + + +static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) +{ + u32 l, h; + unsigned long cpus_allowed; + struct cpufreq_freqs freqs; + int hyperthreading = 0; + int affected_cpu_map = 0; + int sibling = 0; + + if (!cpu_online(cpu) || (newstate > DC_DISABLE) || + (newstate == DC_RESV)) + return -EINVAL; + + /* switch to physical CPU where state is to be changed*/ + cpus_allowed = current->cpus_allowed; + + /* only run on CPU to be set, or on its sibling */ + affected_cpu_map = 1 << cpu; +#ifdef CONFIG_X86_HT + hyperthreading = ((cpu_has_ht) && (smp_num_siblings == 2)); + if (hyperthreading) { + sibling = cpu_sibling_map[cpu]; + affected_cpu_map |= (1 << sibling); + } +#endif + set_cpus_allowed(current, affected_cpu_map); + BUG_ON(!(affected_cpu_map & (1 << smp_processor_id()))); + + /* get current state */ + rdmsr(MSR_IA32_THERM_CONTROL, l, h); + if (l & 0x10) { + l = l >> 1; + l &= 0x7; + } else + l = DC_DISABLE; + + if (l == newstate) { + set_cpus_allowed(current, cpus_allowed); + return 0; + } else if (l == DC_RESV) { + printk(KERN_ERR PFX "BIG FAT WARNING: currently in invalid setting\n"); + } + + /* notifiers */ + freqs.old = stock_freq * l / 8; + freqs.new = stock_freq * newstate / 8; + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + if (hyperthreading) { + freqs.cpu = sibling; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + rdmsr(MSR_IA32_THERM_STATUS, l, h); + if (l & 0x01) + printk(KERN_DEBUG PFX "CPU#%d currently thermal throttled\n", cpu); + + if (has_N44_O17_errata && (newstate == DC_25PT || newstate == DC_DFLT)) + newstate = DC_38PT; + + rdmsr(MSR_IA32_THERM_CONTROL, l, h); + if (newstate == DC_DISABLE) { + printk(KERN_INFO PFX "CPU#%d disabling modulation\n", cpu); + wrmsr(MSR_IA32_THERM_CONTROL, l & ~(1<<4), h); + } else { + printk(KERN_INFO PFX "CPU#%d setting duty cycle to %d%%\n", cpu, ((125 * newstate) / 10)); + /* bits 63 - 5 : reserved + * bit 4 : enable/disable + * bits 3-1 : duty cycle + * bit 0 : reserved + */ + l = (l & ~14); + l = l | (1<<4) | ((newstate & 0x7)<<1); + wrmsr(MSR_IA32_THERM_CONTROL, l, h); + } + + set_cpus_allowed(current, cpus_allowed); + + /* notifiers */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + if (hyperthreading) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + + return 0; +} + + +static struct cpufreq_frequency_table p4clockmod_table[] = { + {DC_RESV, CPUFREQ_ENTRY_INVALID}, + {DC_DFLT, 0}, + {DC_25PT, 0}, + {DC_38PT, 0}, + {DC_50PT, 0}, + {DC_64PT, 0}, + {DC_75PT, 0}, + {DC_88PT, 0}, + {DC_DISABLE, 0}, + {DC_RESV, CPUFREQ_TABLE_END}, +}; + + +static int cpufreq_p4_setpolicy(struct cpufreq_policy *policy) +{ + unsigned int newstate = DC_RESV; + + if (cpufreq_frequency_table_setpolicy(policy, &p4clockmod_table[0], &newstate)) + return -EINVAL; + + cpufreq_p4_setdc(policy->cpu, newstate); + + return 0; +} + + +static int cpufreq_p4_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]); +} + + +static int __init cpufreq_p4_init(void) +{ + struct cpuinfo_x86 *c = cpu_data; + int cpuid; + int ret; + struct cpufreq_driver *driver; + unsigned int i; + + /* + * THERM_CONTROL is architectural for IA32 now, so + * we can rely on the capability checks + */ + if (c->x86_vendor != X86_VENDOR_INTEL) + return -ENODEV; + + if (!test_bit(X86_FEATURE_ACPI, c->x86_capability) || + !test_bit(X86_FEATURE_ACC, c->x86_capability)) + return -ENODEV; + + /* Errata workarounds */ + cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask; + switch (cpuid) { + case 0x0f07: + case 0x0f0a: + case 0x0f11: + case 0x0f12: + has_N44_O17_errata = 1; + default: + break; + } + + printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock Modulation available\n"); + + if (!stock_freq) { + if (cpu_khz) + stock_freq = cpu_khz; + else { + printk(KERN_INFO PFX "unknown core frequency - please use module parameter 'stock_freq'\n"); + return -EINVAL; + } + } + + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->policy = (struct cpufreq_policy *) (driver + 1); + + /* table init */ + for (i=1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) { + if ((i<2) && (has_N44_O17_errata)) + p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID; + else + p4clockmod_table[i].frequency = (stock_freq * i)/8; + } + + +#ifdef CONFIG_CPU_FREQ_24_API + for (i=0;icpu_cur_freq[i] = stock_freq; + } +#endif + + driver->verify = &cpufreq_p4_verify; + driver->setpolicy = &cpufreq_p4_setpolicy; + + for (i=0;ipolicy[i].cpu = i; + ret = cpufreq_frequency_table_cpuinfo(&driver->policy[i], &p4clockmod_table[0]); + if (ret) { + kfree(driver); + return ret; + } + driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; + driver->policy[i].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + } + + cpufreq_p4_driver = driver; + + ret = cpufreq_register(driver); + if (ret) { + cpufreq_p4_driver = NULL; + kfree(driver); + } + + return ret; +} + + +static void __exit cpufreq_p4_exit(void) +{ + unsigned int i; + + if (cpufreq_p4_driver) { + for (i=0; i"); +MODULE_DESCRIPTION ("cpufreq driver for Pentium(TM) 4/Xeon(TM)"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_p4_init); +module_exit(cpufreq_p4_exit); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/pci-dma.c linux.21rc1-ac2/arch/i386/kernel/pci-dma.c --- linux.21rc1/arch/i386/kernel/pci-dma.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/pci-dma.c 2003-04-22 16:44:36.000000000 +0100 @@ -19,7 +19,7 @@ void *ret; int gfp = GFP_ATOMIC; - if (hwdev == NULL || ((u32)hwdev->dma_mask < 0xffffffff)) + if (hwdev == NULL || hwdev->dma_mask < 0xffffffff) gfp |= GFP_DMA; ret = (void *)__get_free_pages(gfp, get_order(size)); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/powernow-k6.c linux.21rc1-ac2/arch/i386/kernel/powernow-k6.c --- linux.21rc1/arch/i386/kernel/powernow-k6.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/powernow-k6.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,237 @@ +/* + * $Id: powernow-k6.c,v 1.42 2003/01/02 22:41:08 db Exp $ + * This file was part of Powertweak Linux (http://powertweak.sf.net) + * and is shared with the Linux Kernel module. + * + * (C) 2000-2002 Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski. + * + * Licensed under the terms of the GNU GPL License version 2. + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long + as it is unused */ + +static struct cpufreq_driver *powernow_driver; +static unsigned int busfreq; /* FSB, in 10 kHz */ +static unsigned int max_multiplier; + + +/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */ +static struct cpufreq_frequency_table clock_ratio[] = { + {45, /* 000 -> 4.5x */ 0}, + {50, /* 001 -> 5.0x */ 0}, + {40, /* 010 -> 4.0x */ 0}, + {55, /* 011 -> 5.5x */ 0}, + {20, /* 100 -> 2.0x */ 0}, + {30, /* 101 -> 3.0x */ 0}, + {60, /* 110 -> 6.0x */ 0}, + {35, /* 111 -> 3.5x */ 0}, + {0, CPUFREQ_TABLE_END} +}; + + +/** + * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier + * + * Returns the current setting of the frequency multiplier. Core clock + * speed is frequency of the Front-Side Bus multiplied with this value. + */ +static int powernow_k6_get_cpu_multiplier(void) +{ + u64 invalue = 0; + u32 msrval; + + msrval = POWERNOW_IOPORT + 0x1; + wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */ + invalue=inl(POWERNOW_IOPORT + 0x8); + msrval = POWERNOW_IOPORT + 0x0; + wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ + + return clock_ratio[(invalue >> 5)&7].index; +} + + +/** + * powernow_k6_set_state - set the PowerNow! multiplier + * @best_i: clock_ratio[best_i] is the target multiplier + * + * Tries to change the PowerNow! multiplier + */ +static void powernow_k6_set_state (unsigned int best_i) +{ + unsigned long outvalue=0, invalue=0; + unsigned long msrval; + struct cpufreq_freqs freqs; + + if (!powernow_driver) { + printk(KERN_ERR "cpufreq: initialization problem or invalid target frequency\n"); + return; + } + + freqs.old = busfreq * powernow_k6_get_cpu_multiplier(); + freqs.new = busfreq * clock_ratio[best_i].index; + freqs.cpu = 0; /* powernow-k6.c is UP only driver */ + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* we now need to transform best_i to the BVC format, see AMD#23446 */ + + outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5); + + msrval = POWERNOW_IOPORT + 0x1; + wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */ + invalue=inl(POWERNOW_IOPORT + 0x8); + invalue = invalue & 0xf; + outvalue = outvalue | invalue; + outl(outvalue ,(POWERNOW_IOPORT + 0x8)); + msrval = POWERNOW_IOPORT + 0x0; + wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return; +} + + +/** + * powernow_k6_verify - verifies a new CPUfreq policy + * @policy: new policy + * + * Policy must be within lowest and highest possible CPU Frequency, + * and at least one possible state must be within min and max. + */ +static int powernow_k6_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &clock_ratio[0]); +} + + +/** + * powernow_k6_setpolicy - sets a new CPUFreq policy + * @policy - new policy + * + * sets a new CPUFreq policy + */ +static int powernow_k6_setpolicy (struct cpufreq_policy *policy) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_setpolicy(policy, &clock_ratio[0], &newstate)) + return -EINVAL; + + powernow_k6_set_state(newstate); + + return 0; +} + + +/** + * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver + * + * Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported + * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero + * on success. + */ +static int __init powernow_k6_init(void) +{ + struct cpuinfo_x86 *c = cpu_data; + struct cpufreq_driver *driver; + unsigned int result; + unsigned int i; + + if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) || + ((c->x86_model != 12) && (c->x86_model != 13))) + return -ENODEV; + + max_multiplier = powernow_k6_get_cpu_multiplier(); + busfreq = cpu_khz / max_multiplier; + + if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) { + printk("cpufreq: PowerNow IOPORT region already used.\n"); + return -EIO; + } + + /* initialization of main "cpufreq" code*/ + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) { + release_region (POWERNOW_IOPORT, 16); + return -ENOMEM; + } + driver->policy = (struct cpufreq_policy *) (driver + 1); + + /* table init */ + for (i=0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { + if (clock_ratio[i].index > max_multiplier) + clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID; + else + clock_ratio[i].frequency = busfreq * clock_ratio[i].index; + } + + driver->verify = &powernow_k6_verify; + driver->setpolicy = &powernow_k6_setpolicy; + + /* cpuinfo and default policy values */ + driver->policy[0].cpu = 0; + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = busfreq * max_multiplier; +#endif + result = cpufreq_frequency_table_cpuinfo(&driver->policy[0], &clock_ratio[0]); + if (result) { + kfree(driver); + return result; + } + + powernow_driver = driver; + + result = cpufreq_register(driver); + if (result) { + release_region (POWERNOW_IOPORT, 16); + powernow_driver = NULL; + kfree(driver); + } + + return result; +} + + +/** + * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support + * + * Unregisters AMD K6-2+ / K6-3+ PowerNow! support. + */ +static void __exit powernow_k6_exit(void) +{ + unsigned int i; + + if (powernow_driver) { + for (i=0;i<8;i++) + if (clock_ratio[i].index == max_multiplier) + powernow_k6_set_state(i); + cpufreq_unregister(); + kfree(powernow_driver); + } +} + + +MODULE_AUTHOR ("Arjan van de Ven , Dave Jones , Dominik Brodowski "); +MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors."); +MODULE_LICENSE ("GPL"); +module_init(powernow_k6_init); +module_exit(powernow_k6_exit); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/process.c linux.21rc1-ac2/arch/i386/kernel/process.c --- linux.21rc1/arch/i386/kernel/process.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/process.c 2003-04-22 16:44:36.000000000 +0100 @@ -124,15 +124,12 @@ void cpu_idle (void) { /* endless idle loop with no priority at all */ - init_idle(); - current->nice = 20; - current->counter = -100; while (1) { void (*idle)(void) = pm_idle; if (!idle) idle = default_idle; - while (!current->need_resched) + if (!current->need_resched) idle(); schedule(); check_pgt_cache(); @@ -187,7 +184,7 @@ } /* we will leave sorting out the final value when we are ready to reboot, since we might not - have set up boot_cpu_id or smp_num_cpu */ + have set up boot_cpu_physical_apicid or smp_num_cpu */ break; #endif } @@ -253,7 +250,7 @@ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ 0x74, 0x02, /* jz f */ - 0x0f, 0x08, /* invd */ + 0x0f, 0x09, /* wbinvd */ 0x24, 0x10, /* f: andb $0x10,al */ 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */ }; @@ -466,23 +463,6 @@ } /* - * No need to lock the MM as we are the last user - */ -void release_segments(struct mm_struct *mm) -{ - void * ldt = mm->context.segments; - - /* - * free the LDT - */ - if (ldt) { - mm->context.segments = NULL; - clear_LDT(); - vfree(ldt); - } -} - -/* * Create a kernel thread */ int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) @@ -535,45 +515,19 @@ void release_thread(struct task_struct *dead_task) { if (dead_task->mm) { - void * ldt = dead_task->mm->context.segments; - // temporary debugging check - if (ldt) { - printk("WARNING: dead process %8s still has LDT? <%p>\n", - dead_task->comm, ldt); + if (dead_task->mm->context.size) { + printk("WARNING: dead process %8s still has LDT? <%p/%d>\n", + dead_task->comm, + dead_task->mm->context.ldt, + dead_task->mm->context.size); BUG(); } } - release_x86_irqs(dead_task); } /* - * we do not have to muck with descriptors here, that is - * done in switch_mm() as needed. - */ -void copy_segments(struct task_struct *p, struct mm_struct *new_mm) -{ - struct mm_struct * old_mm; - void *old_ldt, *ldt; - - ldt = NULL; - old_mm = current->mm; - if (old_mm && (old_ldt = old_mm->context.segments) != NULL) { - /* - * Completely new LDT, we initialize it from the parent: - */ - ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); - if (!ldt) - printk(KERN_WARNING "ldt allocation failed\n"); - else - memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); - } - new_mm->context.segments = ldt; - new_mm->context.cpuvalid = ~0UL; /* valid on all CPU's - they can't have stale data */ -} - -/* * Save a segment. */ #define savesegment(seg,value) \ @@ -698,15 +652,17 @@ asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs)); /* - * Restore %fs and %gs. + * Restore %fs and %gs if needed. */ - loadsegment(fs, next->fs); - loadsegment(gs, next->gs); + if (unlikely(prev->fs | prev->gs | next->fs | next->gs)) { + loadsegment(fs, next->fs); + loadsegment(gs, next->gs); + } /* * Now maybe reload the debug registers */ - if (next->debugreg[7]){ + if (unlikely(next->debugreg[7])) { loaddebug(next, 0); loaddebug(next, 1); loaddebug(next, 2); @@ -716,7 +672,7 @@ loaddebug(next, 7); } - if (prev->ioperm || next->ioperm) { + if (unlikely(prev->ioperm || next->ioperm)) { if (next->ioperm) { /* * 4 cachelines copy ... not good, but not that diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/setup.c linux.21rc1-ac2/arch/i386/kernel/setup.c --- linux.21rc1/arch/i386/kernel/setup.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/setup.c 2003-04-22 16:44:36.000000000 +0100 @@ -118,6 +118,7 @@ #include #include #include +#include /* * Machine setup.. */ @@ -196,6 +197,8 @@ #define KERNEL_START (*(unsigned long *) (PARAM+0x214)) #define INITRD_START (*(unsigned long *) (PARAM+0x218)) #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) +#define EDD_NR (*(unsigned char *) (PARAM+EDDNR)) +#define EDD_BUF ((struct edd_info *) (PARAM+EDDBUF)) #define COMMAND_LINE ((char *) (PARAM+2048)) #define COMMAND_LINE_SIZE 256 @@ -203,6 +206,7 @@ #define RAMDISK_PROMPT_FLAG 0x8000 #define RAMDISK_LOAD_FLAG 0x4000 + #ifdef CONFIG_VISWS char visws_board_type = -1; char visws_board_rev = -1; @@ -700,6 +704,23 @@ return 0; } +#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) +unsigned char eddnr; +struct edd_info edd[EDDMAXNR]; +/** + * copy_edd() - Copy the BIOS EDD information + * from empty_zero_page into a safe place. + * + */ +static inline void copy_edd(void) +{ + eddnr = EDD_NR; + memcpy(edd, EDD_BUF, sizeof(edd)); +} +#else +#define copy_edd() do {} while (0) +#endif + /* * Do NOT EVER look at the BIOS memory size location. * It does not work on many machines. @@ -1112,6 +1133,7 @@ rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif setup_memory_region(); + copy_edd(); if (!MOUNT_ROOT_RDONLY) root_mountflags &= ~MS_RDONLY; @@ -2937,6 +2959,7 @@ * applications want to get the raw CPUID data, they should access * /dev/cpu//cpuid instead. */ + extern int phys_proc_id[NR_CPUS]; static char *x86_cap_flags[] = { /* Intel-defined */ "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", @@ -2994,6 +3017,11 @@ /* Cache size */ if (c->x86_cache_size >= 0) seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size); + +#ifdef CONFIG_SMP + seq_printf(m, "physical id\t: %d\n",phys_proc_id[n]); + seq_printf(m, "siblings\t: %d\n",smp_num_siblings); +#endif /* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */ fpu_exception = c->hard_math && (ignore_irq13 || cpu_has_fpu); @@ -3096,11 +3124,12 @@ set_tss_desc(nr,t); gdt_table[__TSS(nr)].b &= 0xfffffdff; load_TR(nr); - load_LDT(&init_mm); + load_LDT(&init_mm.context); - /* - * Clear all 6 debug registers: - */ + /* Clear %fs and %gs. */ + asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); + + /* Clear all 6 debug registers: */ #define CD(register) __asm__("movl %0,%%db" #register ::"r"(0) ); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/smpboot.c linux.21rc1-ac2/arch/i386/kernel/smpboot.c --- linux.21rc1/arch/i386/kernel/smpboot.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/smpboot.c 2003-04-22 16:44:36.000000000 +0100 @@ -58,7 +58,7 @@ /* Number of siblings per CPU package */ int smp_num_siblings = 1; -int __initdata phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ +int phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ /* Bitmask of currently online CPUs */ unsigned long cpu_online_map; @@ -365,7 +365,7 @@ * (This works even if the APIC is not enabled.) */ phys_id = GET_APIC_ID(apic_read(APIC_ID)); - cpuid = current->processor; + cpuid = cpu(); if (test_and_set_bit(cpuid, &cpu_online_map)) { printk("huh, phys CPU#%d, CPU#%d already present??\n", phys_id, cpuid); @@ -435,6 +435,7 @@ */ smp_store_cpu_info(cpuid); + disable_APIC_timer(); /* * Allow the master to continue. */ @@ -443,7 +444,7 @@ /* * Synchronize the TSC with the BP */ - if (cpu_has_tsc) + if (cpu_has_tsc > 1) synchronize_tsc_ap(); } @@ -465,6 +466,7 @@ smp_callin(); while (!atomic_read(&smp_commenced)) rep_nop(); + enable_APIC_timer(); /* * low-memory mappings have been cleared, flush them from * the local TLBs too. @@ -803,16 +805,13 @@ if (!idle) panic("No idle process for CPU %d", cpu); - idle->processor = cpu; - idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ + init_idle(idle, cpu); map_cpu_to_boot_apicid(cpu, apicid); idle->thread.eip = (unsigned long) start_secondary; - del_from_runqueue(idle); unhash_process(idle); - init_tasks[cpu] = idle; /* start_eip had better be page-aligned! */ start_eip = setup_trampoline(); @@ -925,6 +924,7 @@ } cycles_t cacheflush_time; +unsigned long cache_decay_ticks; static void smp_tune_scheduling (void) { @@ -958,9 +958,13 @@ cacheflush_time = (cpu_khz>>10) * (cachesize<<10) / bandwidth; } + cache_decay_ticks = (long)cacheflush_time/cpu_khz * HZ / 1000; + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", (long)cacheflush_time/(cpu_khz/1000), ((long)cacheflush_time*100/(cpu_khz/1000)) % 100); + printk("task migration cache decay timeout: %ld msecs.\n", + (cache_decay_ticks + 1) * 1000 / HZ); } /* @@ -1026,8 +1030,7 @@ map_cpu_to_boot_apicid(0, boot_cpu_apicid); global_irq_holder = 0; - current->processor = 0; - init_idle(); + current->cpu = 0; smp_tune_scheduling(); /* @@ -1219,7 +1222,7 @@ /* * Synchronize the TSC with the AP */ - if (cpu_has_tsc && cpucount) + if (cpu_has_tsc > 1 && cpucount) synchronize_tsc_bp(); smp_done: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/smp.c linux.21rc1-ac2/arch/i386/kernel/smp.c --- linux.21rc1/arch/i386/kernel/smp.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/smp.c 2003-04-22 16:44:36.000000000 +0100 @@ -496,13 +496,23 @@ * it goes straight through and wastes no time serializing * anything. Worst case is that we lose a reschedule ... */ - void smp_send_reschedule(int cpu) { send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR); } /* + * this function sends a reschedule IPI to all (other) CPUs. + * This should only be used if some 'global' task became runnable, + * such as a RT task, that must be handled now. The first CPU + * that manages to grab the task will run it. + */ +void smp_send_reschedule_all(void) +{ + send_IPI_allbutself(RESCHEDULE_VECTOR); +} + +/* * Structure and data for smp_call_function(). This is designed to minimise * static memory requirements. It also looks cleaner. */ @@ -553,7 +563,7 @@ spin_lock(&call_lock); call_data = &data; - wmb(); + mb(); /* Send a message to all other CPUs and wait for them to respond */ send_IPI_allbutself(CALL_FUNCTION_VECTOR); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/speedstep.c linux.21rc1-ac2/arch/i386/kernel/speedstep.c --- linux.21rc1/arch/i386/kernel/speedstep.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/speedstep.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,731 @@ +/* + * $Id: speedstep.c,v 1.64 2003/01/02 22:16:26 db Exp $ + * + * (C) 2001 Dave Jones, Arjan van de ven. + * (C) 2002 Dominik Brodowski + * + * Licensed under the terms of the GNU GPL License version 2. + * Based upon reverse engineered information, and on Intel documentation + * for chipsets ICH2-M and ICH3-M. + * + * Many thanks to Ducrot Bruno for finding and fixing the last + * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler + * for extensive testing. + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + + +/********************************************************************* + * SPEEDSTEP - DEFINITIONS * + *********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + + +static struct cpufreq_driver *speedstep_driver; + +/* speedstep_chipset: + * It is necessary to know which chipset is used. As accesses to + * this device occur at various places in this module, we need a + * static struct pci_dev * pointing to that device. + */ +static unsigned int speedstep_chipset; +static struct pci_dev *speedstep_chipset_dev; + +#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002 +#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003 + + +/* speedstep_processor + */ +static unsigned int speedstep_processor = 0; +static int speedstep_coppermine = 0; + +#define SPEEDSTEP_PROCESSOR_PIII_C 0x00000001 /* Coppermine core */ +#define SPEEDSTEP_PROCESSOR_PIII_T 0x00000002 /* Tualatin core */ +#define SPEEDSTEP_PROCESSOR_P4M 0x00000003 /* P4-M with 100 MHz FSB */ + + +/* speedstep_[low,high]_freq + * There are only two frequency states for each processor. Values + * are in kHz for the time being. + */ +#define SPEEDSTEP_HIGH 0x00000000 +#define SPEEDSTEP_LOW 0x00000001 + +static struct cpufreq_frequency_table speedstep_freqs[] = { + {SPEEDSTEP_HIGH, 0}, + {SPEEDSTEP_LOW, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +#define speedstep_low_freq speedstep_freqs[SPEEDSTEP_LOW].frequency +#define speedstep_high_freq speedstep_freqs[SPEEDSTEP_HIGH].frequency + + +/* DEBUG + * Define it if you want verbose debug output, e.g. for bug reporting + */ +//#define SPEEDSTEP_DEBUG + +#ifdef SPEEDSTEP_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + + + +/********************************************************************* + * LOW LEVEL CHIPSET INTERFACE * + *********************************************************************/ + +/** + * speedstep_get_state - read the current SpeedStep state + * @state: Speedstep state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) + * + * Tries to read the SpeedStep state. Returns -EIO when there has been + * trouble to read the status or write to the control register, -EINVAL + * on an unsupported chipset, and zero on success. + */ +static int speedstep_get_state (unsigned int *state) +{ + unsigned long flags; + u32 pmbase; + u8 value; + + if (!speedstep_chipset_dev || !state) + return -EINVAL; + + switch (speedstep_chipset) { + case SPEEDSTEP_CHIPSET_ICH2M: + case SPEEDSTEP_CHIPSET_ICH3M: + /* get PMBASE */ + pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); + if (!(pmbase & 0x01)) + return -EIO; + + pmbase &= 0xFFFFFFFE; + if (!pmbase) + return -EIO; + + /* read state */ + local_irq_save(flags); + value = inb(pmbase + 0x50); + local_irq_restore(flags); + + dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + + *state = value & 0x01; + return 0; + + } + + printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\n"); + return -EINVAL; +} + + +/** + * speedstep_set_state - set the SpeedStep state + * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) + * + * Tries to change the SpeedStep state. + */ +static void speedstep_set_state (unsigned int state, int notify) +{ + u32 pmbase; + u8 pm2_blk; + u8 value; + unsigned long flags; + unsigned int oldstate; + struct cpufreq_freqs freqs; + + if (!speedstep_chipset_dev || (state > 0x1)) + return; + + if (speedstep_get_state(&oldstate)) + return; + + if (oldstate == state) + return; + + freqs.old = (oldstate == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq; + freqs.new = (state == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq; + freqs.cpu = 0; /* speedstep.c is UP only driver */ + + if (notify) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + switch (speedstep_chipset) { + case SPEEDSTEP_CHIPSET_ICH2M: + case SPEEDSTEP_CHIPSET_ICH3M: + /* get PMBASE */ + pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); + if (!(pmbase & 0x01)) + { + printk(KERN_ERR "cpufreq: could not find speedstep register\n"); + return; + } + + pmbase &= 0xFFFFFFFE; + if (!pmbase) { + printk(KERN_ERR "cpufreq: could not find speedstep register\n"); + return; + } + + /* Disable IRQs */ + local_irq_save(flags); + + /* read state */ + value = inb(pmbase + 0x50); + + dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + + /* write new state */ + value &= 0xFE; + value |= state; + + dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase); + + /* Disable bus master arbitration */ + pm2_blk = inb(pmbase + 0x20); + pm2_blk |= 0x01; + outb(pm2_blk, (pmbase + 0x20)); + + /* Actual transition */ + outb(value, (pmbase + 0x50)); + + /* Restore bus master arbitration */ + pm2_blk &= 0xfe; + outb(pm2_blk, (pmbase + 0x20)); + + /* check if transition was sucessful */ + value = inb(pmbase + 0x50); + + /* Enable IRQs */ + local_irq_restore(flags); + + dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + + if (state == (value & 0x1)) { + dprintk (KERN_INFO "cpufreq: change to %u MHz succeded\n", (freqs.new / 1000)); + } else { + printk (KERN_ERR "cpufreq: change failed - I/O error\n"); + } + break; + default: + printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\n"); + } + + if (notify) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return; +} + + +/** + * speedstep_activate - activate SpeedStep control in the chipset + * + * Tries to activate the SpeedStep status and control registers. + * Returns -EINVAL on an unsupported chipset, and zero on success. + */ +static int speedstep_activate (void) +{ + if (!speedstep_chipset_dev) + return -EINVAL; + + switch (speedstep_chipset) { + case SPEEDSTEP_CHIPSET_ICH2M: + case SPEEDSTEP_CHIPSET_ICH3M: + { + u16 value = 0; + + pci_read_config_word(speedstep_chipset_dev, + 0x00A0, &value); + if (!(value & 0x08)) { + value |= 0x08; + dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n"); + pci_write_config_word(speedstep_chipset_dev, + 0x00A0, value); + } + + return 0; + } + } + + printk (KERN_ERR "cpufreq: SpeedStep (TM) on this chipset unsupported.\n"); + return -EINVAL; +} + + +/** + * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic + * + * Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to + * the LPC bridge / PM module which contains all power-management + * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected + * chipset, or zero on failure. + */ +static unsigned int speedstep_detect_chipset (void) +{ + speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801CA_12, + PCI_ANY_ID, + PCI_ANY_ID, + NULL); + if (speedstep_chipset_dev) + return SPEEDSTEP_CHIPSET_ICH3M; + + + speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801BA_10, + PCI_ANY_ID, + PCI_ANY_ID, + NULL); + if (speedstep_chipset_dev) { + /* speedstep.c causes lockups on Dell Inspirons 8000 and + * 8100 which use a pretty old revision of the 82815 + * host brige. Abort on these systems. + */ + static struct pci_dev *hostbridge; + u8 rev = 0; + + hostbridge = pci_find_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82815_MC, + PCI_ANY_ID, + PCI_ANY_ID, + NULL); + + if (!hostbridge) + return SPEEDSTEP_CHIPSET_ICH2M; + + pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev); + if (rev < 5) { + dprintk(KERN_INFO "cpufreq: hostbrige does not support speedstep\n"); + speedstep_chipset_dev = NULL; + return 0; + } + + return SPEEDSTEP_CHIPSET_ICH2M; + } + + return 0; +} + + + +/********************************************************************* + * LOW LEVEL PROCESSOR INTERFACE * + *********************************************************************/ + + +/** + * pentium3_get_frequency - get the core frequencies for PIIIs + * + * Returns the core frequency of a Pentium III processor (in kHz) + */ +static unsigned int pentium3_get_frequency (void) +{ + /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */ + struct { + unsigned int ratio; /* Frequency Multiplier (x10) */ + u8 bitmap; /* power on configuration bits + [27, 25:22] (in MSR 0x2a) */ + } msr_decode_mult [] = { + { 30, 0x01 }, + { 35, 0x05 }, + { 40, 0x02 }, + { 45, 0x06 }, + { 50, 0x00 }, + { 55, 0x04 }, + { 60, 0x0b }, + { 65, 0x0f }, + { 70, 0x09 }, + { 75, 0x0d }, + { 80, 0x0a }, + { 85, 0x26 }, + { 90, 0x20 }, + { 100, 0x2b }, + { 0, 0xff } /* error or unknown value */ + }; + /* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */ + struct { + unsigned int value; /* Front Side Bus speed in MHz */ + u8 bitmap; /* power on configuration bits [18: 19] + (in MSR 0x2a) */ + } msr_decode_fsb [] = { + { 66, 0x0 }, + { 100, 0x2 }, + { 133, 0x1 }, + { 0, 0xff} + }; + u32 msr_lo, msr_tmp; + int i = 0, j = 0; + struct cpuinfo_x86 *c = cpu_data; + + /* read MSR 0x2a - we only need the low 32 bits */ + rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); + dprintk(KERN_DEBUG "cpufreq: P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); + msr_tmp = msr_lo; + + /* decode the FSB */ + msr_tmp &= 0x00c0000; + msr_tmp >>= 18; + while (msr_tmp != msr_decode_fsb[i].bitmap) { + if (msr_decode_fsb[i].bitmap == 0xff) + return -EINVAL; + i++; + } + + /* decode the multiplier */ + if ((c->x86_model == 0x08) && (c->x86_mask == 0x01)) + /* different on early Coppermine PIII */ + msr_lo &= 0x03c00000; + else + msr_lo &= 0x0bc00000; + msr_lo >>= 22; + while (msr_lo != msr_decode_mult[j].bitmap) { + if (msr_decode_mult[j].bitmap == 0xff) + return -EINVAL; + j++; + } + + return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100); +} + + +/** + * pentium4_get_frequency - get the core frequency for P4-Ms + * + * Should return the core frequency (in kHz) for P4-Ms. + */ +static unsigned int pentium4_get_frequency(void) +{ + u32 msr_lo, msr_hi; + + rdmsr(0x2c, msr_lo, msr_hi); + + dprintk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); + + /* First 12 bits seem to change a lot (0x511, 0x410 and 0x30f seen + * yet). Next 12 bits always seem to be 0x300. If this is not true + * on this CPU, complain. Last 8 bits are frequency (in 100MHz). + */ + if (msr_hi || ((msr_lo & 0x00FFF000) != 0x300000)) { + printk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); + printk(KERN_INFO "cpufreq: problem in initialization. Please contact Dominik Brodowski\n"); + printk(KERN_INFO "cpufreq: and attach this dmesg. Thanks in advance\n"); + return 0; + } + + msr_lo >>= 24; + return (msr_lo * 100000); +} + + +/** + * speedstep_detect_processor - detect Intel SpeedStep-capable processors. + * + * Returns the SPEEDSTEP_PROCESSOR_-number for the detected processor, + * or zero on failure. + */ +static unsigned int speedstep_detect_processor (void) +{ + struct cpuinfo_x86 *c = cpu_data; + u32 ebx; + + if ((c->x86_vendor != X86_VENDOR_INTEL) || + ((c->x86 != 6) && (c->x86 != 0xF))) + return 0; + + if (c->x86 == 0xF) { + /* Intel Pentium 4 Mobile P4-M */ + if (c->x86_model != 2) + return 0; + + if ((c->x86_mask != 4) && (c->x86_mask != 7)) + return 0; + + ebx = cpuid_ebx(0x00000001); + ebx &= 0x000000FF; + if ((ebx != 0x0e) && (ebx != 0x0f)) + return 0; + + return SPEEDSTEP_PROCESSOR_P4M; + } + + switch (c->x86_model) { + case 0x0B: /* Intel PIII [Tualatin] */ + /* cpuid_ebx(1) is 0x04 for desktop PIII, + 0x06 for mobile PIII-M */ + ebx = cpuid_ebx(0x00000001); + + ebx &= 0x000000FF; + if (ebx != 0x06) + return 0; + + /* So far all PIII-M processors support SpeedStep. See + * Intel's 24540633.pdf of August 2002 + */ + + return SPEEDSTEP_PROCESSOR_PIII_T; + + case 0x08: /* Intel PIII [Coppermine] */ + { + u32 msr_lo, msr_hi; + + /* all mobile PIII Coppermines have FSB 100 MHz + * ==> sort out a few desktop PIIIs. */ + rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi); + dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_EBL_Cr_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi); + msr_lo &= 0x00c0000; + if (msr_lo != 0x0080000) + return 0; + + if (speedstep_coppermine) + return SPEEDSTEP_PROCESSOR_PIII_C; + + printk(KERN_INFO "cpufreq: in case this is a SpeedStep-capable Intel Pentium III Coppermine\n"); + printk(KERN_INFO "cpufreq: processor, please pass the boot option or module parameter\n"); + printk(KERN_INFO "cpufreq: `speedstep_coppermine=1` to the kernel. Thanks!\n"); + return 0; + } + + default: + return 0; + } +} + + + +/********************************************************************* + * HIGH LEVEL FUNCTIONS * + *********************************************************************/ + +/** + * speedstep_detect_speeds - detects low and high CPU frequencies. + * + * Detects the low and high CPU frequencies in kHz. Returns 0 on + * success or -EINVAL / -EIO on problems. + */ +static int speedstep_detect_speeds (void) +{ + unsigned long flags; + unsigned int state; + int i, result; + + /* Disable irqs for entire detection process */ + local_irq_save(flags); + + for (i=0; i<2; i++) { + /* read the current state */ + result = speedstep_get_state(&state); + if (result) + return result; + + /* save the correct value, and switch to other */ + if (state == SPEEDSTEP_LOW) { + switch (speedstep_processor) { + case SPEEDSTEP_PROCESSOR_PIII_C: + case SPEEDSTEP_PROCESSOR_PIII_T: + speedstep_low_freq = pentium3_get_frequency(); + break; + case SPEEDSTEP_PROCESSOR_P4M: + speedstep_low_freq = pentium4_get_frequency(); + } + speedstep_set_state(SPEEDSTEP_HIGH, 0); + } else { + switch (speedstep_processor) { + case SPEEDSTEP_PROCESSOR_PIII_C: + case SPEEDSTEP_PROCESSOR_PIII_T: + speedstep_high_freq = pentium3_get_frequency(); + break; + case SPEEDSTEP_PROCESSOR_P4M: + speedstep_high_freq = pentium4_get_frequency(); + } + speedstep_set_state(SPEEDSTEP_LOW, 0); + } + } + + local_irq_restore(flags); + + if (!speedstep_low_freq || !speedstep_high_freq || + (speedstep_low_freq == speedstep_high_freq)) + return -EIO; + + return 0; +} + + +/** + * speedstep_setpolicy - set a new CPUFreq policy + * @policy: new policy + * + * Sets a new CPUFreq policy. + */ +static int speedstep_setpolicy (struct cpufreq_policy *policy) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_setpolicy(policy, &speedstep_freqs[0], &newstate)) + return -EINVAL; + + speedstep_set_state(newstate, 1); + + return 0; +} + + +/** + * speedstep_verify - verifies a new CPUFreq policy + * @freq: new policy + * + * Limit must be within speedstep_low_freq and speedstep_high_freq, with + * at least one border included. + */ +static int speedstep_verify (struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]); +} + + +#ifndef MODULE +/** + * speedstep_setup speedstep command line parameter parsing + * + * speedstep command line parameter. Use: + * speedstep_coppermine=1 + * if the CPU in your notebook is a SpeedStep-capable Intel + * Pentium III Coppermine. These processors cannot be detected + * automatically, as Intel continues to consider the detection + * alogrithm as proprietary material. + */ +static int __init speedstep_setup(char *str) +{ + speedstep_coppermine = simple_strtoul(str, &str, 0); + return 1; +} +__setup("speedstep_coppermine=", speedstep_setup); +#endif + +/** + * speedstep_init - initializes the SpeedStep CPUFreq driver + * + * Initializes the SpeedStep support. Returns -ENODEV on unsupported + * devices, -EINVAL on problems during initiatization, and zero on + * success. + */ +static int __init speedstep_init(void) +{ + int result; + unsigned int speed; + struct cpufreq_driver *driver; + + + /* detect chipset */ + speedstep_chipset = speedstep_detect_chipset(); + + /* detect chipset */ + if (speedstep_chipset) + speedstep_processor = speedstep_detect_processor(); + + if ((!speedstep_chipset) || (!speedstep_processor)) { + printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this %s not (yet) available.\n", speedstep_chipset ? "processor" : "chipset"); + return -ENODEV; + } + + dprintk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) support $Revision: 1.64 $\n"); + dprintk(KERN_DEBUG "cpufreq: chipset 0x%x - processor 0x%x\n", + speedstep_chipset, speedstep_processor); + + /* activate speedstep support */ + result = speedstep_activate(); + if (result) + return result; + + /* detect low and high frequency */ + result = speedstep_detect_speeds(); + if (result) + return result; + + /* get current speed setting */ + result = speedstep_get_state(&speed); + if (result) + return result; + + speed = (speed == SPEEDSTEP_LOW) ? speedstep_low_freq : speedstep_high_freq; + + dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", + (speed == speedstep_low_freq) ? "low" : "high", + (speed / 1000)); + + /* initialization of main "cpufreq" code*/ + driver = kmalloc(sizeof(struct cpufreq_driver) + + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->policy = (struct cpufreq_policy *) (driver + 1); + + driver->policy[0].cpu = 0; + result = cpufreq_frequency_table_cpuinfo(&driver->policy[0], &speedstep_freqs[0]); + if (result) { + kfree(driver); + return result; + } + +#ifdef CONFIG_CPU_FREQ_24_API + driver->cpu_cur_freq[0] = speed; +#endif + + driver->verify = &speedstep_verify; + driver->setpolicy = &speedstep_setpolicy; + + driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; + + driver->policy[0].policy = (speed == speedstep_low_freq) ? + CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE; + + speedstep_driver = driver; + + result = cpufreq_register(driver); + if (result) { + speedstep_driver = NULL; + kfree(driver); + } + + return result; +} + + +/** + * speedstep_exit - unregisters SpeedStep support + * + * Unregisters SpeedStep support. + */ +static void __exit speedstep_exit(void) +{ + if (speedstep_driver) { + cpufreq_unregister(); + kfree(speedstep_driver); + } +} + + +MODULE_AUTHOR ("Dave Jones , Dominik Brodowski "); +MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors."); +MODULE_LICENSE ("GPL"); +module_init(speedstep_init); +module_exit(speedstep_exit); + +MODULE_PARM (speedstep_coppermine, "i"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/sys_i386.c linux.21rc1-ac2/arch/i386/kernel/sys_i386.c --- linux.21rc1/arch/i386/kernel/sys_i386.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/sys_i386.c 2003-04-22 16:44:36.000000000 +0100 @@ -139,7 +139,10 @@ switch (call) { case SEMOP: - return sys_semop (first, (struct sembuf *)ptr, second); + return sys_semtimedop (first, (struct sembuf *)ptr, second, NULL); + case SEMTIMEDOP: + return sys_semtimedop (first, (struct sembuf *)ptr, second, + (const struct timespec *)fifth); case SEMGET: return sys_semget (first, second, third); case SEMCTL: { @@ -200,7 +203,7 @@ return sys_shmctl (first, second, (struct shmid_ds *) ptr); default: - return -EINVAL; + return -ENOSYS; } } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/time.c linux.21rc1-ac2/arch/i386/kernel/time.c --- linux.21rc1/arch/i386/kernel/time.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/time.c 2003-04-22 16:44:36.000000000 +0100 @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -833,6 +835,50 @@ return 0; } +#ifdef CONFIG_CPU_FREQ +static unsigned int ref_freq = 0; +static unsigned long loops_per_jiffy_ref = 0; + +#ifndef CONFIG_SMP +static unsigned long fast_gettimeoffset_ref = 0; +static unsigned long cpu_khz_ref = 0; +#endif + +static int +time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freq = data; + + if (!ref_freq) { + ref_freq = freq->old; + loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy; +#ifndef CONFIG_SMP + fast_gettimeoffset_ref = fast_gettimeoffset_quotient; + cpu_khz_ref = cpu_khz; +#endif + } + + if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || + (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) { + cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); +#ifndef CONFIG_SMP + if (use_tsc) { + fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq); + cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); + } +#endif + } + + return 0; +} + +static struct notifier_block time_cpufreq_notifier_block = { + .notifier_call = time_cpufreq_notifier +}; +#endif + + void __init time_init(void) { extern int x86_udelay_tsc; @@ -904,6 +950,9 @@ } } +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); +#endif #ifdef CONFIG_VISWS printk("Starting Cobalt Timer system clock\n"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/kernel/traps.c linux.21rc1-ac2/arch/i386/kernel/traps.c --- linux.21rc1/arch/i386/kernel/traps.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/kernel/traps.c 2003-04-22 16:44:36.000000000 +0100 @@ -284,6 +284,20 @@ void die(const char * str, struct pt_regs * regs, long err) { +#ifdef CONFIG_PNPBIOS + if (regs->xcs == 0x60 || regs->xcs == 0x68) + { + extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp; + extern u32 pnp_bios_is_utter_crap; + pnp_bios_is_utter_crap = 1; + printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n"); + __asm__ volatile( + "movl %0, %%esp\n\t" + "jmp %1\n\t" + : "=a" (pnp_bios_fault_esp), "=b" (pnp_bios_fault_eip)); + panic("do_trap: can't hit this"); + } +#endif console_verbose(); spin_lock_irq(&die_lock); bust_spinlocks(1); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/math-emu/fpu_system.h linux.21rc1-ac2/arch/i386/math-emu/fpu_system.h --- linux.21rc1/arch/i386/math-emu/fpu_system.h 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/math-emu/fpu_system.h 2003-04-22 16:44:36.000000000 +0100 @@ -20,7 +20,7 @@ of the stack frame of math_emulate() */ #define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg -#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.segments)[(s) >> 3]) +#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3]) #define SEG_D_SIZE(x) ((x).b & (3 << 21)) #define SEG_G_BIT(x) ((x).b & (1 << 23)) #define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/mm/fault.c linux.21rc1-ac2/arch/i386/mm/fault.c --- linux.21rc1/arch/i386/mm/fault.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/mm/fault.c 2003-04-22 18:25:56.000000000 +0100 @@ -76,9 +76,7 @@ return 1; check_stack: - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, start) == 0) + if (!expand_stack(vma, start)) goto good_area; bad_area: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/i386/mm/init.c linux.21rc1-ac2/arch/i386/mm/init.c --- linux.21rc1/arch/i386/mm/init.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/i386/mm/init.c 2003-04-22 16:44:36.000000000 +0100 @@ -510,7 +510,15 @@ if (!mem_map) BUG(); - +#ifdef CONFIG_HIGHMEM + /* check that fixmap and pkmap do not overlap */ + if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) { + printk(KERN_ERR "fixmap and kmap areas overlap - this will crash\n"); + printk(KERN_ERR "pkstart: %lxh pkend: %lxh fixstart %lxh\n", + PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE, FIXADDR_START); + BUG(); + } +#endif set_max_mapnr_init(); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ia64/ia32/sys_ia32.c linux.21rc1-ac2/arch/ia64/ia32/sys_ia32.c --- linux.21rc1/arch/ia64/ia32/sys_ia32.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ia64/ia32/sys_ia32.c 2003-04-22 16:44:36.000000000 +0100 @@ -2126,6 +2126,7 @@ #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 +#define SEMTIMEDOP 4 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 @@ -2553,6 +2554,17 @@ return err; } +static long +semtimedop32(int semid, struct sembuf *tsems, int nsems, + const struct timespec32 *timeout32) +{ + struct timespec t; + if (get_user (t.tv_sec, &timeout32->tv_sec) || + get_user (t.tv_nsec, &timeout32->tv_nsec)) + return -EFAULT; + return sys_semtimedop(semid, tsems, nsems, &t); +} + asmlinkage long sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) { @@ -2564,7 +2576,11 @@ switch (call) { case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ - return sys_semop(first, (struct sembuf *)AA(ptr), second); + return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, + NULL); + case SEMTIMEDOP: + return semtimedop32(first, (struct sembuf *)AA(ptr), second, + (const struct timespec32 *)AA(fifth)); case SEMGET: return sys_semget(first, second, third); case SEMCTL: @@ -3783,7 +3799,11 @@ return result; } -struct dqblk32 { +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + +#ifdef CONFIG_QIFACE_COMPAT +#ifdef CONFIG_QIFACE_V1 +struct user_dqblk32 { __u32 dqb_bhardlimit; __u32 dqb_bsoftlimit; __u32 dqb_curblocks; @@ -3793,49 +3813,82 @@ __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; +typedef struct v1c_mem_dqblk comp_dqblk_t; -asmlinkage long -sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *addr) +#define Q_COMP_GETQUOTA Q_V1_GETQUOTA +#define Q_COMP_SETQUOTA Q_V1_SETQUOTA +#define Q_COMP_SETQLIM Q_V1_SETQLIM +#define Q_COMP_SETUSE Q_V1_SETUSE +#else +struct user_dqblk32 { + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; +typedef struct v2c_mem_dqblk comp_dqblk_t; + +#define Q_COMP_GETQUOTA Q_V2_GETQUOTA +#define Q_COMP_SETQUOTA Q_V2_SETQUOTA +#define Q_COMP_SETQLIM Q_V2_SETQLIM +#define Q_COMP_SETUSE Q_V2_SETUSE +#endif + +asmlinkage long sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) { - extern asmlinkage long sys_quotactl (int, const char *, int, caddr_t); int cmds = cmd >> SUBCMDSHIFT; + long err; + comp_dqblk_t d; mm_segment_t old_fs; - struct dqblk d; char *spec; - long err; - + switch (cmds) { - case Q_GETQUOTA: - break; - case Q_SETQUOTA: - case Q_SETUSE: - case Q_SETQLIM: - if (copy_from_user (&d, addr, sizeof(struct dqblk32))) - return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; - break; - default: - return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr); + case Q_COMP_GETQUOTA: + break; + case Q_COMP_SETQUOTA: + case Q_COMP_SETUSE: + case Q_COMP_SETQLIM: + if (copy_from_user(&d, (struct user_dqblk32 *)addr, + sizeof (struct user_dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr); } - spec = getname32((void *) A(special)); + spec = getname (special); err = PTR_ERR(spec); - if (IS_ERR(spec)) + if (IS_ERR(spec)) return err; + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); + set_fs (old_fs); + putname (spec); + if (err) return err; - old_fs = get_fs (); - set_fs(KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); - set_fs(old_fs); - putname(spec); - if (cmds == Q_GETQUOTA) { + if (cmds == Q_COMP_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user(addr, &d, sizeof(struct dqblk32))) + ((struct user_dqblk32 *)&d)->dqb_itime = i; + ((struct user_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct user_dqblk32 *)addr, &d, + sizeof (struct user_dqblk32))) return -EFAULT; } - return err; + return 0; +} + +#else +/* No conversion needed for new interface */ +asmlinkage long sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) +{ + return sys_quotactl(cmd, special, id, addr); } +#endif asmlinkage long sys32_sched_rr_get_interval (pid_t pid, struct timespec32 *interval) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ia64/kernel/entry.S linux.21rc1-ac2/arch/ia64/kernel/entry.S --- linux.21rc1/arch/ia64/kernel/entry.S 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ia64/kernel/entry.S 2003-04-22 16:44:36.000000000 +0100 @@ -1200,7 +1200,7 @@ data8 ia64_ni_syscall data8 ia64_ni_syscall // 1245 data8 ia64_ni_syscall - data8 ia64_ni_syscall + data8 sys_semtimedop data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1250 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ia64/mm/fault.c linux.21rc1-ac2/arch/ia64/mm/fault.c --- linux.21rc1/arch/ia64/mm/fault.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ia64/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -127,8 +127,6 @@ check_expansion: if (!(prev_vma && (prev_vma->vm_flags & VM_GROWSUP) && (address == prev_vma->vm_end))) { - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (rgn_index(address) != rgn_index(vma->vm_start) || rgn_offset(address) >= RGN_MAP_LIMIT) goto bad_area; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/mips/au1000/common/serial.c linux.21rc1-ac2/arch/mips/au1000/common/serial.c --- linux.21rc1/arch/mips/au1000/common/serial.c 2003-04-22 16:38:56.000000000 +0100 +++ linux.21rc1-ac2/arch/mips/au1000/common/serial.c 2003-04-22 16:44:36.000000000 +0100 @@ -195,7 +195,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) + kdevname(tty->device), (info->flags), serial_refcount,info->count,atomic_read(&tty->count),s) #else #define DBG_CNT(s) #endif @@ -1909,7 +1909,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/mips/baget/vacserial.c linux.21rc1-ac2/arch/mips/baget/vacserial.c --- linux.21rc1/arch/mips/baget/vacserial.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/mips/baget/vacserial.c 2003-04-22 16:44:36.000000000 +0100 @@ -29,7 +29,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) baget_printk("(%s):[%x] refc=%d, serc=%d, ttyc=%d-> %s\n", \ - kdevname(tty->device),(info->flags),serial_refcount,info->count,tty->count,s) + kdevname(tty->device),(info->flags),serial_refcount,info->count,atomic_read(&tty->count),s) #else #define DBG_CNT(s) #endif @@ -1658,7 +1658,7 @@ baget_printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/mips/mm/fault.c linux.21rc1-ac2/arch/mips/mm/fault.c --- linux.21rc1/arch/mips/mm/fault.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/mips/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -111,8 +111,6 @@ goto bad_area; if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (expand_stack(vma, address)) goto bad_area; /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/mips64/mm/fault.c linux.21rc1-ac2/arch/mips64/mm/fault.c --- linux.21rc1/arch/mips64/mm/fault.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/mips64/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -135,8 +135,6 @@ goto bad_area; if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (expand_stack(vma, address)) goto bad_area; /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/parisc/config.in linux.21rc1-ac2/arch/parisc/config.in --- linux.21rc1/arch/parisc/config.in 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/parisc/config.in 2003-04-22 16:44:36.000000000 +0100 @@ -47,9 +47,6 @@ bool 'Symmetric multi-processing support' CONFIG_SMP bool 'Chassis LCD and LED support' CONFIG_CHASSIS_LCD_LED -bool 'Kernel Debugger support' CONFIG_KWDB -# define_bool CONFIG_KWDB n - bool 'U2/Uturn I/O MMU' CONFIG_IOMMU_CCIO bool 'VSC/GSC/HSC bus support' CONFIG_GSC dep_bool ' Lasi I/O support' CONFIG_GSC_LASI $CONFIG_GSC @@ -194,6 +191,7 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Debug spinlocks' CONFIG_DEBUG_SPINLOCK endmenu source lib/Config.in diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/parisc/kernel/lasimap.map linux.21rc1-ac2/arch/parisc/kernel/lasimap.map --- linux.21rc1/arch/parisc/kernel/lasimap.map 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/parisc/kernel/lasimap.map 1970-01-01 01:00:00.000000000 +0100 @@ -1,322 +0,0 @@ -# HP 712 kernel keymap. This uses 7 modifier combinations. - -keymaps 0-2,4-5,8,12 -# ie, plain, Shift, AltGr, Control, Control+Shift, Alt and Control+Alt - - -# Change the above line into -# keymaps 0-2,4-6,8,12 -# in case you want the entries -# altgr control keycode 83 = Boot -# altgr control keycode 111 = Boot -# below. -# -# In fact AltGr is used very little, and one more keymap can -# be saved by mapping AltGr to Alt (and adapting a few entries): -# keycode 100 = Alt -# -keycode 1 = F9 F19 Console_21 - control keycode 1 = F9 - alt keycode 1 = Console_9 - control alt keycode 1 = Console_9 -keycode 2 = -keycode 3 = F5 F15 Console_17 - control keycode 3 = F5 - alt keycode 3 = Console_5 - control alt keycode 3 = Console_5 -keycode 4 = F3 F13 Console_15 - control keycode 4 = F3 - alt keycode 4 = Console_3 - control alt keycode 4 = Console_3 -keycode 5 = F1 F11 Console_13 - control keycode 5 = F1 - alt keycode 5 = Console_1 - control alt keycode 5 = Console_1 -keycode 6 = F2 F12 Console_14 - control keycode 6 = F2 - alt keycode 6 = Console_2 - control alt keycode 6 = Console_2 -keycode 7 = F12 F12 Console_24 - control keycode 7 = F12 - alt keycode 7 = Console_12 - control alt keycode 7 = Console_12 -keycode 8 = -keycode 9 = F10 F20 Console_22 - control keycode 9 = F10 - alt keycode 9 = Console_10 - control alt keycode 9 = Console_10 -keycode 10 = F8 F18 Console_20 - control keycode 10 = F8 - alt keycode 10 = Console_8 - control alt keycode 10 = Console_8 -keycode 11 = F6 F16 Console_18 - control keycode 11 = F6 - alt keycode 11 = Console_6 - control alt keycode 11 = Console_6 -keycode 12 = F4 F14 Console_16 - control keycode 12 = F4 - alt keycode 12 = Console_4 - control alt keycode 12 = Console_4 -keycode 13 = Tab Tab - alt keycode 13 = Meta_Tab -keycode 14 = grave asciitilde - control keycode 14 = nul - alt keycode 14 = Meta_grave -keycode 15 = -keycode 16 = -keycode 17 = Alt -keycode 18 = Shift -keycode 19 = -keycode 20 = Control -keycode 21 = q -keycode 22 = one exclam exclam -keycode 23 = -keycode 24 = -keycode 25 = -keycode 26 = z -keycode 27 = s -keycode 28 = a - altgr keycode 28 = Hex_A -keycode 29 = w -keycode 30 = two at at -keycode 31 = -keycode 32 = -keycode 33 = c - altgr keycode 46 = Hex_C -keycode 34 = x -keycode 35 = d - altgr keycode 35 = Hex_D -keycode 36 = e - altgr keycode 36 = Hex_E -keycode 37 = four dollar -keycode 38 = three numbersign -keycode 39 = -keycode 40 = -keycode 41 = -keycode 42 = v -keycode 43 = f - altgr keycode 43 = Hex_F -keycode 44 = t -keycode 45 = r -keycode 46 = five percent -keycode 47 = -keycode 48 = -keycode 49 = n -keycode 50 = b - altgr keycode 50 = Hex_B -keycode 51 = h -keycode 52 = g -keycode 53 = y -keycode 54 = six asciicircum -keycode 55 = -keycode 56 = -keycode 57 = -keycode 58 = m -keycode 59 = j -keycode 60 = u -keycode 61 = seven ampersand -keycode 62 = eight asterisk asterisk -keycode 63 = -keycode 64 = -keycode 65 = comma less - alt keycode 65 = Meta_comma -keycode 66 = k -keycode 67 = i -keycode 68 = o -keycode 69 = zero parenright bracketright -keycode 70 = nine parenleft bracketleft -keycode 71 = -keycode 72 = -keycode 73 = period greater - control keycode 73 = Compose - alt keycode 73 = Meta_period -keycode 74 = slash question - control keycode 74 = Delete - alt keycode 53 = Meta_slash -keycode 75 = l -keycode 76 = semicolon colon - alt keycode 39 = Meta_semicolon -keycode 77 = p -keycode 78 = minus underscore -keycode 79 = -keycode 80 = -keycode 81 = -keycode 82 = apostrophe quotedbl - control keycode 82 = Control_g - alt keycode 40 = Meta_apostrophe -keycode 83 = -keycode 84 = bracketleft braceleft - control keycode 84 = Escape - alt keycode 26 = Meta_bracketleft -keycode 85 = equal plus -keycode 86 = -keycode 87 = -keycode 88 = Caps_Lock -keycode 88 = -keycode 89 = -keycode 89 = -keycode 89 = -keycode 90 = Return - alt keycode 90 = Meta_Control_m -keycode 91 = bracketright braceright asciitilde - control keycode 91 = Control_bracketright - alt keycode 91 = Meta_bracketright -keycode 92 = -keycode 93 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash -keycode 94 = -keycode 95 = -keycode 96 = -keycode 97 = -keycode 98 = -keycode 99 = -keycode 100 = -keycode 101 = -keycode 102 = BackSpace -keycode 103 = -keycode 104 = -keycode 105 = KP_1 - alt keycode 105 = Ascii_1 - altgr keycode 105 = Hex_1 -keycode 106 = -keycode 107 = KP_4 - alt keycode 107 = Ascii_4 - altgr keycode 107 = Hex_4 -keycode 108 = KP_7 - alt keycode 108 = Ascii_7 - altgr keycode 108 = Hex_7 -keycode 109 = -keycode 110 = -keycode 111 = -keycode 112 = KP_0 - alt keycode 82 = Ascii_0 - altgr keycode 82 = Hex_0 -keycode 113 = KP_Period -keycode 114 = KP_2 - alt keycode 114 = Ascii_2 - altgr keycode 114 = Hex_2 -keycode 115 = KP_5 - alt keycode 115 = Ascii_5 - altgr keycode 115 = Hex_5 -keycode 116 = KP_6 - alt keycode 116 = Ascii_6 - altgr keycode 116 = Hex_6 -keycode 117 = KP_8 - alt keycode 117 = Ascii_8 - altgr keycode 117 = Hex_8 -keycode 118 = Escape -keycode 119 = -keycode 120 = F11 -keycode 121 = KP_Add -keycode 122 = KP_3 - alt keycode 122 = Ascii_3 - altgr keycode 122 = Hex_3 -keycode 123 = KP_Subtract -keycode 124 = KP_Multiply -keycode 125 = KP_9 - alt keycode 125 = Ascii_9 - altgr keycode 125 = Hex_9 -keycode 126 = -# 131!! -keycode 127 = F7 F17 Console_19 - control keycode 127 = F7 - alt keycode 127 = Console_7 - control alt keycode 127 = Console_7 - -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/8xx_io/uart.c linux.21rc1-ac2/arch/ppc/8xx_io/uart.c --- linux.21rc1/arch/ppc/8xx_io/uart.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/8xx_io/uart.c 2003-04-22 16:44:36.000000000 +0100 @@ -1679,7 +1679,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/cputable.c linux.21rc1-ac2/arch/ppc/kernel/cputable.c --- linux.21rc1/arch/ppc/kernel/cputable.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/cputable.c 2003-04-22 18:16:10.000000000 +0100 @@ -26,8 +26,7 @@ extern void __setup_cpu_750fx(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_7400(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_7410(unsigned long offset, int cpu_nr, struct cpu_spec* spec); -extern void __setup_cpu_7450(unsigned long offset, int cpu_nr, struct cpu_spec* spec); -extern void __setup_cpu_7455(unsigned long offset, int cpu_nr, struct cpu_spec* spec); +extern void __setup_cpu_745x(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_power3(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_power4(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_8xx(unsigned long offset, int cpu_nr, struct cpu_spec* spec); @@ -154,7 +153,7 @@ 0xffff0000, 0x70000000, "750FX", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP | - CPU_FTR_DUAL_PLL_750FX, + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS, COMMON_PPC, 32, 32, __setup_cpu_750fx @@ -201,7 +200,7 @@ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7450 + __setup_cpu_745x }, { /* 7450 2.1 */ 0xffffffff, 0x80000201, "7450", @@ -211,7 +210,7 @@ CPU_FTR_L3_DISABLE_NAP, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7450 + __setup_cpu_745x }, { /* 7450 2.3 and newer */ 0xffff0000, 0x80000000, "7450", @@ -220,35 +219,46 @@ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7450 + __setup_cpu_745x }, { /* 7455 rev 1.x */ 0xffffff00, 0x80010100, "7455", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | - CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450, + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7455 + __setup_cpu_745x }, { /* 7455 rev 2.0 */ 0xffffffff, 0x80010200, "7455", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | - CPU_FTR_L3_DISABLE_NAP, + CPU_FTR_L3_DISABLE_NAP | CPU_FTR_HAS_HIGH_BATS, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7455 + __setup_cpu_745x }, { /* 7455 others */ 0xffff0000, 0x80010000, "7455", CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | - CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR, + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | + CPU_FTR_HAS_HIGH_BATS, + COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, + 32, 32, + __setup_cpu_745x + }, + { /* 7457 */ + 0xffff0000, 0x80020000, "7457", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_NAP | + CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | + CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | + CPU_FTR_HAS_HIGH_BATS, COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC, 32, 32, - __setup_cpu_7455 + __setup_cpu_745x }, { /* 82xx (8240, 8245, 8260 are all 603e cores) */ 0x7fff0000, 0x00810000, "82xx", diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/entry.S linux.21rc1-ac2/arch/ppc/kernel/entry.S --- linux.21rc1/arch/ppc/kernel/entry.S 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/entry.S 2003-04-22 16:44:36.000000000 +0100 @@ -260,7 +260,9 @@ .globl ret_from_fork ret_from_fork: +#ifdef CONFIG_SMP bl schedule_tail +#endif lwz r0,TASK_PTRACE(r2) andi. r0,r0,PT_TRACESYS bnel- syscall_trace diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/i8259.c linux.21rc1-ac2/arch/ppc/kernel/i8259.c --- linux.21rc1/arch/ppc/kernel/i8259.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/i8259.c 2003-04-22 18:16:10.000000000 +0100 @@ -141,6 +141,7 @@ NULL }; +#if 0 /* Do not request these before the host bridge resource have been setup */ static struct resource pic1_iores = { "8259 (master)", 0x20, 0x21, IORESOURCE_BUSY }; @@ -152,6 +153,7 @@ static struct resource pic_edgectrl_iores = { "8259 edge control", 0x4d0, 0x4d1, IORESOURCE_BUSY }; +#endif void __init i8259_init(unsigned long intack_addr) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/idle.c linux.21rc1-ac2/arch/ppc/kernel/idle.c --- linux.21rc1/arch/ppc/kernel/idle.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/idle.c 2003-04-22 16:44:36.000000000 +0100 @@ -48,9 +48,6 @@ do_power_save = 1; /* endless loop with no priority at all */ - current->nice = 20; - current->counter = -100; - init_idle(); for (;;) { #ifdef CONFIG_SMP if (!do_power_save) { @@ -70,11 +67,8 @@ if (do_power_save && !current->need_resched) power_save_6xx(); #endif /* CONFIG_6xx */ - - if (current->need_resched) { - schedule(); - check_pgt_cache(); - } + schedule(); + check_pgt_cache(); } return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/mk_defs.c linux.21rc1-ac2/arch/ppc/kernel/mk_defs.c --- linux.21rc1/arch/ppc/kernel/mk_defs.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/mk_defs.c 2003-04-22 16:44:36.000000000 +0100 @@ -34,8 +34,7 @@ /*DEFINE(KERNELBASE, KERNELBASE);*/ DEFINE(STATE, offsetof(struct task_struct, state)); DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); - DEFINE(COUNTER, offsetof(struct task_struct, counter)); - DEFINE(PROCESSOR, offsetof(struct task_struct, processor)); + DEFINE(PROCESSOR, offsetof(struct task_struct, cpu)); DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(THREAD, offsetof(struct task_struct, thread)); DEFINE(MM, offsetof(struct task_struct, mm)); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/ppc_defs.h linux.21rc1-ac2/arch/ppc/kernel/ppc_defs.h --- linux.21rc1/arch/ppc/kernel/ppc_defs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/ppc_defs.h 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,81 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ +#define STATE 0 +#define NEXT_TASK 76 +#define PROCESSOR 32 +#define SIGPENDING 8 +#define THREAD 608 +#define MM 84 +#define ACTIVE_MM 88 +#define TASK_STRUCT_SIZE 1520 +#define KSP 0 +#define PGDIR 16 +#define LAST_SYSCALL 20 +#define PT_REGS 8 +#define PT_TRACESYS 2 +#define TASK_FLAGS 4 +#define TASK_PTRACE 24 +#define NEED_RESCHED 20 +#define THREAD_FPR0 24 +#define THREAD_FPSCR 284 +#define THREAD_VR0 288 +#define THREAD_VRSAVE 816 +#define THREAD_VSCR 800 +#define TASK_UNION_SIZE 8192 +#define STACK_FRAME_OVERHEAD 16 +#define INT_FRAME_SIZE 192 +#define GPR0 16 +#define GPR1 20 +#define GPR2 24 +#define GPR3 28 +#define GPR4 32 +#define GPR5 36 +#define GPR6 40 +#define GPR7 44 +#define GPR8 48 +#define GPR9 52 +#define GPR10 56 +#define GPR11 60 +#define GPR12 64 +#define GPR13 68 +#define GPR14 72 +#define GPR15 76 +#define GPR16 80 +#define GPR17 84 +#define GPR18 88 +#define GPR19 92 +#define GPR20 96 +#define GPR21 100 +#define GPR22 104 +#define GPR23 108 +#define GPR24 112 +#define GPR25 116 +#define GPR26 120 +#define GPR27 124 +#define GPR28 128 +#define GPR29 132 +#define GPR30 136 +#define GPR31 140 +#define _NIP 144 +#define _MSR 148 +#define _CTR 156 +#define _LINK 160 +#define _CCR 168 +#define _MQ 172 +#define _XER 164 +#define _DAR 180 +#define _DSISR 184 +#define _DEAR 180 +#define _ESR 184 +#define ORIG_GPR3 152 +#define RESULT 188 +#define TRAP 176 +#define CLONE_VM 256 +#define MM_PGD 12 +#define CPU_SPEC_ENTRY_SIZE 32 +#define CPU_SPEC_PVR_MASK 0 +#define CPU_SPEC_PVR_VALUE 4 +#define CPU_SPEC_FEATURES 12 +#define CPU_SPEC_SETUP 28 +#define NUM_USER_SEGMENTS 8 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/kernel/smp.c linux.21rc1-ac2/arch/ppc/kernel/smp.c --- linux.21rc1/arch/ppc/kernel/smp.c 2003-04-22 16:39:33.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/kernel/smp.c 2003-04-22 17:01:10.000000000 +0100 @@ -294,8 +294,6 @@ cpu_callin_map[0] = 1; current->processor = 0; - init_idle(); - for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; prof_multiplier[i] = 1; @@ -351,7 +349,8 @@ p = init_task.prev_task; if (!p) panic("No idle task for CPU %d", i); - del_from_runqueue(p); + init_idle(p, i); + unhash_process(p); init_tasks[i] = p; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/ppc/mm/fault.c linux.21rc1-ac2/arch/ppc/mm/fault.c --- linux.21rc1/arch/ppc/mm/fault.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/ppc/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -141,42 +141,40 @@ goto bad_area; if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (!is_write) - goto bad_area; - - /* - * N.B. The rs6000/xcoff ABI allows programs to access up to - * a few hundred bytes below the stack pointer. - * The kernel signal delivery code writes up to about 1.5kB - * below the stack pointer (r1) before decrementing it. - * The exec code can write slightly over 640kB to the stack - * before setting the user r1. Thus we allow the stack to - * expand to 1MB without further checks. - */ - if (address + 0x100000 < vma->vm_end) { - /* get user regs even if this fault is in kernel mode */ - struct pt_regs *uregs = current->thread.regs; - if (uregs == NULL) - goto bad_area; - - /* - * A user-mode access to an address a long way below - * the stack pointer is only valid if the instruction - * is one which would update the stack pointer to the - * address accessed if the instruction completed, - * i.e. either stwu rs,n(r1) or stwux rs,r1,rb - * (or the byte, halfword, float or double forms). - * - * If we don't check this then any write to the area - * between the last mapped region and the stack will - * expand the stack rather than segfaulting. - */ - if (address + 2048 < uregs->gpr[1] - && (!user_mode(regs) || !store_updates_sp(regs))) - goto bad_area; - } + if (!is_write) + goto bad_area; + + /* + * N.B. The rs6000/xcoff ABI allows programs to access up to + * a few hundred bytes below the stack pointer. + * The kernel signal delivery code writes up to about 1.5kB + * below the stack pointer (r1) before decrementing it. + * The exec code can write slightly over 640kB to the stack + * before setting the user r1. Thus we allow the stack to + * expand to 1MB without further checks. + */ + if (address + 0x100000 < vma->vm_end) { + /* get user regs even if this fault is in kernel mode */ + struct pt_regs *uregs = current->thread.regs; + if (uregs == NULL) + goto bad_area; + + /* + * A user-mode access to an address a long way below + * the stack pointer is only valid if the instruction + * is one which would update the stack pointer to the + * address accessed if the instruction completed, + * i.e. either stwu rs,n(r1) or stwux rs,r1,rb + * (or the byte, halfword, float or double forms). + * + * If we don't check this then any write to the area + * between the last mapped region and the stack will + * expand the stack rather than segfaulting. + */ + if (address + 2048 < uregs->gpr[1] + && (!user_mode(regs) || !store_updates_sp(regs))) + goto bad_area; + } if (expand_stack(vma, address)) goto bad_area; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/defconfig linux.21rc1-ac2/arch/s390/defconfig --- linux.21rc1/arch/s390/defconfig 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/defconfig 2003-04-25 13:53:58.000000000 +0100 @@ -91,22 +91,23 @@ CONFIG_TN3270_CONSOLE=y CONFIG_TN3215=y CONFIG_TN3215_CONSOLE=y -CONFIG_HWC=y -CONFIG_HWC_CONSOLE=y -CONFIG_HWC_CPI=m +CONFIG_SCLP=y +CONFIG_SCLP_TTY=y +CONFIG_SCLP_CONSOLE=y +CONFIG_SCLP_VT220_TTY=y +CONFIG_SCLP_VT220_CONSOLE=y +CONFIG_SCLP_CPI=m CONFIG_S390_TAPE=m # # S/390 tape interface support # -CONFIG_S390_TAPE_CHAR=y CONFIG_S390_TAPE_BLOCK=y # # S/390 tape hardware support # -CONFIG_S390_TAPE_3490=m -CONFIG_S390_TAPE_3480=m +CONFIG_S390_TAPE_34XX=m # # Network device drivers @@ -118,6 +119,7 @@ # CONFIG_TUN is not set CONFIG_NET_ETHERNET=y CONFIG_TR=y +CONFIG_C7000=m # CONFIG_FDDI is not set # @@ -151,13 +153,18 @@ CONFIG_SHARED_IPV6_CARDS=y # CONFIG_KHTTPD is not set # CONFIG_ATM is not set -CONFIG_VLAN_8021Q=m +# CONFIG_VLAN_8021Q is not set # # # # CONFIG_IPX is not set # CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set # CONFIG_DECNET is not set # CONFIG_BRIDGE is not set # CONFIG_X25 is not set @@ -175,6 +182,11 @@ # CONFIG_NET_SCHED is not set # +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# # File systems # # CONFIG_QUOTA is not set @@ -187,6 +199,8 @@ # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set # CONFIG_BFS_FS is not set # CONFIG_EXT3_FS is not set # CONFIG_JBD is not set @@ -200,23 +214,27 @@ # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set # CONFIG_TMPFS is not set -# CONFIG_RAMFS is not set +CONFIG_RAMFS=y # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y -CONFIG_DEVFS_FS=y -CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set # CONFIG_DEVFS_DEBUG is not set # CONFIG_DEVPTS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set +# CONFIG_XIP2FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UDF_FS is not set @@ -234,6 +252,7 @@ # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -247,7 +266,6 @@ # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set # CONFIG_ZISOFS_FS is not set -# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -264,6 +282,7 @@ # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set # CONFIG_SMB_NLS is not set # CONFIG_NLS is not set @@ -271,3 +290,9 @@ # Kernel hacking # CONFIG_MAGIC_SYSRQ=y + +# +# Library routines +# +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/asm-offsets.c linux.21rc1-ac2/arch/s390/kernel/asm-offsets.c --- linux.21rc1/arch/s390/kernel/asm-offsets.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/asm-offsets.c 2003-04-25 13:47:51.000000000 +0100 @@ -26,7 +26,7 @@ DEFINE(__TASK_need_resched, offsetof(struct task_struct, need_resched),); DEFINE(__TASK_ptrace, offsetof(struct task_struct, ptrace),); - DEFINE(__TASK_processor, offsetof(struct task_struct, processor),); + DEFINE(__TASK_processor, offsetof(struct task_struct, cpu),); return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/bitmap.S linux.21rc1-ac2/arch/s390/kernel/bitmap.S --- linux.21rc1/arch/s390/kernel/bitmap.S 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/bitmap.S 2003-04-25 13:47:51.000000000 +0100 @@ -35,3 +35,21 @@ .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + .globl _sb_findmap +_sb_findmap: + .byte 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/entry.S linux.21rc1-ac2/arch/s390/kernel/entry.S --- linux.21rc1/arch/s390/kernel/entry.S 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/entry.S 2003-04-22 16:44:36.000000000 +0100 @@ -254,13 +254,14 @@ ret_from_fork: basr %r13,0 l %r13,.Lentry_base-.(%r13) # setup base pointer to &entry_base + # not saving R14 here because we go to sysc_return ultimately + l %r1,BASED(.Lschedtail) + basr %r14,%r1 # call schedule_tail (unlock stuff) GET_CURRENT # load pointer to task_struct to R9 stosm 24(%r15),0x03 # reenable interrupts sr %r0,%r0 # child returns 0 st %r0,SP_R2(%r15) # store return value (change R2 on stack) - l %r1,BASED(.Lschedtail) - la %r14,BASED(sysc_return) - br %r1 # call schedule_tail, return to sysc_return + b BASED(sysc_return) # # clone, fork, vfork, exec and sigreturn need glue, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/process.c linux.21rc1-ac2/arch/s390/kernel/process.c --- linux.21rc1/arch/s390/kernel/process.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/process.c 2003-04-25 13:47:51.000000000 +0100 @@ -50,15 +50,11 @@ * The idle loop on a S390... */ -int cpu_idle(void *unused) +int cpu_idle(void) { psw_t wait_psw; unsigned long reg; - /* endless idle loop with no priority at all */ - init_idle(); - current->nice = 20; - current->counter = -100; while (1) { if (current->need_resched) { schedule(); @@ -94,7 +90,7 @@ { struct task_struct *tsk = current; - printk("CPU: %d %s\n", tsk->processor, print_tainted()); + printk("CPU: %d %s\n", tsk->cpu, print_tainted()); printk("Process %s (pid: %d, task: %08lx, ksp: %08x)\n", current->comm, current->pid, (unsigned long) tsk, tsk->thread.ksp); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/setup.c linux.21rc1-ac2/arch/s390/kernel/setup.c --- linux.21rc1/arch/s390/kernel/setup.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/setup.c 2003-04-25 13:53:30.000000000 +0100 @@ -166,9 +166,9 @@ static int __init conmode_setup(char *str) { -#if defined(CONFIG_HWC_CONSOLE) - if (strncmp(str, "hwc", 4) == 0) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0) + SET_CONSOLE_SCLP; #endif #if defined(CONFIG_TN3215_CONSOLE) if (strncmp(str, "3215", 5) == 0) @@ -200,8 +200,8 @@ */ cpcmd("TERM CONMODE 3215", NULL, 0); if (ptr == NULL) { -#if defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif return; } @@ -210,16 +210,16 @@ SET_CONSOLE_3270; #elif defined(CONFIG_TN3215_CONSOLE) SET_CONSOLE_3215; -#elif defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } else if (strncmp(ptr + 8, "3215", 4) == 0) { #if defined(CONFIG_TN3215_CONSOLE) SET_CONSOLE_3215; #elif defined(CONFIG_TN3270_CONSOLE) SET_CONSOLE_3270; -#elif defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } } else if (MACHINE_IS_P390) { @@ -229,8 +229,8 @@ SET_CONSOLE_3270; #endif } else { -#if defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } } @@ -273,21 +273,25 @@ /* * Reboot, halt and power_off stubs. They just call _machine_restart, - * _machine_halt or _machine_power_off. + * _machine_halt or _machine_power_off after making sure that all pending + * printks reached their destination. */ void machine_restart(char *command) { + console_unblank(); _machine_restart(command); } void machine_halt(void) { + console_unblank(); _machine_halt(); } void machine_power_off(void) { + console_unblank(); _machine_power_off(); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/smp.c linux.21rc1-ac2/arch/s390/kernel/smp.c --- linux.21rc1/arch/s390/kernel/smp.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/smp.c 2003-04-25 13:47:51.000000000 +0100 @@ -38,7 +38,7 @@ #include /* prototypes */ -extern int cpu_idle(void * unused); +extern int cpu_idle(void); extern __u16 boot_cpu_addr; extern volatile int __cpu_logical_map[]; @@ -56,6 +56,7 @@ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; unsigned long cpu_online_map; +unsigned long cache_decay_ticks; /* * Setup routine for controlling SMP activation @@ -468,7 +469,7 @@ { int curr_cpu; - current->processor = 0; + current->cpu = 0; smp_num_cpus = 1; cpu_online_map = 1; for (curr_cpu = 0; @@ -509,7 +510,7 @@ pfault_init(); #endif /* cpu_idle will call schedule for us */ - return cpu_idle(NULL); + return cpu_idle(); } /* @@ -547,12 +548,9 @@ idle = init_task.prev_task; if (!idle) panic("No idle process for CPU %d",cpu); - idle->processor = cpu; - idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ + init_idle(idle, cpu); - del_from_runqueue(idle); unhash_process(idle); - init_tasks[cpu] = idle; cpu_lowcore = get_cpu_lowcore(cpu); cpu_lowcore->save_area[15] = idle->thread.ksp; @@ -604,6 +602,8 @@ panic("Couldn't request external interrupt 0x1202"); smp_count_cpus(); memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + + cache_decay_ticks = (200 * HZ) / 1000; /* Is 200ms ok? Robus? XXX */ /* * Initialize the logical to physical CPU number mapping diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390/kernel/traps.c linux.21rc1-ac2/arch/s390/kernel/traps.c --- linux.21rc1/arch/s390/kernel/traps.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390/kernel/traps.c 2003-04-25 13:47:51.000000000 +0100 @@ -142,7 +142,7 @@ * We can't print the backtrace of a running process. It is * unreliable at best and can cause kernel oopses. */ - if (task_has_cpu(tsk)) + if (tsk->state == TASK_RUNNING) return; show_trace((unsigned long *) tsk->thread.ksp); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/defconfig linux.21rc1-ac2/arch/s390x/defconfig --- linux.21rc1/arch/s390x/defconfig 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/defconfig 2003-04-25 13:53:58.000000000 +0100 @@ -91,22 +91,23 @@ CONFIG_TN3270_CONSOLE=y CONFIG_TN3215=y CONFIG_TN3215_CONSOLE=y -CONFIG_HWC=y -CONFIG_HWC_CONSOLE=y -CONFIG_HWC_CPI=m +CONFIG_SCLP=y +CONFIG_SCLP_TTY=y +CONFIG_SCLP_CONSOLE=y +CONFIG_SCLP_VT220_TTY=y +CONFIG_SCLP_VT220_CONSOLE=y +CONFIG_SCLP_CPI=m CONFIG_S390_TAPE=m # # S/390 tape interface support # -CONFIG_S390_TAPE_CHAR=y CONFIG_S390_TAPE_BLOCK=y # # S/390 tape hardware support # -CONFIG_S390_TAPE_3490=m -CONFIG_S390_TAPE_3480=m +CONFIG_S390_TAPE_34XX=m # # Network device drivers @@ -151,13 +152,18 @@ CONFIG_SHARED_IPV6_CARDS=y # CONFIG_KHTTPD is not set # CONFIG_ATM is not set -CONFIG_VLAN_8021Q=m +# CONFIG_VLAN_8021Q is not set # # # # CONFIG_IPX is not set # CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set # CONFIG_DECNET is not set # CONFIG_BRIDGE is not set # CONFIG_X25 is not set @@ -175,6 +181,11 @@ # CONFIG_NET_SCHED is not set # +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# # File systems # # CONFIG_QUOTA is not set @@ -187,6 +198,8 @@ # CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set # CONFIG_BFS_FS is not set # CONFIG_EXT3_FS is not set # CONFIG_JBD is not set @@ -200,23 +213,27 @@ # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set # CONFIG_TMPFS is not set -# CONFIG_RAMFS is not set +CONFIG_RAMFS=y # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y -CONFIG_DEVFS_FS=y -CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set # CONFIG_DEVFS_DEBUG is not set # CONFIG_DEVPTS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set +# CONFIG_XIP2FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UDF_FS is not set @@ -234,6 +251,7 @@ # CONFIG_ROOT_NFS is not set # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -247,7 +265,6 @@ # CONFIG_NCPFS_NLS is not set # CONFIG_NCPFS_EXTRAS is not set # CONFIG_ZISOFS_FS is not set -# CONFIG_ZLIB_FS_INFLATE is not set # # Partition Types @@ -264,6 +281,7 @@ # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set # CONFIG_SMB_NLS is not set # CONFIG_NLS is not set @@ -271,3 +289,9 @@ # Kernel hacking # CONFIG_MAGIC_SYSRQ=y + +# +# Library routines +# +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/asm-offsets.c linux.21rc1-ac2/arch/s390x/kernel/asm-offsets.c --- linux.21rc1/arch/s390x/kernel/asm-offsets.c 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/asm-offsets.c 2003-04-25 13:47:51.000000000 +0100 @@ -26,7 +26,7 @@ DEFINE(__TASK_need_resched, offsetof(struct task_struct, need_resched),); DEFINE(__TASK_ptrace, offsetof(struct task_struct, ptrace),); - DEFINE(__TASK_processor, offsetof(struct task_struct, processor),); + DEFINE(__TASK_processor, offsetof(struct task_struct, cpu),); return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/binfmt_elf32.c linux.21rc1-ac2/arch/s390x/kernel/binfmt_elf32.c --- linux.21rc1/arch/s390x/kernel/binfmt_elf32.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/binfmt_elf32.c 2003-04-25 13:50:03.000000000 +0100 @@ -87,7 +87,13 @@ #define ELF_PLATFORM (NULL) #ifdef __KERNEL__ -#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) +#define SET_PERSONALITY(ex, ibcs2) \ +do { \ + if (ibcs2) \ + set_personality(PER_SVR4); \ + else if (current->personality != PER_LINUX32) \ + set_personality(PER_LINUX); \ +} while (0) #endif #include "linux32.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/bitmap.S linux.21rc1-ac2/arch/s390x/kernel/bitmap.S --- linux.21rc1/arch/s390x/kernel/bitmap.S 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/bitmap.S 2003-04-25 13:47:51.000000000 +0100 @@ -35,3 +35,22 @@ .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + .globl _sb_findmap +_sb_findmap: + .byte 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/entry.S linux.21rc1-ac2/arch/s390x/kernel/entry.S --- linux.21rc1/arch/s390x/kernel/entry.S 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/entry.S 2003-04-25 13:50:03.000000000 +0100 @@ -240,11 +240,11 @@ # .globl ret_from_fork ret_from_fork: + brasl %r14,schedule_tail GET_CURRENT # load pointer to task_struct to R9 stosm 48(%r15),0x03 # reenable interrupts xc SP_R2(8,%r15),SP_R2(%r15) # child returns 0 - larl %r14,sysc_return - jg schedule_tail # return to sysc_return + j sysc_return # # clone, fork, vfork, exec and sigreturn need glue, @@ -392,7 +392,7 @@ .long SYSCALL(sys_oldumount,sys32_oldumount_wrapper) .long SYSCALL(sys_ni_syscall,sys32_setuid16_wrapper) /* old setuid16 syscall*/ .long SYSCALL(sys_ni_syscall,sys32_getuid16) /* old getuid16 syscall*/ - .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 25 old stime syscall */ + .long SYSCALL(sys_ni_syscall,sys32_stime_wrapper) /* 25 old stime syscall */ .long SYSCALL(sys_ptrace,sys32_ptrace_wrapper) .long SYSCALL(sys_alarm,sys32_alarm_wrapper) .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old fstat syscall */ @@ -489,7 +489,7 @@ .long SYSCALL(sys_sigreturn_glue,sys32_sigreturn_glue) .long SYSCALL(sys_clone_glue,sys_clone_glue) /* 120 */ .long SYSCALL(sys_setdomainname,sys32_setdomainname_wrapper) - .long SYSCALL(sys_newuname,sys32_newuname_wrapper) + .long SYSCALL(s390x_newuname,sys32_newuname_wrapper) .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* modify_ldt for i386 */ .long SYSCALL(sys_adjtimex,sys32_adjtimex_wrapper) .long SYSCALL(sys_mprotect,sys32_mprotect_wrapper) /* 125 */ @@ -503,7 +503,7 @@ .long SYSCALL(sys_fchdir,sys32_fchdir_wrapper) .long SYSCALL(sys_bdflush,sys32_bdflush_wrapper) .long SYSCALL(sys_sysfs,sys32_sysfs_wrapper) /* 135 */ - .long SYSCALL(sys_personality,sys32_personality_wrapper) + .long SYSCALL(s390x_personality,sys32_personality_wrapper) .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* for afs_syscall */ .long SYSCALL(sys_ni_syscall,sys32_setfsuid16_wrapper) /* old setfsuid16 syscall */ .long SYSCALL(sys_ni_syscall,sys32_setfsgid16_wrapper) /* old setfsgid16 syscall */ @@ -516,7 +516,7 @@ .long SYSCALL(sys_writev,sys32_writev_wrapper) .long SYSCALL(sys_getsid,sys32_getsid_wrapper) .long SYSCALL(sys_fdatasync,sys32_fdatasync_wrapper) - .long SYSCALL(sys_sysctl,sys_ni_syscall) + .long SYSCALL(sys_sysctl,sys32_sysctl_wrapper) .long SYSCALL(sys_mlock,sys32_mlock_wrapper) /* 150 */ .long SYSCALL(sys_munlock,sys32_munlock_wrapper) .long SYSCALL(sys_mlockall,sys32_mlockall_wrapper) @@ -558,7 +558,7 @@ .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* streams1 */ .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* streams2 */ .long SYSCALL(sys_vfork_glue,sys_vfork_glue) /* 190 */ - .long SYSCALL(sys_getrlimit,sys32_old_getrlimit_wrapper) + .long SYSCALL(sys_getrlimit,sys32_getrlimit_wrapper) .long SYSCALL(sys_mmap2,sys32_mmap2_wrapper) .long SYSCALL(sys_ni_syscall,sys32_truncate64_wrapper) .long SYSCALL(sys_ni_syscall,sys32_ftruncate64_wrapper) @@ -589,7 +589,7 @@ .long SYSCALL(sys_madvise,sys32_madvise_wrapper) .long SYSCALL(sys_getdents64,sys32_getdents64_wrapper)/* 220 */ .long SYSCALL(sys_ni_syscall,sys32_fcntl64_wrapper) - .long SYSCALL(sys_readahead,sys_ni_syscall) + .long SYSCALL(sys_readahead,sys32_readahead) .long SYSCALL(sys_ni_syscall,sys_ni_syscall) .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 224 - reserved for setxattr */ .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 225 - reserved for lsetxattr */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/exec_domain32.c linux.21rc1-ac2/arch/s390x/kernel/exec_domain32.c --- linux.21rc1/arch/s390x/kernel/exec_domain32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/exec_domain32.c 2003-04-25 13:50:03.000000000 +0100 @@ -0,0 +1,30 @@ +/* + * Support for 32-bit Linux for S390 personality. + * + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * + * + */ + +#include +#include +#include +#include + +struct exec_domain s390_exec_domain; + +static int __init +s390_init (void) +{ + s390_exec_domain.name = "Linux/s390"; + s390_exec_domain.handler = NULL; + s390_exec_domain.pers_low = PER_LINUX32; + s390_exec_domain.pers_high = PER_LINUX32; + s390_exec_domain.signal_map = default_exec_domain.signal_map; + s390_exec_domain.signal_invmap = default_exec_domain.signal_invmap; + register_exec_domain(&s390_exec_domain); + return 0; +} + +__initcall(s390_init); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/ioctl32.c linux.21rc1-ac2/arch/s390x/kernel/ioctl32.c --- linux.21rc1/arch/s390x/kernel/ioctl32.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/ioctl32.c 2003-04-25 13:53:58.000000000 +0100 @@ -25,9 +25,15 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include +#include #include #include "linux32.h" @@ -373,6 +379,107 @@ return sys_ioctl(fd, cmd, arg); } +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + + static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) { mm_segment_t old_fs = get_fs(); @@ -413,7 +520,21 @@ IOCTL32_DEFAULT(BIODASDINFO), IOCTL32_DEFAULT(BIODASDFMT), + IOCTL32_DEFAULT(TAPE390_DISPLAY), + + IOCTL32_DEFAULT(BLKROSET), + IOCTL32_DEFAULT(BLKROGET), IOCTL32_DEFAULT(BLKRRPART), + IOCTL32_DEFAULT(BLKFLSBUF), + IOCTL32_DEFAULT(BLKRASET), + IOCTL32_DEFAULT(BLKFRASET), + IOCTL32_DEFAULT(BLKSECTSET), + IOCTL32_DEFAULT(BLKSSZGET), + IOCTL32_DEFAULT(BLKBSZGET), + IOCTL32_DEFAULT(BLKGETSIZE64), + + IOCTL32_DEFAULT(BLKELVGET), + IOCTL32_DEFAULT(BLKELVSET), IOCTL32_HANDLER(HDIO_GETGEO, hd_geometry_ioctl), @@ -422,6 +543,7 @@ IOCTL32_DEFAULT(TCSETAW), IOCTL32_DEFAULT(TCSETAF), IOCTL32_DEFAULT(TCSBRK), + IOCTL32_DEFAULT(TCSBRKP), IOCTL32_DEFAULT(TCXONC), IOCTL32_DEFAULT(TCFLSH), IOCTL32_DEFAULT(TCGETS), @@ -508,6 +630,10 @@ IOCTL32_DEFAULT(SIOCGSTAMP), + IOCTL32_DEFAULT(LOOP_SET_FD), + IOCTL32_DEFAULT(LOOP_CLR_FD), + + IOCTL32_DEFAULT(SIOCATMARK), IOCTL32_HANDLER(SIOCGIFNAME, dev_ifname32), IOCTL32_HANDLER(SIOCGIFCONF, dev_ifconf), IOCTL32_HANDLER(SIOCGIFFLAGS, dev_ifsioc), @@ -552,7 +678,18 @@ IOCTL32_HANDLER(EXT2_IOC32_GETVERSION, do_ext2_ioctl), IOCTL32_HANDLER(EXT2_IOC32_SETVERSION, do_ext2_ioctl), - IOCTL32_HANDLER(BLKGETSIZE, w_long) + IOCTL32_HANDLER(LOOP_SET_STATUS, loop_status), + IOCTL32_HANDLER(LOOP_GET_STATUS, loop_status), + +/* Raw devices */ + IOCTL32_DEFAULT(RAW_SETBIND), + IOCTL32_DEFAULT(RAW_GETBIND), + + IOCTL32_HANDLER(BLKRAGET, w_long), + IOCTL32_HANDLER(BLKGETSIZE, w_long), + IOCTL32_HANDLER(BLKFRAGET, w_long), + IOCTL32_HANDLER(BLKSECTGET, w_long), + IOCTL32_HANDLER(BLKPG, blkpg_ioctl_trans) }; @@ -604,6 +741,8 @@ static void ioctl32_insert(struct ioctl32_list *entry) { int hash = ioctl32_hash(entry->handler.cmd); + + entry->next = 0; if (!ioctl32_hash_table[hash]) ioctl32_hash_table[hash] = entry; else { @@ -612,10 +751,51 @@ while (l->next) l = l->next; l->next = entry; - entry->next = 0; } } +int register_ioctl32_conversion(unsigned int cmd, + int (*handler)(unsigned int, unsigned int, + unsigned long, struct file *)) +{ + struct ioctl32_list *l, *new; + int hash; + + hash = ioctl32_hash(cmd); + for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next) + if (l->handler.cmd == cmd) + return -EBUSY; + new = kmalloc(sizeof(struct ioctl32_list), GFP_KERNEL); + if (new == NULL) + return -ENOMEM; + new->handler.cmd = cmd; + new->handler.function = (void *) handler; + ioctl32_insert(new); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + struct ioctl32_list *p, *l; + int hash; + + hash = ioctl32_hash(cmd); + p = NULL; + for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next) { + if (l->handler.cmd == cmd) + break; + p = l; + } + if (l == NULL) + return -ENOENT; + if (p == NULL) + ioctl32_hash_table[hash] = l->next; + else + p->next = l->next; + kfree(l); + return 0; +} + static int __init init_ioctl32(void) { int i; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/linux32.c linux.21rc1-ac2/arch/s390x/kernel/linux32.c --- linux.21rc1/arch/s390x/kernel/linux32.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/linux32.c 2003-04-25 13:50:03.000000000 +0100 @@ -63,6 +63,7 @@ #include #include +#include #include "linux32.h" @@ -384,18 +385,18 @@ struct shmid64_ds32 { struct ipc64_perm_ds32 shm_perm; - unsigned int __pad1; + __kernel_size_t32 shm_segsz; __kernel_time_t32 shm_atime; - unsigned int __pad2; + unsigned int __unused1; __kernel_time_t32 shm_dtime; - unsigned int __pad3; + unsigned int __unused2; __kernel_time_t32 shm_ctime; - __kernel_size_t32 shm_segsz; + unsigned int __unused3; __kernel_pid_t32 shm_cpid; __kernel_pid_t32 shm_lpid; unsigned int shm_nattch; - unsigned int __unused1; - unsigned int __unused2; + unsigned int __unused4; + unsigned int __unused5; }; @@ -504,16 +505,23 @@ static int do_sys32_msgsnd (int first, int second, int third, void *uptr) { - struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_USER); struct msgbuf32 *up = (struct msgbuf32 *)uptr; mm_segment_t old_fs; int err; if (!p) return -ENOMEM; - err = get_user (p->mtype, &up->mtype); - err |= __copy_from_user (p->mtext, &up->mtext, second); - if (err) + + err = -EINVAL; + if (second > MSGMAX || first < 0 || second < 0) + goto out; + + err = -EFAULT; + if (!uptr) + goto out; + if (get_user (p->mtype, &up->mtype) || + __copy_from_user (p->mtext, &up->mtext, second)) goto out; old_fs = get_fs (); set_fs (KERNEL_DS); @@ -532,6 +540,9 @@ mm_segment_t old_fs; int err; + if (first < 0 || second < 0) + return -EINVAL; + if (!version) { struct ipc_kludge_32 *uipck = (struct ipc_kludge_32 *)uptr; struct ipc_kludge_32 ipck; @@ -546,12 +557,12 @@ msgtyp = ipck.msgtyp; } err = -ENOMEM; - p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + p = kmalloc (second + sizeof (struct msgbuf), GFP_USER); if (!p) goto out; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_msgrcv (first, p, second + 4, msgtyp, third); + err = sys_msgrcv (first, p, second, msgtyp, third); set_fs (old_fs); if (err < 0) goto free_then_out; @@ -879,25 +890,37 @@ { switch (cmd) { case F_GETLK: - case F_SETLK: - case F_SETLKW: { struct flock f; mm_segment_t old_fs; long ret; - if(get_flock(&f, (struct flock32 *)arg)) + if(get_flock(&f, (struct flock32 *)A(arg))) return -EFAULT; old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs (old_fs); if (ret) return ret; if (f.l_start >= 0x7fffffffUL || - f.l_len >= 0x7fffffffUL || f.l_start + f.l_len >= 0x7fffffffUL) return -EOVERFLOW; - if(put_flock(&f, (struct flock32 *)arg)) + if(put_flock(&f, (struct flock32 *)A(arg))) + return -EFAULT; + return 0; + } + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)A(arg))) return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if (ret) return ret; return 0; } default: @@ -912,64 +935,97 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; - __u32 dqb_ihardlimit; - __u32 dqb_isoftlimit; - __u32 dqb_curinodes; - __kernel_time_t32 dqb_btime; - __kernel_time_t32 dqb_itime; -}; - extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); -asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) +#ifdef CONFIG_QIFACE_COMPAT +#ifdef CONFIG_QIFACE_V1 +struct user_dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; +typedef struct v1c_mem_dqblk comp_dqblk_t; + +#define Q_COMP_GETQUOTA Q_V1_GETQUOTA +#define Q_COMP_SETQUOTA Q_V1_SETQUOTA +#define Q_COMP_SETQLIM Q_V1_SETQLIM +#define Q_COMP_SETUSE Q_V1_SETUSE +#else +struct user_dqblk32 { + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; +typedef struct v2c_mem_dqblk comp_dqblk_t; + +#define Q_COMP_GETQUOTA Q_V2_GETQUOTA +#define Q_COMP_SETQUOTA Q_V2_SETQUOTA +#define Q_COMP_SETQLIM Q_V2_SETQLIM +#define Q_COMP_SETUSE Q_V2_SETUSE +#endif + +asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + comp_dqblk_t d; mm_segment_t old_fs; char *spec; switch (cmds) { - case Q_GETQUOTA: - break; - case Q_SETQUOTA: - case Q_SETUSE: - case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) - return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; - break; + case Q_COMP_GETQUOTA: + break; + case Q_COMP_SETQUOTA: + case Q_COMP_SETUSE: + case Q_COMP_SETQLIM: + if (copy_from_user(&d, (struct user_dqblk32 *)addr, + sizeof (struct user_dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime; + break; default: - return sys_quotactl(cmd, special, - id, (caddr_t)addr); + return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; - old_fs = get_fs (); + old_fs = get_fs(); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); if (err) return err; - if (cmds == Q_GETQUOTA) { + if (cmds == Q_COMP_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct user_dqblk32 *)&d)->dqb_itime = i; + ((struct user_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct user_dqblk32 *)addr, &d, + sizeof (struct user_dqblk32))) return -EFAULT; } return 0; } +#else +/* No conversion needed for new interface */ +asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) +{ + return sys_quotactl(cmd, special, id, addr); +} +#endif + static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) { int err; @@ -2151,15 +2207,17 @@ return ret; } -#define RLIM_INFINITY32 0x7fffffff -#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) +#define RLIM_OLD_INFINITY32 0x7fffffff +#define RLIM_INFINITY32 0xffffffff +#define RESOURCE32_OLD(x) ((x > RLIM_OLD_INFINITY32) ? RLIM_OLD_INFINITY32 : x) +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) struct rlimit32 { u32 rlim_cur; u32 rlim_max; }; -extern asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim); +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); asmlinkage int sys32_old_getrlimit(unsigned int resource, struct rlimit32 *rlim) { @@ -2168,7 +2226,23 @@ mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); - ret = sys_old_getrlimit(resource, &r); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + ret = put_user (RESOURCE32_OLD(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user (RESOURCE32_OLD(r.rlim_max), &rlim->rlim_max); + } + return ret; +} + +asmlinkage int sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); set_fs (old_fs); if (!ret) { ret = put_user (RESOURCE32(r.rlim_cur), &rlim->rlim_cur); @@ -2588,8 +2662,23 @@ CMSG32_ALIGN(sizeof(struct cmsghdr32))); kcmsg32->cmsg_len = clen32; + switch (kcmsg32->cmsg_type) { + /* + * The timestamp type's data needs to be converted + * from 64-bit time values to 32-bit time values + */ + case SO_TIMESTAMP: { + __kernel_time_t32* ptr_time32 = CMSG32_DATA(kcmsg32); + __kernel_time_t* ptr_time = CMSG_DATA(ucmsg); + get_user(*ptr_time32, ptr_time); + get_user(*(ptr_time32+1), ptr_time+1); + kcmsg32->cmsg_len -= 2*(sizeof(__kernel_time_t) - + sizeof(__kernel_time_t32)); + } + default:; + } ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); - wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(kcmsg32->cmsg_len)); } /* Copy back fixed up data, and adjust pointers. */ @@ -2612,6 +2701,7 @@ kmsg->msg_control = (void *) orig_cmsg_uptr; } +#if 0 asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) { struct socket *sock; @@ -2721,7 +2811,7 @@ sockfd_put(sock); } - if(uaddr != NULL && err >= 0) + if(uaddr != NULL && err >= 0 && kern_msg.msg_namelen) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); if(cmsg_ptr != 0 && err >= 0) { unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); @@ -2737,6 +2827,222 @@ return err; return len; } +#endif + +/* + * BSD sendmsg interface + */ + +int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */ + unsigned char *ctl_buf = ctl; + struct msghdr msg_sys; + int err, ctl_len, iov_size, total_len; + + err = -EFAULT; + if (msghdr_from_user32_to_kern(&msg_sys, msg)) + goto out; + + sock = sockfd_lookup(fd, &err); + if (!sock) + goto out; + + /* do not move before msg_sys is valid */ + err = -EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) + goto out_put; + + /* Check whether to allocate the iovec area*/ + err = -ENOMEM; + iov_size = msg_sys.msg_iovlen * sizeof(struct iovec32); + if (msg_sys.msg_iovlen > UIO_FASTIOV) { + iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); + if (!iov) + goto out_put; + } + + /* This will also move the address data into kernel space */ + err = verify_iovec32(&msg_sys, iov, address, VERIFY_READ); + if (err < 0) + goto out_freeiov; + total_len = err; + + err = -ENOBUFS; + + if (msg_sys.msg_controllen > INT_MAX) + goto out_freeiov; + ctl_len = msg_sys.msg_controllen; + if (ctl_len) + { + if (ctl_len > sizeof(ctl)) + { + ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); + if (ctl_buf == NULL) + goto out_freeiov; + } + else if (ctl_len < sizeof(struct cmsghdr)) + { + /* to get same error message as on 31 bit native */ + err = EOPNOTSUPP; + goto out_freeiov; + } + err = -EFAULT; + if (cmsghdr_from_user32_to_kern(&msg_sys, ctl_buf, ctl_len)) + goto out_freectl; +// msg_sys.msg_control = ctl_buf; + } + msg_sys.msg_flags = flags; + + if (sock->file->f_flags & O_NONBLOCK) + msg_sys.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &msg_sys, total_len); + +out_freectl: + if (ctl_buf != ctl) + sock_kfree_s(sock->sk, ctl_buf, ctl_len); +out_freeiov: + if (iov != iovstack) + sock_kfree_s(sock->sk, iov, iov_size); +out_put: + sockfd_put(sock); +out: + return err; +} + +static __inline__ void +scm_recv32(struct socket *sock, struct msghdr *msg, + struct scm_cookie *scm, int flags, unsigned long cmsg_ptr) +{ + if(!msg->msg_control) + { + if(sock->passcred || scm->fp) + msg->msg_flags |= MSG_CTRUNC; + scm_destroy(scm); + return; + } + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) msg->msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(msg, cmsg_ptr); + /* Wheee... */ + if(sock->passcred) + put_cmsg32(msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm->creds), &scm->creds); + if(!scm->fp) + return; + + scm_detach_fds32(msg, scm); +} + +static int +sock_recvmsg32(struct socket *sock, struct msghdr *msg, int size, int flags, + unsigned long cmsg_ptr) +{ + struct scm_cookie scm; + + memset(&scm, 0, sizeof(scm)); + size = sock->ops->recvmsg(sock, msg, size, flags, &scm); + if (size >= 0) + scm_recv32(sock, msg, &scm, flags, cmsg_ptr); + + return size; +} + +/* + * BSD recvmsg interface + */ + +int +sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags) +{ + struct socket *sock; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack; + struct msghdr msg_sys; + unsigned long cmsg_ptr; + int err, iov_size, total_len, len; + + /* kernel mode address */ + char addr[MAX_SOCK_ADDR]; + + /* user mode address pointers */ + struct sockaddr *uaddr; + int *uaddr_len; + + err=-EFAULT; + if (msghdr_from_user32_to_kern(&msg_sys, msg)) + goto out; + + sock = sockfd_lookup(fd, &err); + if (!sock) + goto out; + + err = -EINVAL; + if (msg_sys.msg_iovlen > UIO_MAXIOV) + goto out_put; + + /* Check whether to allocate the iovec area*/ + err = -ENOMEM; + iov_size = msg_sys.msg_iovlen * sizeof(struct iovec); + if (msg_sys.msg_iovlen > UIO_FASTIOV) { + iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL); + if (!iov) + goto out_put; + } + + /* + * Save the user-mode address (verify_iovec will change the + * kernel msghdr to use the kernel address space) + */ + + uaddr = msg_sys.msg_name; + uaddr_len = &msg->msg_namelen; + err = verify_iovec32(&msg_sys, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out_freeiov; + total_len=err; + + cmsg_ptr = (unsigned long)msg_sys.msg_control; + msg_sys.msg_flags = 0; + + if (sock->file->f_flags & O_NONBLOCK) + flags |= MSG_DONTWAIT; + err = sock_recvmsg32(sock, &msg_sys, total_len, flags, cmsg_ptr); + if (err < 0) + goto out_freeiov; + len = err; + + if (uaddr != NULL && + /* in order to get same error message as on native 31 bit */ + msg_sys.msg_namelen > 0) { + err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len); + if (err < 0) + goto out_freeiov; + } + err = __put_user(msg_sys.msg_flags, &msg->msg_flags); + if (err) + goto out_freeiov; + err = __put_user((__kernel_size_t32) ((unsigned long)msg_sys.msg_control - cmsg_ptr), &msg->msg_controllen); + if (err) + goto out_freeiov; + err = len; + +out_freeiov: + if (iov != iovstack) + sock_kfree_s(sock->sk, iov, iov_size); +out_put: + sockfd_put(sock); +out: + return err; +} extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); @@ -2820,6 +3126,20 @@ if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER) return do_set_icmpv6_filter(fd, level, optname, optval, optlen); + if (level == SOL_SOCKET && + (optname == SO_SNDTIMEO || optname == SO_RCVTIMEO)) { + long ret; + struct timeval tmp; + mm_segment_t old_fs; + + if (get_tv32(&tmp, (struct timeval32 *)optval )) + return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, (char *) &tmp, sizeof(struct timeval)); + set_fs(old_fs); + return ret; + } return sys_setsockopt(fd, level, optname, optval, optlen); } @@ -3669,7 +3989,7 @@ */ static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) { - return copy_to_user(res32, kres, sizeof(*res32)); + return copy_to_user(res32, kres, sizeof(*res32)) ? -EFAULT : 0; } /* @@ -3891,15 +4211,25 @@ asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count, u32 poshi, u32 poslo) { + if ((ssize_t32) count < 0) + return -EINVAL; return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count, u32 poshi, u32 poslo) { + if ((ssize_t32) count < 0) + return -EINVAL; return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } +extern asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count); + +asmlinkage ssize_t32 sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) +{ + return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); +} extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count); @@ -4203,7 +4533,7 @@ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) { /* Result is out of bounds. */ - do_munmap(current->mm, addr, len); + do_munmap(current->mm, addr, len, 1); error = -ENOMEM; } up_write(¤t->mm->mmap_sem); @@ -4344,3 +4674,22 @@ return ret; } +asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count); + +asmlinkage ssize_t32 sys32_read(unsigned int fd, char * buf, size_t count) +{ + if ((ssize_t32) count < 0) + return -EINVAL; + + return sys_read(fd, buf, count); +} + +asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count); + +asmlinkage ssize_t32 sys32_write(unsigned int fd, char * buf, size_t count) +{ + if ((ssize_t32) count < 0) + return -EINVAL; + + return sys_write(fd, buf, count); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/Makefile linux.21rc1-ac2/arch/s390x/kernel/Makefile --- linux.21rc1/arch/s390x/kernel/Makefile 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/Makefile 2003-04-25 13:50:03.000000000 +0100 @@ -29,7 +29,7 @@ # obj-$(CONFIG_REMOTE_DEBUG) += gdb-stub.o #gdb-low.o -obj-$(CONFIG_S390_SUPPORT) += linux32.o signal32.o ioctl32.o wrapper32.o exec32.o +obj-$(CONFIG_S390_SUPPORT) += linux32.o signal32.o ioctl32.o wrapper32.o exec32.o exec_domain32.o obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o .PHONY: asm-offsets.h diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/process.c linux.21rc1-ac2/arch/s390x/kernel/process.c --- linux.21rc1/arch/s390x/kernel/process.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/process.c 2003-04-25 13:47:51.000000000 +0100 @@ -55,10 +55,6 @@ psw_t wait_psw; unsigned long reg; - /* endless idle loop with no priority at all */ - init_idle(); - current->nice = 20; - current->counter = -100; while (1) { if (current->need_resched) { schedule(); @@ -91,7 +87,7 @@ { struct task_struct *tsk = current; - printk("CPU: %d %s\n", tsk->processor, print_tainted()); + printk("CPU: %d %s\n", tsk->cpu, print_tainted()); printk("Process %s (pid: %d, task: %016lx, ksp: %016lx)\n", current->comm, current->pid, (unsigned long) tsk, tsk->thread.ksp); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/s390_ksyms.c linux.21rc1-ac2/arch/s390x/kernel/s390_ksyms.c --- linux.21rc1/arch/s390x/kernel/s390_ksyms.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/s390_ksyms.c 2003-04-25 13:50:03.000000000 +0100 @@ -62,6 +62,20 @@ EXPORT_SYMBOL(overflowuid); EXPORT_SYMBOL(overflowgid); +#ifdef CONFIG_S390_SUPPORT +/* + * Dynamically add/remove 31 bit ioctl conversion functions. + */ +extern int register_ioctl32_conversion(unsigned int cmd, + int (*handler)(unsigned int, + unsigned int, + unsigned long, + struct file *)); +int unregister_ioctl32_conversion(unsigned int cmd); +EXPORT_SYMBOL(register_ioctl32_conversion); +EXPORT_SYMBOL(unregister_ioctl32_conversion); +#endif + /* * misc. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/setup.c linux.21rc1-ac2/arch/s390x/kernel/setup.c --- linux.21rc1/arch/s390x/kernel/setup.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/setup.c 2003-04-25 13:53:30.000000000 +0100 @@ -166,9 +166,9 @@ static int __init conmode_setup(char *str) { -#if defined(CONFIG_HWC_CONSOLE) - if (strncmp(str, "hwc", 4) == 0) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0) + SET_CONSOLE_SCLP; #endif #if defined(CONFIG_TN3215_CONSOLE) if (strncmp(str, "3215", 5) == 0) @@ -200,8 +200,8 @@ */ cpcmd("TERM CONMODE 3215", NULL, 0); if (ptr == NULL) { -#if defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif return; } @@ -210,16 +210,16 @@ SET_CONSOLE_3270; #elif defined(CONFIG_TN3215_CONSOLE) SET_CONSOLE_3215; -#elif defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } else if (strncmp(ptr + 8, "3215", 4) == 0) { #if defined(CONFIG_TN3215_CONSOLE) SET_CONSOLE_3215; #elif defined(CONFIG_TN3270_CONSOLE) SET_CONSOLE_3270; -#elif defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } } else if (MACHINE_IS_P390) { @@ -229,8 +229,8 @@ SET_CONSOLE_3270; #endif } else { -#if defined(CONFIG_HWC_CONSOLE) - SET_CONSOLE_HWC; +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; #endif } } @@ -273,21 +273,25 @@ /* * Reboot, halt and power_off stubs. They just call _machine_restart, - * _machine_halt or _machine_power_off. + * _machine_halt or _machine_power_off after making sure that all pending + * printks reached their destination. */ void machine_restart(char *command) { + console_unblank(); _machine_restart(command); } void machine_halt(void) { + console_unblank(); _machine_halt(); } void machine_power_off(void) { + console_unblank(); _machine_power_off(); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/signal32.c linux.21rc1-ac2/arch/s390x/kernel/signal32.c --- linux.21rc1/arch/s390x/kernel/signal32.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/signal32.c 2003-04-25 13:50:03.000000000 +0100 @@ -302,7 +302,7 @@ save_fp_regs(&fpregs); __put_user(fpregs.fpc, &sregs->fpregs.fpc); for(i=0; ifpregs.fprs[i].d); + err |= __put_user(fpregs.fprs[i].ui, &sregs->fpregs.fprs[i].d); } return(err); @@ -331,7 +331,7 @@ (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); __get_user(fpregs.fpc, &sregs->fpregs.fpc); for(i=0; ifpregs.fprs[i].d); + err |= __get_user(fpregs.fprs[i].ui, &sregs->fpregs.fprs[i].d); if(!err) restore_fp_regs(&fpregs); } @@ -474,6 +474,10 @@ goto give_sigsegv; } + /* Set up backchain. */ + if (__put_user((unsigned int) regs->gprs[15], (unsigned int *) frame)) + goto give_sigsegv; + /* Set up registers for signal handler */ regs->gprs[15] = (addr_t)frame; regs->psw.addr = FIX_PSW(ka->sa.sa_handler); @@ -527,6 +531,10 @@ (u16 *)(frame->retcode)); } + /* Set up backchain. */ + if (__put_user((unsigned int) regs->gprs[15], (unsigned int *) frame)) + goto give_sigsegv; + /* Set up registers for signal handler */ regs->gprs[15] = (addr_t)frame; regs->psw.addr = FIX_PSW(ka->sa.sa_handler); @@ -675,7 +683,7 @@ continue; switch (signr) { - case SIGCONT: case SIGCHLD: case SIGWINCH: + case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG: continue; case SIGTSTP: case SIGTTIN: case SIGTTOU: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/signal.c linux.21rc1-ac2/arch/s390x/kernel/signal.c --- linux.21rc1/arch/s390x/kernel/signal.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/signal.c 2003-04-25 13:47:51.000000000 +0100 @@ -575,10 +575,7 @@ /* FALLTHRU */ default: - sigaddset(¤t->pending.signal, signr); - recalc_sigpending(current); - current->flags |= PF_SIGNALED; - do_exit(exit_code); + sig_exit(signr, exit_code, &info); /* NOTREACHED */ } } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/smp.c linux.21rc1-ac2/arch/s390x/kernel/smp.c --- linux.21rc1/arch/s390x/kernel/smp.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/smp.c 2003-04-25 13:47:51.000000000 +0100 @@ -38,7 +38,7 @@ #include /* prototypes */ -extern int cpu_idle(void * unused); +extern int cpu_idle(void); extern __u16 boot_cpu_addr; extern volatile int __cpu_logical_map[]; @@ -56,6 +56,7 @@ spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; unsigned long cpu_online_map; +unsigned long cache_decay_ticks; /* * Setup routine for controlling SMP activation @@ -451,7 +452,7 @@ { int curr_cpu; - current->processor = 0; + current->cpu = 0; smp_num_cpus = 1; cpu_online_map = 1; for (curr_cpu = 0; @@ -491,7 +492,7 @@ pfault_init(); #endif /* cpu_idle will call schedule for us */ - return cpu_idle(NULL); + return cpu_idle(); } /* @@ -529,12 +530,9 @@ idle = init_task.prev_task; if (!idle) panic("No idle process for CPU %d",cpu); - idle->processor = cpu; - idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */ + init_idle(idle, cpu); - del_from_runqueue(idle); unhash_process(idle); - init_tasks[cpu] = idle; cpu_lowcore = get_cpu_lowcore(cpu); cpu_lowcore->save_area[15] = idle->thread.ksp; @@ -588,6 +586,8 @@ smp_count_cpus(); memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + cache_decay_ticks = (200 * HZ) / 1000; /* Is 200ms ok? Robus? XXX */ + /* * Initialize the logical to physical CPU number mapping */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/traps.c linux.21rc1-ac2/arch/s390x/kernel/traps.c --- linux.21rc1/arch/s390x/kernel/traps.c 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/traps.c 2003-04-25 13:47:51.000000000 +0100 @@ -144,7 +144,7 @@ * We can't print the backtrace of a running process. It is * unreliable at best and can cause kernel oopses. */ - if (task_has_cpu(tsk)) + if (tsk->state == TASK_RUNNING) return; show_trace((unsigned long *) tsk->thread.ksp); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/s390x/kernel/wrapper32.S linux.21rc1-ac2/arch/s390x/kernel/wrapper32.S --- linux.21rc1/arch/s390x/kernel/wrapper32.S 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/arch/s390x/kernel/wrapper32.S 2003-04-25 13:50:03.000000000 +0100 @@ -17,14 +17,14 @@ llgfr %r2,%r2 # unsigned int llgtr %r3,%r3 # char * llgfr %r4,%r4 # size_t - jg sys_read # branch to sys_read + jg sys32_read # branch to sys_read .globl sys32_write_wrapper sys32_write_wrapper: llgfr %r2,%r2 # unsigned int llgtr %r3,%r3 # const char * llgfr %r4,%r4 # size_t - jg sys_write # branch to system call + jg sys32_write # branch to system call .globl sys32_open_wrapper sys32_open_wrapper: @@ -311,6 +311,12 @@ llgtr %r3,%r3 # struct rlimit_emu31 * jg sys32_old_getrlimit # branch to system call + .globl sys32_getrlimit_wrapper +sys32_getrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg sys32_getrlimit # branch to system call + .globl sys32_mmap2_wrapper sys32_mmap2_wrapper: llgtr %r2,%r2 # struct mmap_arg_struct_emu31 * @@ -537,7 +543,7 @@ .globl sys32_newuname_wrapper sys32_newuname_wrapper: llgtr %r2,%r2 # struct new_utsname * - jg sys_newuname # branch to system call + jg s390x_newuname # branch to system call .globl sys32_adjtimex_wrapper sys32_adjtimex_wrapper: @@ -614,7 +620,7 @@ .globl sys32_personality_wrapper sys32_personality_wrapper: llgfr %r2,%r2 # unsigned long - jg sys_personality # branch to system call + jg s390x_personality # branch to system call .globl sys32_setfsuid16_wrapper sys32_setfsuid16_wrapper: @@ -1091,3 +1097,13 @@ llgtr %r3,%r3 # struct stat64 * llgfr %r4,%r4 # long jg sys32_fstat64 # branch to system call + + .globl sys32_stime_wrapper +sys32_stime_wrapper: + llgtr %r2,%r2 # int * + jg sys_stime # branch to system call + + .globl sys32_sysctl_wrapper +sys32_sysctl_wrapper: + llgtr %r2,%r2 # struct __sysctl_args32 * + jg sys32_sysctl diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sh/mm/fault.c linux.21rc1-ac2/arch/sh/mm/fault.c --- linux.21rc1/arch/sh/mm/fault.c 2003-04-22 16:38:58.000000000 +0100 +++ linux.21rc1-ac2/arch/sh/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -72,8 +72,6 @@ return 1; check_stack: - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (expand_stack(vma, start) == 0) goto good_area; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc/kernel/signal.c linux.21rc1-ac2/arch/sparc/kernel/signal.c --- linux.21rc1/arch/sparc/kernel/signal.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc/kernel/signal.c 2003-04-22 18:21:00.000000000 +0100 @@ -1131,6 +1131,8 @@ line = d_path(map->vm_file->f_dentry, map->vm_file->f_vfsmnt, buffer, PAGE_SIZE); + if (IS_ERR(line)) + break; } printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, kdevname(dev), ino); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc/kernel/sunos_ioctl.c linux.21rc1-ac2/arch/sparc/kernel/sunos_ioctl.c --- linux.21rc1/arch/sparc/kernel/sunos_ioctl.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc/kernel/sunos_ioctl.c 2003-04-22 16:44:36.000000000 +0100 @@ -39,8 +39,12 @@ { int ret = -EBADF; - if (fd >= SUNOS_NR_OPEN || !fcheck(fd)) + read_lock(¤t->files->file_lock); + if (fd >= SUNOS_NR_OPEN || !fcheck(fd)) { + read_unlock(¤t->files->file_lock); goto out; + } + read_unlock(¤t->files->file_lock); /* First handle an easy compat. case for tty ldisc. */ if(cmd == TIOCSETD) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc/mm/fault.c linux.21rc1-ac2/arch/sparc/mm/fault.c --- linux.21rc1/arch/sparc/mm/fault.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -249,8 +249,6 @@ goto bad_area; if(vma->vm_start <= address) goto good_area; - if(!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if(expand_stack(vma, address)) goto bad_area; /* @@ -496,8 +494,6 @@ goto bad_area; if(vma->vm_start <= address) goto good_area; - if(!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if(expand_stack(vma, address)) goto bad_area; good_area: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/kernel/signal32.c linux.21rc1-ac2/arch/sparc64/kernel/signal32.c --- linux.21rc1/arch/sparc64/kernel/signal32.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/kernel/signal32.c 2003-04-22 18:21:00.000000000 +0100 @@ -1348,6 +1348,8 @@ line = d_path(map->vm_file->f_dentry, map->vm_file->f_vfsmnt, buffer, PAGE_SIZE); + if (IS_ERR(line)) + break; } printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, kdevname(dev), ino); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/kernel/signal.c linux.21rc1-ac2/arch/sparc64/kernel/signal.c --- linux.21rc1/arch/sparc64/kernel/signal.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/kernel/signal.c 2003-04-22 18:21:00.000000000 +0100 @@ -679,6 +679,8 @@ line = d_path(map->vm_file->f_dentry, map->vm_file->f_vfsmnt, buffer, PAGE_SIZE); + if (IS_ERR(line)) + break; } printk(MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, kdevname(dev), ino); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/kernel/sunos_ioctl32.c linux.21rc1-ac2/arch/sparc64/kernel/sunos_ioctl32.c --- linux.21rc1/arch/sparc64/kernel/sunos_ioctl32.c 2003-04-22 16:38:57.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/kernel/sunos_ioctl32.c 2003-04-22 16:44:36.000000000 +0100 @@ -100,8 +100,12 @@ if(fd >= SUNOS_NR_OPEN) goto out; - if(!fcheck(fd)) + read_lock(¤t->files->file_lock); + if(!fcheck(fd)) { + read_unlock(¤t->files->file_lock); goto out; + } + read_unlock(¤t->files->file_lock); if(cmd == TIOCSETD) { mm_segment_t old_fs = get_fs(); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/kernel/sys_sparc32.c linux.21rc1-ac2/arch/sparc64/kernel/sys_sparc32.c --- linux.21rc1/arch/sparc64/kernel/sys_sparc32.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/kernel/sys_sparc32.c 2003-04-22 16:44:36.000000000 +0100 @@ -889,62 +889,97 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; - __u32 dqb_ihardlimit; - __u32 dqb_isoftlimit; - __u32 dqb_curinodes; - __kernel_time_t32 dqb_btime; - __kernel_time_t32 dqb_itime; -}; - extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); -asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) +#ifdef CONFIG_QIFACE_COMPAT +#ifdef CONFIG_QIFACE_V1 +struct user_dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; +typedef struct v1c_mem_dqblk comp_dqblk_t; + +#define Q_COMP_GETQUOTA Q_V1_GETQUOTA +#define Q_COMP_SETQUOTA Q_V1_SETQUOTA +#define Q_COMP_SETQLIM Q_V1_SETQLIM +#define Q_COMP_SETUSE Q_V1_SETUSE +#else +struct user_dqblk32 { + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; +typedef struct v2c_mem_dqblk comp_dqblk_t; + +#define Q_COMP_GETQUOTA Q_V2_GETQUOTA +#define Q_COMP_SETQUOTA Q_V2_SETQUOTA +#define Q_COMP_SETQLIM Q_V2_SETQLIM +#define Q_COMP_SETUSE Q_V2_SETUSE +#endif + +asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + comp_dqblk_t d; mm_segment_t old_fs; char *spec; switch (cmds) { - case Q_GETQUOTA: - break; - case Q_SETQUOTA: - case Q_SETUSE: - case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) - return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; - break; + case Q_COMP_GETQUOTA: + break; + case Q_COMP_SETQUOTA: + case Q_COMP_SETUSE: + case Q_COMP_SETQLIM: + if (copy_from_user(&d, (struct user_dqblk32 *)addr, + sizeof (struct user_dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime; + break; default: - return sys_quotactl(cmd, special, - id, (caddr_t)addr); + return sys_quotactl(cmd, special, id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; - old_fs = get_fs (); + old_fs = get_fs(); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); - if (cmds == Q_GETQUOTA) { + if (err) + return err; + if (cmds == Q_COMP_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct user_dqblk32 *)&d)->dqb_itime = i; + ((struct user_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct user_dqblk32 *)addr, &d, + sizeof (struct user_dqblk32))) return -EFAULT; } - return err; + return 0; } +#else +/* No conversion needed for new interface */ +asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr) +{ + return sys_quotactl(cmd, special, id, addr); +} +#endif + static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) { int err; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/mm/fault.c linux.21rc1-ac2/arch/sparc64/mm/fault.c --- linux.21rc1/arch/sparc64/mm/fault.c 2003-04-22 16:38:57.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/mm/fault.c 2003-04-22 16:44:36.000000000 +0100 @@ -373,8 +373,6 @@ if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; if (!(fault_code & FAULT_CODE_WRITE)) { /* Non-faulting loads shouldn't expand stack. */ insn = get_fault_insn(regs, insn); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/sparc64/solaris/timod.c linux.21rc1-ac2/arch/sparc64/solaris/timod.c --- linux.21rc1/arch/sparc64/solaris/timod.c 2003-04-22 16:38:57.000000000 +0100 +++ linux.21rc1-ac2/arch/sparc64/solaris/timod.c 2003-04-22 16:44:36.000000000 +0100 @@ -149,7 +149,9 @@ struct socket *sock; SOLD("wakeing socket"); + read_lock(¤t->files->file_lock); sock = ¤t->files->fd[fd]->f_dentry->d_inode->u.socket_i; + read_unlock(¤t->files->file_lock); wake_up_interruptible(&sock->wait); read_lock(&sock->sk->callback_lock); if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) @@ -163,7 +165,9 @@ struct sol_socket_struct *sock; SOLD("queuing primsg"); + read_lock(¤t->files->file_lock); sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + read_unlock(¤t->files->file_lock); it->next = sock->pfirst; sock->pfirst = it; if (!sock->plast) @@ -177,7 +181,9 @@ struct sol_socket_struct *sock; SOLD("queuing primsg at end"); + read_lock(¤t->files->file_lock); sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + read_unlock(¤t->files->file_lock); it->next = NULL; if (sock->plast) sock->plast->next = it; @@ -355,7 +361,11 @@ (int (*)(int, unsigned long *))SYS(socketcall); int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto); - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); + if (!filp) + return -EBADF; ino = filp->f_dentry->d_inode; sock = (struct sol_socket_struct *)filp->private_data; SOLD("entry"); @@ -636,7 +646,11 @@ SOLD("entry"); SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p)); - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); + if (!filp) + return -EBADF; ino = filp->f_dentry->d_inode; sock = (struct sol_socket_struct *)filp->private_data; SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); @@ -847,7 +861,9 @@ lock_kernel(); if(fd >= NR_OPEN) goto out; - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); if(!filp) goto out; ino = filp->f_dentry->d_inode; @@ -914,7 +930,9 @@ lock_kernel(); if(fd >= NR_OPEN) goto out; - filp = current->files->fd[fd]; + read_lock(¤t->files->file_lock); + filp = fcheck(fd); + read_unlock(¤t->files->file_lock); if(!filp) goto out; ino = filp->f_dentry->d_inode; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/arch/x86_64/ia32/ia32_ioctl.c linux.21rc1-ac2/arch/x86_64/ia32/ia32_ioctl.c --- linux.21rc1/arch/x86_64/ia32/ia32_ioctl.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/arch/x86_64/ia32/ia32_ioctl.c 2003-04-22 18:21:00.000000000 +0100 @@ -4675,7 +4675,7 @@ "cmd(%08x){%s} arg(%08x) on %s\n", current->comm, current->pid, (int)fd, (unsigned int)cmd, buf, (unsigned int)arg, - fn); + IS_ERR(fn) ? "???" : fn); if (path) free_page((unsigned long)path); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/CREDITS linux.21rc1-ac2/CREDITS --- linux.21rc1/CREDITS 2003-04-22 16:39:32.000000000 +0100 +++ linux.21rc1-ac2/CREDITS 2003-04-22 16:44:36.000000000 +0100 @@ -2560,6 +2560,12 @@ S: 7000 Stuttgart 50 S: Germany +N: Andrew Rodland +E: arodland@linuxguru.net +D: That crazy morse code thing. +P: D2B1 5215 B1B9 18E0 B6AD 6ADD 4373 165F 1770 BD5C +S: Pennsylvania, USA + N: Christoph Rohland E: hans-christoph.rohland@sap.com E: ch.rohland@gmx.net diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/00-INDEX linux.21rc1-ac2/Documentation/00-INDEX --- linux.21rc1/Documentation/00-INDEX 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/00-INDEX 2003-04-22 16:44:36.000000000 +0100 @@ -52,6 +52,8 @@ - directory with information on the CD-ROM drivers that Linux has. computone.txt - info on Computone Intelliport II/Plus Multiport Serial Driver +cpufreq + - describes the CPU frequency and voltage scaling support cpqarray.txt - info on using Compaq's SMART2 Intelligent Disk Array Controllers. devices.txt diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/cciss.txt linux.21rc1-ac2/Documentation/cciss.txt --- linux.21rc1/Documentation/cciss.txt 2003-04-22 16:39:32.000000000 +0100 +++ linux.21rc1-ac2/Documentation/cciss.txt 2003-04-25 13:31:16.000000000 +0100 @@ -11,7 +11,8 @@ * SA 5312 * SA 641 * SA 642 - * SA 6400 + * SA 6402 + * SA 6404/256 If nodes are not already created in the /dev/cciss directory diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/Configure.help linux.21rc1-ac2/Documentation/Configure.help --- linux.21rc1/Documentation/Configure.help 2003-04-22 16:39:32.000000000 +0100 +++ linux.21rc1-ac2/Documentation/Configure.help 2003-04-25 13:54:18.000000000 +0100 @@ -262,6 +262,12 @@ If you don't have this computer, you may safely say N. +Clustered APIC support +CONFIG_X86_CLUSTERED_APIC + This option is required to support systems with more than 8 logical CPUs. + + If you don't have such a computer, you may safely say N. + IO-APIC support on uniprocessors CONFIG_X86_UP_IOAPIC An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an @@ -464,8 +470,14 @@ The initial RAM disk is a RAM disk that is loaded by the boot loader (loadlin or lilo) and that is mounted as root before the normal boot procedure. It is typically used to load modules needed to mount the - "real" root file system, etc. See - for details. + "real" root file system, etc. + + Due to a problem elsewhere in the kernel, initial RAM disks _must_ + have the file system on them created with a 1024 byte block size. + If any other value is used, the kernel will be unable to mount the + RAM disk at boot time, causing a kernel panic. + + See for details. Loopback device support CONFIG_BLK_DEV_LOOP @@ -1105,10 +1117,12 @@ SAY N! -AMD Viper (7401/7409/7411) chipset support +AMD and nVidia IDE support CONFIG_BLK_DEV_AMD74XX - This driver ensures (U)DMA support for the AMD756/760 Viper - chipsets. + This driver adds explicit support for AMD-7xx and AMD-8111 chips + and also for the nVidia nForce chip. This allows the kernel to + change PIO, DMA and UDMA speeds and to configure the chip to + optimum performance. If you say Y here, you also need to say Y to "Use DMA by default when available", above. @@ -1837,6 +1851,20 @@ want), say M here and read . The module will be called lvm-mod.o. +Device-mapper support +CONFIG_BLK_DEV_DM + Device-mapper is a low level volume manager. It works by allowing + people to specify mappings for ranges of logical sectors. Various + mapping types are available, in addition people may write their own + modules containing custom mappings if they wish. + + Higher level volume managers such as LVM2 use this driver. + + If you want to compile this as a module, say M here and read + . The module will be called dm-mod.o. + + If unsure, say N. + Multiple devices driver support (RAID and LVM) CONFIG_MD Support multiple physical spindles through a single logical device. @@ -3590,11 +3618,10 @@ Generic SiS support CONFIG_AGP_SIS - This option gives you AGP support for the GLX component of the "soon - to be released" XFree86 4.x on Silicon Integrated Systems [SiS] - chipsets. + This option gives you AGP support for the GLX component of + XFree86 4.x on Silicon Integrated Systems [SiS] chipsets. - Note that 5591/5592 AGP chipsets are NOT supported. + The 5591/5592 AGP bridge is only generically supported. You should say Y here if you use XFree86 3.3.6 or 4.x and want to use GLX or DRI. If unsure, say N. @@ -4758,12 +4785,11 @@ messages. Most people will want to say N here. If unsure, you will also want to say N. -Matrox unified accelerated driver CONFIG_FB_MATROX - Say Y here if you have a Matrox Millennium, Millennium II, Mystique, - Mystique 220, Productiva G100, Mystique G200, Millennium G200, - Matrox G400, G450 or G550 card in your box. At this time, support for - the G-series digital output is almost non-existant. + Say Y here if you have a Matrox Millennium, Matrox Millennium II, + Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox + Mystique G200, Matrox Millennium G200, Matrox Marvel G200 video, + Matrox G400, G450 or G550 card in your box. This driver is also available as a module ( = code which can be inserted and removed from the running kernel whenever you want). @@ -4774,7 +4800,6 @@ module load time. The parameters look like "video=matrox:XXX", and are described in . -Matrox Millennium I/II support CONFIG_FB_MATROX_MILLENIUM Say Y here if you have a Matrox Millennium or Matrox Millennium II video card. If you select "Advanced lowlevel driver options" below, @@ -4782,7 +4807,6 @@ packed pixel, 24 bpp packed pixel and 32 bpp packed pixel. You can also use font widths different from 8. -Matrox Mystique support CONFIG_FB_MATROX_MYSTIQUE Say Y here if you have a Matrox Mystique or Matrox Mystique 220 video card. If you select "Advanced lowlevel driver options" below, @@ -4790,27 +4814,46 @@ packed pixel and 32 bpp packed pixel. You can also use font widths different from 8. -Matrox G100/G200/G400/G450/G550 support -CONFIG_FB_MATROX_G100 - Say Y here if you have a Matrox G100, G200, G400, G450, or G550 - based video card. If you select "Advanced lowlevel driver options", - you should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp - packed pixel and 32 bpp packed pixel. You can also use font widths +CONFIG_FB_MATROX_G450 + Say Y here if you have a Matrox G100, G200, G400, G450 or G550 based + video card. If you select "Advanced lowlevel driver options", you + should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed + pixel and 32 bpp packed pixel. You can also use font widths different from 8. If you need support for G400 secondary head, you must first say Y to "I2C support" and "I2C bit-banging support" in the character devices section, and then to "Matrox I2C support" and "G400 second head - support" here in the framebuffer section. + support" here in the framebuffer section. G450/G550 secondary head + and digital output are supported without additional modules. - If you have G550, you must also compile support for G450/G550 secondary - head into kernel, otherwise picture will be shown only on the output you - are probably not using... + The driver starts in monitor mode. You must use the matroxset tool + (available at ) to + swap primary and secondary head outputs, or to change output mode. + Secondary head driver always start in 640x480 resolution and you + must use fbset to change it. + + Do not forget that second head supports only 16 and 32 bpp + packed pixels, so it is a good idea to compile them into the kernel + too. You can use only some font widths, as the driver uses generic + painting procedures (the secondary head does not use acceleration + engine). + + G450/G550 hardware can display TV picture only from secondary CRTC, + and it performs no scaling, so picture must have 525 or 625 lines. + +CONFIG_FB_MATROX_G100A + Say Y here if you have a Matrox G100, G200 or G400 based + video card. If you select "Advanced lowlevel driver options", you + should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed + pixel and 32 bpp packed pixel. You can also use font widths + different from 8. - If you need support for G450 or G550 secondary head, say Y to - "Matrox G450/G550 second head support" below. + If you need support for G400 secondary head, you must first say Y to + "I2C support" and "I2C bit-banging support" in the character devices + section, and then to "Matrox I2C support" and "G400 second head + support" here in the framebuffer section. -Matrox I2C support CONFIG_FB_MATROX_I2C This drivers creates I2C buses which are needed for accessing the DDC (I2C) bus present on all Matroxes, an I2C bus which @@ -4824,7 +4867,6 @@ If you compile it as module, it will create a module named i2c-matroxfb.o. -Matrox G400 second head support CONFIG_FB_MATROX_MAVEN WARNING !!! This support does not work with G450 !!! @@ -4853,32 +4895,14 @@ painting procedures (the secondary head does not use acceleration engine). -Matrox G450 second head support -CONFIG_FB_MATROX_G450 - Say Y or M here if you want to use a secondary head (meaning two - monitors in parallel) on G450, or if you are using analog output - of G550. - - If you compile it as module, two modules are created, - matroxfb_crtc2.o and matroxfb_g450.o. Both modules are needed if you - want two independent display devices. - - The driver starts in monitor mode and currently does not support - output in TV modes. You must use the matroxset tool (available - at ) to swap - primary and secondary head outputs. Secondary head driver always - start in 640x480 resolution and you must use fbset to change it. - - Note on most G550 cards the analog output is the secondary head, - so you will need to say Y here to use it. - - Also do not forget that second head supports only 16 and 32 bpp - packed pixels, so it is a good idea to compile them into the kernel - too. You can use only some font widths, as the driver uses generic - painting procedures (the secondary head does not use acceleration - engine). - -Matrox unified driver multihead support +CONFIG_FB_MATROX_PROC + Say Y or M here if you want to access some informations about driver + state through /proc interface. + + You should download matrox_pins tool (available at + ) to get human + readable output. + CONFIG_FB_MATROX_MULTIHEAD Say Y here if you have more than one (supported) Matrox device in your computer and you want to use all of them for different monitors @@ -4980,19 +5004,36 @@ SIS acceleration CONFIG_FB_SIS - This is the frame buffer device driver for the SiS 630 and 640 Super - Socket 7 UMA cards. Specs available at . + This is the frame buffer device driver for SiS VGA controllers. + + This driver is required for SiS DRM. + + See for detailed + documentation. + + Hardware specs available at . SIS 630/540/730 support CONFIG_FB_SIS_300 - This is the frame buffer device driver for the SiS 630 and related - Super Socket 7 UMA cards. Specs available at - . + This is the frame buffer device driver for SiS 300/305/630/730 VGA + controllers. + + This driver is required for SiS DRM. + + See for detailed + documentation. + + Hardware specs available at . SIS 315H/315 support CONFIG_FB_SIS_315 - This is the frame buffer device driver for the SiS 315 graphics - card. Specs available at . + This is the frame buffer device driver for SiS 315 (including 315H + and 315PRO), 650, M650, 651, 740 and Xabre VGA controllers. + + See for detailed + documentation. + + Hardware specs available at . IMS Twin Turbo display support CONFIG_FB_IMSTT @@ -5324,6 +5365,19 @@ replacement for kerneld.) Say Y here and read about configuring it in . +Kernel .config file saved in kernel image +CONFIG_IKCONFIG + This option enables the complete Linux kernel ".config" file contents + to be saved in the kernel (zipped) image file. It provides + documentation of which kernel options are used in a running kernel or + in an on-disk kernel. It can be extracted from the kernel image file + with a script and used as input to rebuild the current kernel or to + build another kernel. Since the kernel image is zipped, using this + option adds approximately 8 KB to a kernel image file. + This option is not available as a module. If you want a separate + file to save the kernel's .config contents, use 'installkernel' or 'cp' + or a similar tool, or just save it in '/lib/modules/'. + ARP daemon support CONFIG_ARPD Normally, the kernel maintains an internal cache which maps IP @@ -9147,7 +9201,7 @@ Aironet 4500/4800 I365 broken support CONFIG_AIRONET4500_I365 If you have a PCMCIA Aironet 4500/4800 card which you want to use - without the standard PCMCIA cardservices provided by the pcmcia-cs + without the standard PCMCIA card services provided by the pcmcia-cs package, say Y here. This is not recommended, so say N. Aironet 4500/4800 PCMCIA support @@ -10554,6 +10608,15 @@ If unsure, say N here. +Raw HDLC Ethernet device support +CONFIG_HDLC_RAW_ETH + Say Y to this option if you want generic HDLC driver to support + raw HDLC Ethernet device emulation over WAN (Wide Area Network) + connections. + You will need it for Ethernet over HDLC bridges. + + If unsure, say N here. + Cisco HDLC support CONFIG_HDLC_CISCO Say Y to this option if you want generic HDLC driver to support @@ -10568,13 +10631,6 @@ If unsure, say N here. -Frame-Relay bridging support -CONFIG_HDLC_FR_BRIDGE - Say Y to this option if you want generic HDLC driver to support - bridging LAN frames over Frame-Relay links. - - If unsure, say N here. - Synchronous Point-to-Point Protocol (PPP) support CONFIG_HDLC_PPP Say Y to this option if you want generic HDLC driver to support @@ -12765,12 +12821,44 @@ Quota support CONFIG_QUOTA If you say Y here, you will be able to set per user limits for disk - usage (also called disk quotas). Currently, it works only for the - ext2 file system. You need additional software in order to use quota - support; for details, read the Quota mini-HOWTO, available from + usage (also called disk quotas). Currently, it works for the + ext2, ext3, and reiserfs file system. You need additional software + in order to use quota support (you can download sources from + ). For further details, read + the Quota mini-HOWTO, available from . Probably the quota support is only useful for multi user systems. If unsure, say N. +Old quota format support +CONFIG_QFMT_V1 + This quota format was (is) used by kernels earlier than 2.4.??. If + you have quota working and you don't want to convert to new quota + format say Y here. + +VFS v0 quota format support +CONFIG_QFMT_V2 + This quota format allows using quotas with 32-bit UIDs/GIDs. If you + need this functionality say Y here. Note that you will need latest + quota utilities for new quota format with this kernel. + +Compatible quota interfaces +CONFIG_QIFACE_COMPAT + This option will enable old quota interface in kernel. + If you have old quota tools (version <= 3.04) and you don't want to + upgrade them say Y here. + +Original quota interface +CONFIG_QIFACE_V1 + This is the oldest quota interface. It was used for old quota format. + If you have old quota tools and you use old quota format choose this + interface (if unsure, this interface is the best one to choose). + +VFS v0 quota interface +CONFIG_QIFACE_V2 + This quota interface was used by VFS v0 quota format. If you need + support for VFS v0 quota format (eg. you're using quota on ReiserFS) + and you don't want to upgrade quota tools, choose this interface. + Memory Technology Device (MTD) support CONFIG_MTD Memory Technology Devices are flash, RAM and similar chips, often @@ -15378,7 +15466,7 @@ debugging output from the driver. This is unlike previous versions of the driver, where enabling this option would turn on debugging output automatically. - + Example: mount -t befs /dev/hda2 /mnt -o debug @@ -15781,6 +15869,30 @@ If unsure, say N. +Allow direct I/O on files in NFS +CONFIG_NFS_DIRECTIO + There are important applications whose performance or correctness + depends on uncached access to file data. Database clusters (multiple + copies of the same instance running on separate hosts) implement their + own cache coherency protocol that subsumes the NFS cache protocols. + Applications that process datasets considerably larger than the client's + memory do not always benefit from a local cache. A streaming video + server, for instance, has no need to cache the contents of a file. + + This option enables applications to perform direct I/O on files in NFS + file systems using the O_DIRECT open() flag. When O_DIRECT is set for + files, their data is not cached in the system's page cache. Direct + read and write operations are aligned to block boundaries. Data is + moved to and from user-level application buffers directly. + + Unless your program is designed to use O_DIRECT properly, you are much + better off allowing the NFS client to manage caching for you. Misusing + O_DIRECT can cause poor server performance or network storms. This + kernel build option defaults OFF to avoid exposing system administrators + unwittingly to a potentially hazardous feature. + + If unsure, say N. + Root file system on NFS CONFIG_ROOT_NFS If you want your Linux box to mount its whole root file system (the @@ -16177,7 +16289,7 @@ Say Y here if you would like to use hard disks under Linux which were partitioned on a Macintosh. -Windows Logical Disk Manager (Dynamic Disk) support (EXPERIMENTAL) +Windows Logical Disk Manager (Dynamic Disk) support CONFIG_LDM_PARTITION Say Y here if you would like to use hard disks under Linux which were partitioned using Windows 2000's or XP's Logical Disk Manager. @@ -16192,8 +16304,7 @@ Normal partitions are now called Basic Disks under Windows 2000 and XP. - Technical documentation to accompany this driver is available from: - . + For a fuller description read . If unsure, say N. @@ -16266,8 +16377,9 @@ Intel EFI GUID partition support CONFIG_EFI_PARTITION Say Y here if you would like to use hard disks under Linux which - were partitioned using EFI GPT. Presently only useful on the - IA-64 platform. + were partitioned using EFI GPT. This is the default partition + scheme on IA64, and can be used on other platforms when + large block device (64-bit block address) support is desired. Ultrix partition table support CONFIG_ULTRIX_PARTITION @@ -17109,17 +17221,36 @@ HIL keyboard support CONFIG_HIL The "Human Interface Loop" is a older, 8-channel USB-like controller - used in Hewlett Packard PA-RISC based machines. There are a few - cases where it is seen on PC/MAC architectures as well, usually also - manufactured by HP. This driver is based off MACH and BSD drivers, - and implements support for a keyboard attached to the HIL port. + used in several Hewlett Packard models. This driver is based off + MACH and BSD drivers, and implements support for a keyboard attached + to the HIL port, but not for any other types of HIL input devices + like mice or tablets. However, it has been thoroughly tested and is + stable. + Full support for the USB-like functions and non-keyboard channels of - the HIL is not provided for in this driver. There are vestiges of - mouse support in the driver, but it is probably not working. The - necessary hardware documentation to fully support the HIL controller - and interface it to the linux-input API is lacking. + the HIL is currently being added to the PA-RISC port and will + be backported to work on the m68k port as well. + + Enable this option if you intend to use a HIL keyboard as your + primary keyboard and/or do not wish to test the new HIL driver. - Enable this option if you intend to use a HIL keyboard. +HP System Device Controller support +CONFIG_HP_SDC + This option enables supports for the the "System Device Controller", + an i8042 carrying microcode to manage a few miscellanous devices + on some Hewlett Packard systems. The SDC itself contains a 10ms + resolution timer/clock capable of delivering interrupts on periodic + and one-shot basis. The SDC may also be connected to a battery-backed + real-time clock, a basic audio waveform generator, and an HP-HIL + Master Link Controller serving up to seven input devices. + + By itself this option is rather useless, but enabling it will + enable selection of drivers for the abovementioned devices. + It is, however, incompatible with the old, reliable HIL keyboard + driver, and the new HIL driver is experimental, so if you plan to + use a HIL keyboard as your primary keyboard, you may wish to + keep using that driver until the new HIL drivers have had more + testing. Include IOP (IIfx/Quadra 9x0) ADB driver CONFIG_ADB_IOP @@ -17310,6 +17441,27 @@ If you want to compile this driver as a module, say M here and read . The module will be called pcxx.o. +Cyclades-PC300 support +CONFIG_PC300 + This is a driver for the Cyclades-PC300 synchronous communication + boards. These boards provide synchronous serial interfaces to your + Linux box (interfaces currently available are RS-232/V.35, X.21 and + T1/E1). If you wish to support Multilink PPP, please select the + option below this one and read the file README.mlppp provided by PC300 + package. + + 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 Documentation/modules.txt. The module will be + called pc300.o. + + If you haven't heard about it, it's safe to say N. + +Cyclades-PC300 Sync TTY (to MLPPP) support +CONFIG_PC300_MLPPP + Say 'Y' to this option if you are planning to use Multilink PPP over the + PC300 synchronous communication boards. + SDL RISCom/8 card support CONFIG_RISCOM8 This is a driver for the SDL Communications RISCom/8 multiport card, @@ -17429,6 +17581,19 @@ read . The module will be called istallion.o. +PDC software console support +CONFIG_PDC_CONSOLE + Saying Y here will enable the software based PDC console to be + used as the system console. This is useful for machines in + which the hardware based console has not been written yet. The + following steps must be competed to use the PDC console: + + 1. create the device entry (mknod /dev/ttyB0 c 60 0) + 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 + 3. Add device ttyB0 to /etc/securetty (if you want to log on as + root on this console.) + 4. Change the kernel command console parameter to: console=ttyB0 + Microgate SyncLink adapter support CONFIG_SYNCLINK Provides support for the SyncLink ISA and PCI multiprotocol serial @@ -17578,6 +17743,10 @@ doing that; to actually get it to happen you need to pass the option "console=lp0" to the kernel at boot time. + Note that kernel messages can get lost if the printer is out of + paper (or off, or unplugged, or too busy..), but this behaviour + can be changed. See drivers/char/lp.c (do this at your own risk). + If the printer is out of paper (or off, or unplugged, or too busy..) the kernel will stall until the printer is ready again. By defining CONSOLE_LP_STRICT to 0 (at your own risk) you @@ -19083,6 +19252,15 @@ . The module will be called cpuid.o +x86 BIOS Enhanced Disk Drive support +CONFIG_EDD + Say Y or M here if you want to enable BIOS Enhanced Disk Drive + Services real mode BIOS calls to determine which disk + BIOS tries boot from. This information is then exported via /proc. + + This option is experimental, but believed to be safe, + and most disk controller BIOS vendors do not yet implement this feature. + SBC-60XX Watchdog Timer CONFIG_60XX_WDT This driver can be used with the watchdog timer found on some @@ -19177,6 +19355,34 @@ Provides an emulation for RTC_UIE which is required by some programs and may improve precision of the generic RTC support in some cases. +Generic Real Time Clock Support +CONFIG_GEN_RTC + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + + In 2.4 and later kernels this is the only way to set and get rtc + time on m68k systems so it is highly recommended. + + It reports status information via the file /proc/driver/rtc and its + behaviour is set by various ioctls on /dev/rtc. If you enable the + "extended RTC operation" below it will also provide an emulation + for RTC_UIE which is required by some programs and may improve + precision in some cases. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called genrtc.o. If you want to compile it as a module, + say M here and read . To load the + module automatically add 'alias char-major-10-135 genrtc' to your + /etc/modules.conf + +Extended RTC operation +CONFIG_GEN_RTC_X + Provides an emulation for RTC_UIE which is required by some programs + and may improve precision of the generic RTC support in some cases. + Tadpole ANA H8 Support CONFIG_H8 The Hitachi H8/337 is a microcontroller used to deal with the power @@ -23168,6 +23374,23 @@ say M here and read . The module will be called radio-sf16fmi.o. +SF16FMR2 Radio +CONFIG_RADIO_SF16FMR2 + Choose Y here if you have one of these FM radio cards. If you + compile the driver into the kernel and your card is not PnP one, you + have to add "sf16fmr2=" to the kernel command line (I/O address is + 0x284 or 0x384, default 0x384). + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called radio-sf16fmr2.o. + Typhoon Radio (a.k.a. EcoRadio) CONFIG_RADIO_TYPHOON Choose Y here if you have one of these FM radio cards, and then fill @@ -23656,24 +23879,28 @@ system console. Available only if 3270 support is compiled in statically. -Support for HWC line mode terminal -CONFIG_HWC - Include support for IBM HWC line-mode terminals. - -Console on HWC line mode terminal -CONFIG_HWC_CONSOLE - Include support for using an IBM HWC line-mode terminal as the Linux +Support for SCLP +CONFIG_SCLP + Include support for the IBM SCLP interface to the service element. + +Support for SCLP line mode terminal +CONFIG_SCLP_TTY + Include support for IBM SCLP line-mode terminals. + +Support for console on SCLP line mode terminal +CONFIG_SCLP_CONSOLE + Include support for using an IBM SCLP line-mode terminal as a Linux system console. -Control Program Identification -CONFIG_HWC_CPI - Allows for Control Program Identification via the HWC interface, - i.e. provides a mean to pass an OS instance name (system name) - to the machine. - - This option should only be selected as a module since the - system name has to be passed as module parameter. The module - will be called hwc_cpi.o. +Control-Program Identification +CONFIG_SCLP_CPI + This option enables the hardware console interface for system + identification. This is commonly used for workload management and + gives you a nice name for the system on the service element. + Please select this option as a module since built-in operation is + completely untested. + You should only select this option if you know what you are doing, + need this feature and intend to run your kernel in LPAR. S/390 tape device support CONFIG_S390_TAPE @@ -23769,6 +23996,20 @@ enabled, you'll be able to toggle chpids logically offline and online. Even if you don't understand what this means, you should say "Y". +Process warning machine checks +CONFIG_MACHCHK_WARNING + Select this option if you want the machine check handler on IBM S/390 or + zSeries to process warning machine checks (e.g. on power failures). + If unsure, say "Y". + +Use chscs for Common I/O +CONFIG_CHSC + Select this option if you want the s390 common I/O layer to use information + obtained by channel subsystem calls. This will enable Linux to process link + failures and resource accessibility events. Moreover, if you have procfs + enabled, you'll be able to toggle chpids logically offline and online. Even + if you don't understand what this means, you should say "Y". + Kernel support for 31 bit ELF binaries CONFIG_S390_SUPPORT Select this option if you want to enable your system kernel to @@ -25920,6 +26161,14 @@ of the BUG call as well as the EIP and oops trace. This aids debugging but costs about 70-100K of memory. +Morse code panics +CONFIG_PANIC_MORSE + Say Y here to receive panic messages in morse code on your keyboard LEDs, and + optionally the PC speaker, if available. + The kernel param "panicblink" controls this feature, set it to 0 to disable, + 1 for LEDs only, 2 for pc speaker, or 3 for both. If you disable this option, + then you will receive a steady blink on the LEDs instead. + Include kgdb kernel debugger CONFIG_KGDB Include in-kernel hooks for kgdb, the Linux kernel source level @@ -25965,9 +26214,11 @@ U2/Uturn I/O MMU CONFIG_IOMMU_CCIO - Say Y here to enable DMA management routines for the first - generation of PA-RISC cache-coherent machines. Programs the - U2/Uturn chip in "Virtual Mode" and use the I/O MMU. + The U2/UTurn is a bus converter with io mmu present in the Cxxx, D, + J, K, and R class machines. Compiling this driver into the kernel will + not hurt anything, removing it will reduce your kernel by about 14k. + + If unsure, say Y. LBA/Elroy PCI support CONFIG_PCI_LBA @@ -26340,10 +26591,103 @@ written) to implement the policy. If you don't understand what this is all about, it's safe to say 'N'. + For more information, take a look at linux/Documentation/cpufreq or + at + + If in doubt, say N. + +CONFIG_CPU_FREQ_24_API + This enables the /proc/sys/cpu/ sysctl interface for controlling + CPUFreq, as known from the 2.4.-kernel patches for CPUFreq. 2.5 + uses /proc/cpufreq instead. Please note that some drivers do not + work with the 2.4. /proc/sys/cpu sysctl interface, so if in doubt, + say N here. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_POWERNOW_K6 + This adds the CPUFreq driver for mobile AMD K6-2+ and mobile + AMD K6-3+ processors. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_P4_CLOCKMOD + This adds the CPUFreq driver for Intel Pentium 4 / XEON + processors. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_ELAN_CPUFREQ + This adds the CPUFreq driver for AMD Elan SC400 and SC410 + processors. + + You need to specify the processor maximum speed as a boot + parameter (or as module parameter): + elanfreq=maxspeed + with the argument "maxspeed" given in kHz. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_LONGHAUL + This adds the CPUFreq driver for VIA Samuel/CyrixIII, + VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T + processors. + + If you do not want to scale the Front Side Bus or voltage, + pass the module parameter "dont_scale_fsb=1" or + "dont_scale_voltage=1". Additionally, it is advised that + you pass the current Front Side Bus speed (in MHz) to + this module as module parameter "current_fsb", e.g. + "current_fsb=133" for a Front Side Bus speed of 133 MHz. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_SPEEDSTEP + This adds the CPUFreq driver for certain mobile Intel Pentium III + (Coppermine), all mobile Intel Pentium III-M (Tulatin) and all + mobile Intel Pentium 4 P4-Ms. + + If you use a Coppermine Pentium III which is capable of + SpeedStep, you need to pass the boot or module parameter + "speedstep_coppermine=1" to the kernel. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_LONGRUN + This adds the CPUFreq driver for Transmeta Crusoe processors which + support LongRun. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + +CONFIG_X86_GX_SUSPMOD + This add the CPUFreq driver for NatSemi Geode processors which + support suspend modulation. + + For details, take a look at linux/Documentation/cpufreq. + + If in doubt, say N. + SiS CONFIG_DRM_SIS - Choose this option if you have a SIS graphics card. AGP support is - required for this driver to work. + Choose this option if you have a SIS 300 series graphics card or + VGA controller (300, 305, 540, 630, 730). + + AGP support as well as the SiS framebuffer driver are required + for this driver to work. Etrax Ethernet slave support (over lp0/1) CONFIG_ETRAX_ETHERNET_LPSLAVE @@ -26353,7 +26697,7 @@ Slave has its own LEDs CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS - Enable if the slave has it's own LEDs. + Enable if the slave has its own LEDs. ATA/IDE support CONFIG_ETRAX_IDE @@ -26523,6 +26867,90 @@ If unsure, say N. +NatSemi SCx200 support +CONFIG_SCx200 + This provides basic support for the National Semiconductor SCx200 + processor. Right now this is just a driver for the GPIO pins. + + If you don't know what to do here, say N. + + This support is also available as a module. If compiled as a + module, it will be called scx200.o. + +NatSemi SCx200 Watchdog +CONFIG_SCx200_WDT + Enable the built-in watchdog timer support on the National + Semiconductor SCx200 processors. + + If compiled as a module, it will be called scx200_watchdog.o. + +Flash device mapped with DOCCS on NatSemi SCx200 +CONFIG_MTD_SCx200_DOCFLASH + Enable support for a flash chip mapped using the DOCCS signal on a + National Semiconductor SCx200 processor. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_docflash.o. + +NatSemi SCx200 I2C using GPIO pins +CONFIG_SCx200_I2C + Enable the use of two GPIO pins of a SCx200 processor as an I2C bus. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_i2c.o. + +GPIO pin used for SCL +CONFIG_SCx200_I2C_SCL + Enter the GPIO pin number used for the SCL signal. This value can + also be specified with a module parameter. + +GPIO pin used for SDA +CONFIG_SCx200_I2C_SDA + Enter the GPIO pin number used for the SSA signal. This value can + also be specified with a module parameter. + +NatSemi SCx200 ACCESS.bus +CONFIG_SCx200_ACB + Enable the use of the ACCESS.bus controllers of a SCx200 processor. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_acb.o. + +IPMI top-level message handler +CONFIG_IPMI_HANDLER + This enables the central IPMI message handler, required for IPMI + to work. Note that you must have this enabled to do any other IPMI + things. + + IPMI is a standard for managing sensors (temperature, + voltage, etc.) in a system. + + See Documentation/IPMI.txt for more details on the driver. + + If unsure, say N. + +Generate a panic event to all BMCs on a panic +CONFIG_IPMI_PANIC_EVENT + When a panic occurs, this will cause the IPMI message handler to + generate an IPMI event describing the panic to each interface + registered with the message handler. + +Device interface for IPMI +CONFIG_IPMI_DEVICE_INTERFACE + This provides an IOCTL interface to the IPMI message handler so + userland processes may use IPMI. It supports poll() and select(). + +IPMI KCS handler +CONFIG_IPMI_KCS + Provides a driver for a KCS-style interface to a BMC. + +IPMI Watchdog Timer +CONFIG_IPMI_WATCHDOG + This enables the IPMI watchdog timer. + # # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/cpufreq linux.21rc1-ac2/Documentation/cpufreq --- linux.21rc1/Documentation/cpufreq 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/cpufreq 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,363 @@ + CPU frequency and voltage scaling code in the Linux(TM) kernel + + + L i n u x C P U F r e q + + + + + Dominik Brodowski + David Kimdon + + + + Clock scaling allows you to change the clock speed of the CPUs on the + fly. This is a nice method to save battery power, because the lower + the clock speed, the less power the CPU consumes. + + + +Contents: +--------- +1. Supported architectures +2. User interface +2.1 /proc/cpufreq interface [2.6] +2.2. /proc/sys/cpu/ interface [2.4] +3. CPUFreq core and interfaces +3.1 General information +3.2 CPUFreq notifiers +3.3 CPUFreq architecture drivers +4. Mailing list and Links + + + +1. Supported architectures +========================== + +ARM: + ARM Integrator, SA 1100, SA1110 +-------------------------------- + No known issues. + + +AMD Elan: + SC400, SC410 +-------------------------------- + You need to specify the highest allowed CPU frequency as + a module parameter ("max_freq") or as boot parameter + ("elanfreq="). Else the available speed range will be + limited to the speed at which the CPU runs while this + module is loaded. + + +VIA Cyrix Longhaul: + VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, + VIA Cyrix Ezra, VIA Cyrix Ezra-T +-------------------------------- + If you do not want to scale the Front Side Bus or voltage, + pass the module parameter "dont_scale_fsb=1" or + "dont_scale_voltage=1". Additionally, it is advised that + you pass the current Front Side Bus speed (in MHz) to + this module as module parameter "current_fsb", e.g. + "current_fsb=133" for a Front Side Bus speed of 133 MHz. + + +Intel SpeedStep: + certain mobile Intel Pentium III (Coppermine), and all mobile + Intel Pentium III-M (Tualatin) and mobile Intel Pentium 4 P4-Ms. +-------------------------------- + Unfortunately, only modern Intel ICH2-M and ICH3-M chipsets are + supported yet. + + +P4 CPU Clock Modulation: + Intel Pentium 4 Xeon processors +--------------------------------- + Note that you can only switch the speed of two logical CPUs at + once - but each phyiscal CPU may have different throttling levels. + + +PowerNow! K6: + mobile AMD K6-2+ / mobile K6-3+: +-------------------------------- + No known issues. + + +Transmeta Crusoe Longrun: + Transmeta Crusoe processors: +-------------------------------- + It is recommended to use the 2.6. /proc/cpufreq interface when + using this driver + + + +2. User Interface +================= + +2.1 /proc/cpufreq interface [2.6] +*********************************** + +Starting in the patches for kernel 2.5.33, CPUFreq uses a "policy" +interface /proc/cpufreq. + +When you "cat" this file, you'll find something like: + +-- + minimum CPU frequency - maximum CPU frequency - policy +CPU 0 1200000 ( 75%) - 1600000 (100%) - performance +-- + +This means the current policy allows this CPU to be run anywhere +between 1.2 GHz (the value is in kHz) and 1.6 GHz with an eye towards +performance. + +To change the policy, "echo" the desired new policy into +/proc/cpufreq. Use one of the following formats: + +cpu_nr:min_freq:max_freq:policy +cpu_nr%min_freq%max_freq%policy +min_freq:max_freq:policy +min_freq%max_freq%policy + +with cpu_nr being the CPU which shall be affected, min_freq and +max_freq the lower and upper limit of the CPU core frequency in kHz, +and policy either "performance" or "powersave". +A few examples: + +root@notebook:#echo -n "0:0:0:powersave" > /proc/cpufreq + sets the CPU #0 to the lowest supported frequency. + +root@notebook:#echo -n "1%100%100%performance" > /proc/cpufreq + sets the CPU #1 to the highest supported frequency. + +root@notebook:#echo -n "1000000:2000000:performance" > /proc/cpufreq + to set the frequency of all CPUs between 1 GHz and 2 GHz and to + the policy "performance". + +Please note that the values you "echo" into /proc/cpufreq are +validated first, and may be limited by hardware or thermal +considerations. Because of this, a read from /proc/cpufreq might +differ from what was written into it. + + +When you read /proc/cpufreq for the first time after a CPUFreq driver +has been initialized, you'll see the "default policy" for this +driver. If this does not suit your needs, you can pass a boot +parameter to the cpufreq core. Use the following syntax for this: + "cpufreq=min_freq:max_freq:policy", i.e. you may not chose a +specific CPU and you need to specify the limits in kHz and not in +per cent. + + +2.2 /proc/cpufreq interface [2.4] +*********************************** + +Previsiously (and still available as a config option), CPUFreq used +a "sysctl" interface which is located in + /proc/sys/cpu/0/ + /proc/sys/cpu/1/ ... (SMP only) + +In these directories, you will find three files of importance for +CPUFreq: speed-max, speed-min and speed: + +speed shows the current CPU frequency in kHz, +speed-min the minimum supported CPU frequency, and +speed-max the maximum supported CPU frequency. + + +To change the CPU frequency, "echo" the desired CPU frequency (in kHz) +to speed. For example, to set the CPU speed to the lowest/highest +allowed frequency do: + +root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed +root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed + + + +3. CPUFreq core and interfaces +=============================== + +3.1 General information +************************* + +The CPUFreq core code is located in linux/kernel/cpufreq.c. This +cpufreq code offers a standardized interface for the CPUFreq +architecture drivers (those pieces of code that do actual +frequency transitions), as well as to "notifiers". These are device +drivers or other part of the kernel that need to be informed of +policy changes (like thermal modules like ACPI) or of all +frequency changes (like timing code) or even need to force certain +speed limits (like LCD drivers on ARM architecture). Additionally, the +kernel "constant" loops_per_jiffy is updated on frequency changes +here. + + +3.2 CPUFreq notifiers +*********************** + +CPUFreq notifiers conform to the standard kernel notifier interface. +See linux/include/linux/notifier.h for details on notifiers. + +There are two different CPUFreq notifiers - policy notifiers and +transition notifiers. + + +3.2.1 CPUFreq policy notifiers +****************************** + +These are notified when a new policy is intended to be set. Each +CPUFreq policy notifier is called three times for a policy transition: + +1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if + they see a need for this - may it be thermal considerations or + hardware limitations. + +2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid + hardware failure. + +3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy + - if two hardware drivers failed to agree on a new policy before this + stage, the incompatible hardware shall be shut down, and the user + informed of this. + +The phase is specified in the second argument to the notifier. + +The third argument, a void *pointer, points to a struct cpufreq_policy +consisting of five values: cpu, min, max, policy and max_cpu_freq. Min +and max are the lower and upper frequencies (in kHz) of the new +policy, policy the new policy, cpu the number of the affected CPU or +CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported +CPU frequency. This value is given for informational purposes only. + + +3.2.2 CPUFreq transition notifiers +********************************** + +These are notified twice when the CPUfreq driver switches the CPU core +frequency and this change has any external implications. + +The second argument specifies the phase - CPUFREQ_PRECHANGE or +CPUFREQ_POSTCHANGE. + +The third argument is a struct cpufreq_freqs with the following +values: +cpu - number of the affected CPU or CPUFREQ_ALL_CPUS +old - old frequency +new - new frequency + + +3.3 CPUFreq architecture drivers +********************************** + +CPUFreq architecture drivers are the pieces of kernel code that +actually perform CPU frequency transitions. These need to be +initialized separately (separate initcalls), and may be +modularized. They interact with the CPUFreq core in the following way: + +cpufreq_register() +------------------ +cpufreq_register registers an arch driver to the CPUFreq core. Please +note that only one arch driver may be registered at any time. -EBUSY +is returned when an arch driver is already registered. The argument to +cpufreq_register, struct cpufreq_driver *driver, is described later. + +cpufreq_unregister() +-------------------- +cpufreq_unregister unregisters an arch driver, e.g. on module +unloading. Please note that there is no check done that this is called +from the driver which actually registered itself to the core, so +please only call this function when you are sure the arch driver got +registered correctly before. + +cpufreq_notify_transition() +--------------------------- +On "dumb" hardware where only fixed frequency can be set, the driver +must call cpufreq_notify_transition() once before, and once after the +actual transition. + +struct cpufreq_driver +--------------------- +On initialization, the arch driver is supposed to pass a pointer +to a struct cpufreq_driver *cpufreq_driver consisting of the following +entries: + +cpufreq_verify_t verify: This is a pointer to a function with the + following definition: + int verify_function (struct cpufreq_policy *policy). + This function must verify the new policy is within the limits + supported by the CPU, and at least one supported CPU is within + this range. It may be useful to use cpufreq.h / + cpufreq_verify_within_limits for this. If this is called with + CPUFREQ_ALL_CPUS, and there is no common subset of frequencies + for all CPUs, exit with an error. + +cpufreq_setpolicy_t setpolicy: This is a pointer to a function with + the following definition: + int setpolicy_function (struct cpufreq_policy *policy). + This function must set the CPU to the new policy. If it is a + "dumb" CPU which only allows fixed frequencies to be set, it + shall set it to the lowest within the limit for + CPUFREQ_POLICY_POWERSAVE, and to the highest for + CPUFREQ_POLICY_PERFORMANCE. Once CONFIG_CPU_FREQ_DYNAMIC is + implemented, it can use a dynamic method to adjust the speed + between the lower and upper limit. + +struct cpufreq_policy *policy: This is an array of NR_CPUS struct + cpufreq_policies, containing the current policies set for these + CPUs. Note that policy[cpu].max_cpu_freq must contain the + absolute maximum CPU frequency supported by the specified cpu. + +In case the driver is expected to run with the 2.4.-style API +(/proc/sys/cpu/.../), two more values must be passed +#ifdef CONFIG_CPU_FREQ_24_API + unsigned int cpu_min_freq[NR_CPUS]; + unsigned int cpu_cur_freq[NR_CPUS]; +#endif + with cpu_min_freq[cpu] being the minimum CPU frequency + supported by the CPU; and the entries in cpu_cur_freq + reflecting the current speed of the appropriate CPU. + +Some Requirements to CPUFreq architecture drivers +------------------------------------------------- +* Only call cpufreq_register() when the ability to switch CPU + frequencies is _verified_ or can't be missing. Also, all + other initialization must be done beofre this call, as + cpfureq_register calls the driver's verify and setpolicy code for + each CPU. +* cpufreq_unregister() may only be called if cpufreq_register() has + been successfully(!) called before. +* kfree() the struct cpufreq_driver only after the call to + cpufreq_unregister(), unless cpufreq_register() failed. + + + +4. Mailing list and Links +************************* + + +Mailing List +------------ +There is a CPU frequency changing CVS commit and general list where +you can report bugs, problems or submit patches. To post a message, +send an email to cpufreq@www.linux.org.uk, to subscribe go to +http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the +mailing list are available to subscribers at +http://www.linux.org.uk/mailman/private/cpufreq/. + + +Links +----- +the FTP archives: +* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ + +how to access the CVS repository: +* http://cvs.arm.linux.org.uk/ + +the CPUFreq Mailing list: +* http://www.linux.org.uk/mailman/listinfo/cpufreq + +Clock and voltage scaling for the SA-1100: +* http://www.lart.tudelft.nl/projects/scaling + +CPUFreq project homepage +* http://www.brodo.de/cpufreq/ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/DriverFixers linux.21rc1-ac2/Documentation/DriverFixers --- linux.21rc1/Documentation/DriverFixers 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/DriverFixers 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,75 @@ +People who fix drivers as a business - ie for money. (No recommendation, +business association or other relationship implied. This for the benefit of +American lawyers is just a list of people who have asked to be listed - nothing +more). + +Companies Who Will Do Small Contract Work +----------------------------------------- + +Company: BitWizard +Contact: Rogier Wolff +E-Mail: R.E.Wolff@BitWizard.nl + +Company: Caederus +Contact: Justin Mitchell +E-Mail: info@caederus.com +Location: Swansea, Wales, UK +URL: http://www.caederus.com/ + +Company: Calsoft Inc +Contact: Anupam Bhide +E-Mail: anupam@calsoftinc.com +URL: http://www.calsoftinc.com +Location: Pune, India + +Company: Hansen Partnership Inc +Contact: James Bottomley +E-Mail: James.Bottomley@HansenPartnership.com +Location: 1, Partridge Square, Oswego, Illinois 60543, USA + +Company: Linking +Contact: Elmer Joandi +E-Mail: elmer@linkingsoft.com + +Company: Penguru Consulting, LLC +Contact: Komron Takmil +E-Mail: komron@penguru.net +Location: Salt Lake City, UT USA + +Company: 7Chips +Contact: Vadim Lebedev +E-Mail: vadim@7chips.com +Location: Paris, France +Notes: Experienced in Linux and uClinux on x86/ARM/Motorola + +Company: Weinigel Ingenjörsbyrå AB +Contact: Christer Weinigel +E-Mail: christer@weinigel.se +Location: Stockholm, Sweden + +Company: WildOpenSource +Contact: Martin Hicks +E-Mail: info@wildopensource.com + + +Companies Only Interested In Larger ($10000+) Jobs +-------------------------------------------------- + + + +Companies Only Interested In Very Large ($100000+) Jobs +------------------------------------------------------- + + +To be added to the list: email giving the +following information + +Company: CompanyName [Required] +Contact: ContactName [Required] +E-Mail: An email address [Required] +URL: Web site [Optional] +Location: Area/Country [Optional] +Telephone: Contact phone number [Optional] +Speciality: Any specific speciality [Optional] +Notes: Any other notes (eg certifications, specialities) + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/i386/zero-page.txt linux.21rc1-ac2/Documentation/i386/zero-page.txt --- linux.21rc1/Documentation/i386/zero-page.txt 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/i386/zero-page.txt 2003-04-22 16:44:36.000000000 +0100 @@ -31,6 +31,7 @@ 0x1e0 unsigned long ALT_MEM_K, alternative mem check, in Kb 0x1e8 char number of entries in E820MAP (below) +0x1e9 unsigned char number of entries in EDDBUF (below) 0x1f1 char size of setup.S, number of sectors 0x1f2 unsigned short MOUNT_ROOT_RDONLY (if !=0) 0x1f4 unsigned short size of compressed kernel-part in the @@ -66,6 +67,7 @@ 0x220 4 bytes (setup.S) 0x224 unsigned short setup.S heap end pointer 0x2d0 - 0x600 E820MAP +0x600 - 0x7D4 EDDBUF (setup.S) 0x800 string, 2K max COMMAND_LINE, the kernel commandline as copied using CL_OFFSET. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/networking/generic-hdlc.txt linux.21rc1-ac2/Documentation/networking/generic-hdlc.txt --- linux.21rc1/Documentation/networking/generic-hdlc.txt 2003-04-22 16:39:32.000000000 +0100 +++ linux.21rc1-ac2/Documentation/networking/generic-hdlc.txt 2003-04-22 16:44:36.000000000 +0100 @@ -1,11 +1,13 @@ -Generic HDLC layer for Linux kernel 2.4/2.5 +Generic HDLC layer Krzysztof Halasa -May, 2001 +January, 2003 Generic HDLC layer currently supports: -- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP), -- raw HDLC (IPv4 only), +- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP). + Normal (routed) and Ethernet-bridged (Ethernet device emulation) + interfaces can share a single PVC. +- raw HDLC - either IP (IPv4) interface or Ethernet device emulation. - Cisco HDLC, - PPP (uses syncppp.c), - X.25 (uses X.25 routines). @@ -15,6 +17,10 @@ - RISCom/N2 by SDL Communications Inc. - and others, some not in the official kernel. +Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible +with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging). + + Make sure the hdlc.o and the hardware driver are loaded. It should create a number of "hdlc" (hdlc0 etc) network devices, one for each WAN port. You'll need the "sethdlc" utility, get it from: @@ -58,6 +64,9 @@ no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity +* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding + as above. + * cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported) interval - time in seconds between keepalive packets timeout - time in seconds after last received keepalive packet before @@ -77,7 +86,12 @@ n392 - error threshold - both user and network n393 - monitored events count - both user and network -* create | delete n - FR only - adds / deletes PVC interface with DLCI #n. +Frame-Relay only: +* create n | delete n - adds / deletes PVC interface with DLCI #n. + Newly created interface will be named pvc0, pvc1 etc. + +* create ether n | delete ether n - adds a device for Ethernet-bridged + frames. The device will be named pvceth0, pvceth1 etc. @@ -104,11 +118,11 @@ If you have a problem with N2 or C101 card, you can issue the "private" -command to see port's packet descriptor rings: +command to see port's packet descriptor rings (in kernel logs): sethdlc hdlc0 private -The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS. +The hardware driver has to be build with CONFIG_HDLC_DEBUG_RINGS. Attaching this info to bug reports would be helpful. Anyway, let me know if you have problems using this. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/s390/c7000.txt linux.21rc1-ac2/Documentation/s390/c7000.txt --- linux.21rc1/Documentation/s390/c7000.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/s390/c7000.txt 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,92 @@ +Cisco 7000 (CLAW) support + +The c7000 module provides support for a channel attached Cisco 7xxx +family router on Linux/390. The parameters for the module are as follows: + + base0=0xYYYY This parameter defines the base unit address of the + channel attached router. + + lhost0=s1 This parameter defines the local host name and + must match the claw directive "host-name" field + (first string). The default value is "UTS". + + uhost0=s2 This parameter defines the unit's name and must + match the claw directive "device-name" field + (second string). The default value is "C7011". + + lappl0=s3 This parameter defines the local application name + and must match the claw directive "host-app" field + (third string). The default value is "TCPIP". + + uappl0=s4 This parameter defines the unit application name and + must match the claw directive "device-app" field + (fourth string). The default value is "TCPIP". + + dbg=x This parameter defines the message level. Higher + numbers will result in additional diagnostic messages. + The default value is 0. + + noauto=z This parameter controls the automatic detection of + the unit base address (base0). When set to a non + zero value, automatic detection of unit base addresses + is not done. The default value is 0. + +Note that the values coded in strings s1 - s4 are case sensitive. + +For example, assume that the following claw directive has been coded in +the Cisco router: + +claw 0100 6C 129.212.61.101 UTS C7011 TCPIP TCPIP + +The module can be loaded using the following command: + +insmod c7000 base0=0x336c lhost0="UTS" uhost0="C7011" lappl0="TCPIP" \ +uappl0="TCPIP" dbg=0 noauto=1 + +Additional interfaces can be defined via parameters base1 - base3, +lhost1 - lhost3, lappl1 - lappl3, uhost1 - uhost3, uappl1 - uappl3. +The interfaces are named "ci0" - "ci3". After loading the module, the +ifconfig command is used to configure the interface. For example: + +ifconfig ci0 129.212.61.101 +ifconfig ci0 netmask 255.255.255.0 broadcast 129.212.61.0 +ifconfig ci0 + +The route command is used to specify the router as the default route: + +route add default gw 129.212.61.200 + +The interface can be automatically activated at boot time by following this +procedure: + +1) Add the following two lines to file "/etc/conf.modules": + +alias ci0 c7000 +options c7000 base0=0xYYYY lhost0=s1 uhost0=s2 lappl0=s3 uappl0=s4 + +2) Edit file "/etc/sysconfig/network" as follows: + +NETWORKING=yes +FORWARD_IPV4=no +HOSTNAME=your-hostname +GATEWAYDEV=ci0 +GATEWAY=your-gateway-ip-address + +Substitute your own host name and gateway IP address. + +3) Create a file in directory "/etc/sysconfig/network-scripts" called +"ifcfg-ci0". The contents are as follows: + +DEVICE=ci0 +USERCTL=no +ONBOOT=yes +BOOTPROTO=none +BROADCAST=your-broadcast-ip-address +NETWORK=your-network-address +NETMASK=your-netmask +IPADDR=your-ip-address + +Substitute your IP address, broadcast IP address, network address and +network mask. + +4) chmod +x ifcfg-ci0 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/s390/cds.txt linux.21rc1-ac2/Documentation/s390/cds.txt --- linux.21rc1/Documentation/s390/cds.txt 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/s390/cds.txt 2003-04-25 13:50:29.000000000 +0100 @@ -5,12 +5,14 @@ Author : Ingo Adlung -Copyright, IBM Corp. 1999 +Copyright, IBM Corp. 1999-2002 + +ChangeLog: 02/01/2002 Cornelia Huck brought up-to-date Introduction This document describes the common device support routines for Linux/390. -Different than other hardware architectures, ESA/390 hasdefined a unified +Different than other hardware architectures, ESA/390 has defined a unified I/O access method. This gives relief to the device drivers as they don't have to deal with different bus types, polling versus interrupt processing, shared versus non-shared interrupt processing, DMA versus port @@ -26,14 +28,11 @@ In order to build common device support for ESA/390 I/O interfaces, a functional layer was introduced that provides generic I/O access methods to -the hardware. The following figure shows the usage of the common device support -of Linux/390 using a TbCP/IP driven device access an example. Similar figures -could be drawn for other access methods, e.g. file system access to disk -devices. - -The common device support layer shown above comprises the I/O support routines -defined below. Some of them implement common Linux device driver interfaces, -while some of them are ESA/390 platform specific. +the hardware. + +The common device support layer comprises the I/O support routines defined +below. Some of them implement common Linux device driver interfaces, while +some of them are ESA/390 platform specific. get_dev_info_by_irq() / get_dev_info_by_devno() allow a device driver to determine the devices attached (visible) to the @@ -44,10 +43,17 @@ read_dev_chars() read device characteristics + +read_conf_data() + read configuration data. request_irq() obtain ownership for a specific device. +s390_request_irq_special() + obtain ownership for a specific device. Similar to request_irq(), but + allows for device not operational notification too. + free_irq() release ownership for a specific device. @@ -60,6 +66,9 @@ do_IO() initiate an I/O request. +resume_IO() + resume channel program execution. + halt_IO() terminate the current I/O request processed on the device. @@ -70,12 +79,14 @@ interrupt handler according to the rules (flags) defined during I/O request initiation with do_IO(). -The next chapters describe the functions, other than do_IRQ() in more details. +The next chapters describe the functions other than do_IRQ() in more details. The do_IRQ() interface is not described, as it is called from the Linux/390 first level interrupt handler only and does not comprise a device driver callable interface. Instead, the functional description of do_IO() also describes the input to the device specific interrupt handler. +Note: All explanations apply also to the 64 bit architecture s390x. + Common Device Support (CDS) for Linux/390 Device Drivers @@ -90,7 +101,7 @@ of them can be found on other Linux platforms implementations too. Miscellaneous function prototypes, data declarations, and macro definitions can be found in the architecture specific C header file -linux/arch/s390/kernel/irq.h. +linux/include/asm-s390/irq.h. Overview of CDS interface concepts @@ -106,7 +117,7 @@ single device is uniquely identified to the system by a so called subchannel, where the ESA/390 architecture allows for 64k devices be attached. -Linux, however was first built on the Intel PC architecture, with its two +Linux, however, was first built on the Intel PC architecture, with its two cascaded 8259 programmable interrupt controllers (PICs), that allow for a maximum of 15 different interrupt lines. All devices attached to such a system share those 15 interrupt levels. Devices attached to the ISA bus system must @@ -117,7 +128,7 @@ has to call every single device driver registered on this IRQ in order to determine the device driver owning the device that raised the interrupt. -In order to not introduce a new I/O concept to the common Linux code, +In order not to introduce a new I/O concept to the common Linux code, Linux/390 preserves the IRQ concept and semantically maps the ESA/390 subchannels to Linux as IRQs. This allows Linux/390 to support up to 64k different IRQs, uniquely representig a single device each. @@ -126,7 +137,7 @@ of those devices is uniquely defined by a so called subchannel by the ESA/390 channel subsystem. While the subchannel numbers are system generated, each subchannel also takes a user defined attribute, the so called device number. -Both, subchannel number and device number can not exceed 65535. The +Both subchannel number and device number can not exceed 65535. The init_IRQ() routine gathers the information about control unit type and device types that imply specific I/O commands (channel command words - CCWs) in order to operate the device. Device drivers can retrieve this set of hardware @@ -141,7 +152,7 @@ When a device driver has recognized a device it wants to claim ownership for, it calls request_irq() with the device's subchannel id serving as pseudo irq line. One of the required parameters it has to specify is dev_id, defining a -device status block, the CDS layer will use to notify the device driver's +device status block which the CDS layer will use to notify the device driver's interrupt handler about interrupt information observed. It depends on the device driver to properly handle those interrupts. @@ -169,6 +180,41 @@ +get_irq_first() / get_irq_next() - Retrieve Information about available IRQs + +A device driver can use those interface routines to retrieve information for +those IRQs only that have valid device information available. As +Linux for S/390 supports a maximum of 65535 subchannels (devices), it might +be a waste of CPU to scan for the max number of devices while a fraction is +available/usable only. get_irq_first() will retrieve the first usable IRQ. +Using this as input get_irq_next() will retrieve the next IRQ available, etc.. + +int get_irq_first( void ); +int get_irq_next( int irq ); + +irq - defines the subchannel to start scanning with. This must be + a valid subchannel or an error is returned. + +The get_irq_first() / get_irq_next() functions return: + +non-negative value - next available IRQ + -ENODEV - no more IRQs available + +Example : + + irq = get_irq_first(); + while ( irq != -ENODEV) + { + get_dev_info_by_irq( irq, &dinfo); + if ( dinfo.devno == devno_to_look_for + || dinfo.sid_data.cu_type == cu_type_to_look_for ) + { + do_some_action( irq, &dinfo ); + } /* endif */ + + irq = get_irq_next(irq); + } + get_dev_info_by_irq() / get_dev_info_by_devno() - Retrieve Device Information During system startup - init_IRQ() processing - the generic I/O device support @@ -176,60 +222,69 @@ SenseID information. For those devices supporting the command it also obtains extended SenseID information. -int get_dev_info_by_irq( int irq, - dev_info_t *devinfo); +int get_dev_info_by_irq( int irq, + s390_dev_info_t *pdi); -int get_dev_info_by_devno( unsigned int irq, - dev_info_t *devinfo); +int get_dev_info_by_devno( __u16 devno, + s390_dev_info_t *pdi); -irq - defines the subchannel, status information is to be +irq - defines the subchannel status information is to be returned for. devno - device number. -devinfo - pointer to a user buffer of type dev_info_t that should +pdi - pointer to a user buffer of type s390_dev_info_t that should be filled with device specific information. typedef struct { - unsigned int devno; /* device number */ - unsigned int status; /* device status */ - senseid_t sid_data; /* senseID data */ -} dev_info_t; + int irq; /* irq, aka. subchannel */ + __u16 devno; /* device number */ + unsigned int status; /* device status */ + senseid_t sid_data; /* senseID data */ +} s390_dev_info_t; +irq - subchannel. devno - device number as configured in the IOCDS. status - device status sid_data - data obtained by a SenseID call Possible status values are : -DEVSTAT_NOT_OPER - device was found not-operational. In this case - the caller should disregard the sid_data - buffer content. +DEVSTAT_NOT_OPER - device was found not-operational. In this case + the caller should disregard the sid_data + buffer content. +DEVSTAT_UNFRIENDLY_DEV - device is locked by someone else. The sid_data buffer + doesn't contain valid data. +DEVSTAT_UNKNOWN_DEV - The device is unknown, and the sid_data buffer doesn't + contain valid data. +DEVSTAT_DEVICE_OWNED - An interrupt handler is registered. // -// SenseID response buffer layout +// sense-id response buffer layout // typedef struct { /* common part */ - unsigned char reserved; /* always 0x'FF' */ - unsigned short cu_type; /* control unit type */ - unsigned char cu_model; /* control unit model */ - unsigned short dev_type; /* device type */ - unsigned char dev_model; /* device model */ - unsigned char unused; /* padding byte */ + __u8 reserved; /* always 0x'FF' */ + __u16 cu_type; /* control unit type */ + __u8 cu_model; /* control unit model */ + __u16 dev_type; /* device type */ + __u8 dev_model; /* device model */ + __u8 unused; /* padding byte */ /* extended part */ - ciw_t ciw[62]; /* variable # of CIWs */ -} senseid_t; + ciw_t ciw[MAX_CIWS]; /* variable # of CIWs */ +} __attribute__ ((packed,aligned(4))) senseid_t; + +MAX_CIWS is currently defined as 8. The ESA/390 I/O architecture defines certain device specific I/O functions. The device returns the device specific command code together with the SenseID data in so called Command Information Words (CIW) : typedef struct _ciw { - unsigned int et : 2; // entry type - unsigned int reserved : 2; // reserved - unsigned int ct : 4; // command type - unsigned int cmd : 8; // command - unsigned int count : 16; // count -} ciw_t; + __u32 et : 2; // entry type + __u32 reserved : 2; // reserved + __u32 ct : 4; // command type + __u32 cmd : 8; // command + __u32 count : 16; // count +} __attribute__ ((packed)) ciw_t; Possible CIW entry types are : @@ -245,6 +300,7 @@ -ENODEV - irq or devno don't specify a known subchannel or device number. -EINVAL - invalid devinfo value. +-EUSERS - device is locked by someone else. Usage Notes : @@ -252,7 +308,7 @@ calling get_dev_info() until it returns -ENODEV as there aren't any more available devices. -If a device driver wants to request ownership for a specific device it must +If a device driver wants to request ownership for a specific device, it must call request_irq() prior to be able to issue any I/O request for it, including above mentioned device dependent commands. @@ -269,7 +325,7 @@ numbers configured in the IOCDS. The following routines serve the purpose to convert irq values into device numbers and vice versa. -int get_irq_by_devno( unsigned int devno ); +int get_irq_by_devno( __u16 devno ); unsigned int get_devno_by_irq( int irq ); @@ -336,6 +392,41 @@ he specifies, or the buffer he wants to be allocated. +read_conf_data() - Read Configuration Data + +Retrieve the device dependent configuration data. Please have a look at your +device dependent I/O commands for the device specific layout of the node +descriptor elements. + +The function is meant to be called without an irq handler be in place. However, +the irq for the requested device must not be locked or this will cause a +deadlock situation ! + +The function may be called enabled or disabled. + +int read_conf_data( int irq, void **buffer, int *length, __u8 lpm); + +irq - Specifies the subchannel the configuration data is to be + retrieved for. +buffer - Pointer to a buffer pointer. The read_conf_data() routine + will allocate a buffer and initialize the buffer pointer + accordingly. It's the device driver's responsability to + release the kernel memory if no longer needed. +length - Length of the buffer allocated and retrieved. +lpm - Logical path mask to be used for retrieving the data. If + zero the data is retrieved on the next path available. + +The read_conf_data() function returns : + 0 - Successful completion +-ENODEV - irq doesn't specify a valid subchannel number +-EINVAL - An invalid parameter was detected +-EIO - An irrecoverable I/O error occured or the device is + not operational. +-ENOMEM - The read_conf_data() routine couldn't obtain storage. +-EOPNOTSUPP - The device doesn't support the read configuration + data command. + + request_irq() - Request Device Ownership As previously discussed a device driver will scan for the devices its supports @@ -343,6 +434,9 @@ request_irq() to request ownership for it. This call causes the subchannel to be enabled for interrupts if it was found operational. +Note: This function is obsolete and provided for compatibility purposes only. +Device drivers should use s390_request_irq_special() instead. + int request_irq( unsigned int irq, int (*handler)( int, void *, @@ -354,27 +448,28 @@ irq : specifies the subchannel the ownership is requested for handler : specifies the device driver's interrupt handler to be called for interrupt processing -irqflags : IRQ flags, must be 0 (zero) or SA_SAMPLE_RANDOM +irqflags : IRQ flags, currently ignored devname : device name dev_id : required pointer to a device specific buffer of type devstat_t typedef struct { - unsigned int devno; /* device number from irb */ - unsigned int intparm; /* interrupt parameter */ - unsigned char cstat; /* channel status - accumulated */ - unsigned char dstat; /* device status - accumulated */ - unsigned char lpum; /* last path used mask from irb */ - unsigned char unused; /* not used - reserved */ - unsigned int flag; /* flag : see below */ - unsigned long cpa; /* CCW addr from irb at prim. status */ - unsigned int rescnt; /* count from irb at primary status */ - unsigned int scnt; /* sense count, if available */ + __u16 devno; /* device number, aka. "cuu" from irb */ + unsigned long intparm; /* interrupt parameter */ + __u8 cstat; /* channel status - accumulated */ + __u8 dstat; /* device status - accumulated */ + __u8 lpum; /* last path used mask from irb */ + __u8 unused; /* not used - reserved */ + unsigned int flag; /* flag : see below */ + __u32 cpa; /* CCW address from irb at primary status */ + __u32 rescnt; /* res. count from irb at primary status */ + __u32 scnt; /* sense count, if DEVSTAT_FLAG_SENSE_AVAIL */ union { - irb_t irb; /* interruption response block */ - sense_t sense; /* sense information */ - } ii; /* interrupt information */ - } devstat_t; + irb_t irb; /* interruption response block */ + sense_t sense; /* sense information */ + } ii; /* interrupt information */ +} devstat_t; + During request_irq() processing, the devstat_t layout does not matter as it won't be used during request_irq() processing. See do_IO() for a functional @@ -396,9 +491,9 @@ layer, and the device specific driver. The value passed to request_irq() must therefore point to a valid devstat_t type buffer area the device driver must preserve for later usage. I.e. it must not be released prior to a call -to free_irq() +to free_irq(). -The only value parameter irqflags supports is SA_SAMPLE_RANDOM if appropriate. +Irqflags are currently ignored by the cds layer. The Linux/390 kernel does neither know about "fast" interrupt handlers, nor does it allow for interrupt sharing. Remember, the term interrupt level (irq), device, and subchannel are used interchangeably in Linux/390. @@ -416,6 +511,126 @@ therefore not rely on this parameter on function entry. +s390_request_irq_special() - Request Device Ownership + +As previously discussed a device driver will scan for the devices its supports +by calling get_dev_info(). Once it has found a device it will call +request_irq() to request ownership. + +Note: This function replaces request_irq() described previously. + +int s390_request_irq_special( + int irq, + io_handler_func_t io_handler, + not_oper_handler_func_t not_oper_handler, + unsigned long irqflags, + const char *devname, + void *dev_id); + +irq : specifies the subchannel the ownership is + requested for +io_handler : specifies the device driver's interrupt handler + to be called for interrupt processing +not_oper_handler : specifies a device driver "not operational" handler +irqflags : IRQ flags, currently ignored +devname : device name +dev_id : required pointer to a device specific buffer of + type devstat_t + + +typedef struct { + __u16 devno; /* device number, aka. "cuu" from irb */ + unsigned long intparm; /* interrupt parameter */ + __u8 cstat; /* channel status - accumulated */ + __u8 dstat; /* device status - accumulated */ + __u8 lpum; /* last path used mask from irb */ + __u8 unused; /* not used - reserved */ + unsigned int flag; /* flag : see below */ + __u32 cpa; /* CCW address from irb at primary status */ + __u32 rescnt; /* res. count from irb at primary status */ + __u32 scnt; /* sense count, if DEVSTAT_FLAG_SENSE_AVAIL */ + union { + irb_t irb; /* interruption response block */ + sense_t sense; /* sense information */ + } ii; /* interrupt information */ +} devstat_t; + +During request_irq() processing, the devstat_t layout does not matter as it +won't be used during request_irq() processing. See do_IO() for a functional +description of its usage. + +typedef void (* io_handler_func_t) ( int irq, + void *devstat, + struct pt_regs *rgs); + +irq : IRQ the interrupt handler is called for +devstat : device status block +rgs : obsolete + +typedef (void)(* not_oper_handler_func_t)( int irq, + int status ); + +irq : IRQ the not operational status has been encountered for +status : device status + DEVSTAT_NOT_OPER - device is not operational + DEVSTAT_REVALIDATE - revalidate device number + DEVSTAT_DEVICE_GONE - no such device (irq) + +Note: Revalidate indicates that running under VM the device number has been +modified by means of a DEFINE xxxx [as] yyyy command. Therewith device number +xxxx was altered to yyyy. It's the device drivers responsibility to decide +whether device ownership can be retained. + +Gone indicates that the device was detached under VM, or the device number +became invalid (native, LPAR). In order to prevent further I/O the IRQ was +implicitly freed on behalf of the device driver. The driver must not call +free_irq itself. + +Not Oper indicates the device became not operational. It's the device driver's +responsibility whether it wants to maintain ownership for the IRQ, or not. + +The s390_request_irq_special() function returns : + 0 - successful completion +-EINVAL - an invalid parameter was detected +-EBUSY - device (subchannel) already owned +-ENODEV - the device is not-operational +-ENOMEM - not enough kernel memory to process request + +Usage Notes : + +While Linux for Intel defines dev_id as a unique identifier for shared +interrupt lines, it has a totally different purpose on Linux for S/390. Here +it serves as a shared interrupt status area between the generic device support +layer and the device specific driver. The value passed to request_irq() must +therefore point to a valid devstat_t type buffer area the device driver must +preserve for later usage. I.e. it must not be released prior to a call to +free_irq(). + +Currently, the value of irqflags is ignored. The Linux for S/390 kernel does +neither know about "fast" interrupt handlers, nor does it allow for interrupt +sharing. Remember, the term interrupt level (irq), device, and subchannel are +used interchangeably in Linux for S/390. + +Other than request_irq(), this function does allow for a not operational +handler to be defined. This handler is called when a device either became not +operational, the last path to a device became not operational, or the device +was detached from the system. A detach could be a "detach" under VM or that +the device became unassigned by the Support Element (SE) or Hardware Management +Console (HMC). + +If s390_request_irq_special() was called in enabled state, or if multiple CPUs +are present, the device may present an interrupt to the specified handler prior +to request_irq() return to the caller already ! This includes the possibility +of unsolicited interrupts or a pending interrupt status from an earlier +solicited I/O request. The device driver must be able to handle this situation +properly or the device may become unoperational otherwise ! + +Although the interrupt handler is defined to be called with a pointer to a +struct pt_regs buffer area, this is not implemented by the Linux for S/390 +platform specific common I/O support layer. The device driver's interrupt +handler must therefore not rely on this parameter on function entry. + + free_irq() - Release Device Ownership A device driver may call free_irq() to release ownership of a previously @@ -523,19 +738,19 @@ int do_IO( int irq, ccw1_t *cpa, - unsigned long intparm, + unsigned long user_intparm, unsigned int lpm, unsigned long flag); -irq : irq (subchannel) the I/O request is destined for -cpa : logical start address of channel program -intparm : user specific interrupt information; will be presented - back to the device driver's interrupt handler. Allows a - device driver to associate the interrupt with a - particular I/O request. -lpm : defines the channel path to be used for a specific I/O - request. Valid with flag value DOIO_VALID_LPM only. -flag : defines the action to e parformed for I/O processing +irq : irq (subchannel) the I/O request is destined for +cpa : logical start address of channel program +user_intparm : user specific interrupt information; will be presented + back to the device driver's interrupt handler. Allows a + device driver to associate the interrupt with a + particular I/O request. +lpm : defines the channel path to be used for a specific I/O + request. Valid with flag value DOIO_VALID_LPM only. +flag : defines the action to e parformed for I/O processing Possible flag values are : @@ -543,16 +758,25 @@ DOIO_VALID_LPM - LPM input parameter is valid (see usage notes below for details) DOIO_WAIT_FOR_INTERRUPT - wait synchronously for final status +DOIO_TIMEOUT - perform a loop while waiting for final status + and fail after a timeout DOIO_REPORT_ALL - report all interrupt conditions +DOIO_ALLOW_SUSPEND - channel program may become suspended +DOIO_DENY_PREFETCH - don't allow for CCW prefetch; usually + this implies the channel program might + become modified +DOIO_CANCEL_ON_TIMEOUT - do a cancel_IO if there is a timeout waiting + for the channel program to finish (see usage + notes below for details) The cpa parameter points to the first format 1 CCW of a channel program : typedef struct { - char cmd_code; /* command code */ - char flags; /* flags, like IDA addressing, etc. */ - unsigned short count; /* byte count */ - void *cda; /* data address */ -} ccw1_t __attribute__ ((aligned(8))); + __u8 cmd_code;/* command code */ + __u8 flags; /* flags, like IDA adressing, etc. */ + __u16 count; /* byte count */ + __u32 cda; /* data address */ +} __attribute__ ((packed,aligned(8))) ccw1_t; with the following CCW flags values defined : @@ -605,6 +829,9 @@ successfully be started previously. DEVSTAT_FINAL_STATUS - This is a final interrupt status for the I/O requst identified by intparm. +DEVSTAT_PCI - A PCI was received. +DEVSTAT_SUSPENDED - A "suspended" intermediate status was + received. If device status DEVSTAT_FLAG_SENSE_AVAIL is indicated in field dev_id->flag, field dev_id->scnt describes the numer of device specific sense bytes @@ -612,9 +839,9 @@ driver itself is required. typedef struct { - unsigned char res[32]; /* reserved */ - unsigned char data[32]; /* sense data */ -} sense_t; + __u8 res[32]; /* reserved */ + __u8 data[32]; /* sense data */ + } __attribute__ ((packed)) sense_t; The device interrupt handler can use the following definitions to investigate the primary unit check source coded in sense byte 0 : @@ -625,6 +852,7 @@ SNS0_EQUIPMENT_CHECK 0x10 SNS0_DATA_CHECK 0x08 SNS0_OVERRUN 0x04 +SNS0_INCOMPL_DOMAIN 0x01 Depending on the device status, multiple of those values may be set together. Please refer to the device specific documentation for details. @@ -661,12 +889,10 @@ Usage Notes : -Prior to call do_IO() the device driver must - -assure disabled state, i.e. the I/O mask value in the PSW must be disabled. -This can be accomplished by calling __save_flags( flags). The current PSW -flags are preserved and can be restored by __restore_flags( flags) at a -later time. +Prior to call do_IO() the device driver must assure disabled state, i.e. the +I/O mask value in the PSW must be disabled. This can be accomplished by calling +__save_flags( flags). The current PSW flags are preserved and can be restored +by __restore_flags( flags) at a later time. If the device driver violates this rule while running in a uni-processor environment an interrupt might be presented prior to the do_IO() routine @@ -674,7 +900,7 @@ deadlock situation as the interrupt handler will try to obtain the irq lock the device driver still owns (see below) ! -the driver must assure to hold the device specific lock. This can be +The driver must assure to hold the device specific lock. This can be accomplished by (i) s390irq_spin_lock( irq), or @@ -705,6 +931,10 @@ is therewith primarily meant to be used during device driver initialization for ease of device setup. +If the device driver is using the DOIO_TIMEOUT parameter, it is a good idea +also to specify DOIO_CANCEL_ON_TIMEOUT. Otherwise, the failing channel program +may prevent the execution of any other channel program at the subchannel. + The lpm input parameter might be used for multipath devices shared among multiple systems as the Linux/390 CDS isn't grouping channel paths. Therefore its use might be required if multiple access paths to a device are available @@ -757,6 +987,34 @@ and dev_id->dstat that represent the accumulated subchannel and device status information gathered since do_IO() request initiation. +Channel programs that intend to set the suspend flag on a channel command word +(CCW) must start the I/O operation with the DOIO_ALLOW_SUSPEND option or the +suspend flag will cause a channel program check. At the time the channel program +becomes suspended an intermediate interrupt will be generated by the channel +subsystem. + +resume_IO() - Resume Channel Program Execution + +If a device driver chooses to suspend the current channel program execution by +setting the CCW suspend flag on a particular CCW, the channel program execution +is suspended. In order to resume channel program execution the CIO layer +provides the resume_IO() routine. + +int resume_IO( int irq); + +irq : irq (subchannel) the halt operation is requested for + +The resume_IO() function returns: + + 0 - suspended channel program is resumed +-EBUSY - status pending +-ENODEV - invalid or not-operational subchannel +-EINVAL - resume function not applicable +-ENOTCONN - there is no I/O request pending for completion + +Usage Notes: +Please have a look at the do_IO() usage notes for more details on suspended +channel programs. halt_IO() - Halt I/O Request Processing @@ -765,9 +1023,9 @@ a halt subchannel (HSCH) I/O command. For those purposes the halt_IO() command is provided. -int halt_IO( int irq, /* subchannel number */ - int intparm, /* dummy intparm */ - unsigned int flag); /* operation mode */ +int halt_IO( int irq, /* subchannel number */ + unsigned long intparm, /* dummy intparm */ + unsigned long flag); /* operation mode */ irq : irq (subchannel) the halt operation is requested for intparm : interruption parameter; value is only used if no I/O @@ -867,7 +1125,7 @@ reset_cons_dev - Reset Console Device This routine allows for resetting the console device specification. See -set_cons_dev() for details. +wait_cons_dev() for details. int reset_cons_dev( int irq); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/s390/CommonIO linux.21rc1-ac2/Documentation/s390/CommonIO --- linux.21rc1/Documentation/s390/CommonIO 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/s390/CommonIO 2003-04-25 13:50:29.000000000 +0100 @@ -21,7 +21,8 @@ Default is on. -* cio_ignore = , , ... +* cio_ignore = | , + | , ... The given device numbers will be ignored by the common I/O-layer; no detection and device sensing will be done on any of those devices. The subchannel to @@ -55,14 +56,22 @@ * /proc/subchannels - Shows for each subchannel - - device number - - device type/model and if applicable control unit type/model - - whether the device is in use - - path installed mask, path available mask, path operational mask and last - path used mask - - the channel path IDs (chpids) + This entry shows information on a per-subchannel basis. + The data is ordered in the following way: + + - device number + - subchannel number + - device type/model (if applicable; if not, this is empty) and control unit + type/model + - whether the device is in use (i. e. a device driver has requested ownership + and registered an interrupt handler) + - path installed mask (PIM), as reflected by last store subchannel + - path available mask (PAM), as reflected by last store subchannel + - path operational mask (POM), as reflected by last store subchannel + - the channel path IDs (CHPIDs) + + All fields are separated by spaces, the chpids are in blocks of four chpids. * /proc/deviceinfo/ @@ -137,3 +146,26 @@ This entry counts how many times s390_process_IRQ has been called for each CPU. This info is in /proc/interrupts on other architectures. + +* /proc/chpids + + This entry will only show up if you specified CONFIG_CHSC=y during kernel + config. + + This entry serves a dual purpose: + + - show which chpids are currently known to Linux and their status (online, + logically offline), + + - toggling known chpids logically online/offline. + + To toggle a known chpid logically offline, do an + echo off > /proc/chpids + is interpreted as hex, even if you omit the '0x'. + The chpid will be treated by Linux as if it were not online, which can mean + some devices will become unavailable. + + You can toggle a logically offline chpid online again by + echo on > /proc/chpids + If devices became unavailable by toggling the chpid logically offline, they + will become available again after you toggle the chpid online again. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/s390/Debugging390.txt linux.21rc1-ac2/Documentation/s390/Debugging390.txt --- linux.21rc1/Documentation/s390/Debugging390.txt 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/s390/Debugging390.txt 2003-04-25 13:50:29.000000000 +0100 @@ -238,7 +238,7 @@ On 390 our limitations & strengths make us slightly different. For backward compatibility ( because of the psw address hi bit which -indicates whether we are in 31 or 64 bit mode ) we are only allowed +indicates whether we are in 31 or 24 bit mode ) we are only allowed use 31 bits (2GB) of our 32 bit addresses. However, we use entirely separate address spaces for the user & kernel. @@ -1475,6 +1475,12 @@ D 00014CB4.20 V00014CB4 2F646576 2F636F6E 736F6C65 00001BF5 V00014CC4 FC00014C B4001001 E0001000 B8070707 + +Alternatively you can do the more elegant +D 0.20;BASE2 +BASE2 telling VM to use GPR2 as the base register. + + Now copy the text till the first 00 hex ( which is the end of the string to an xterm & do hex2ascii on it. hex2ascii 2F646576 2F636F6E 736F6C65 00 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/s390/TAPE linux.21rc1-ac2/Documentation/s390/TAPE --- linux.21rc1/Documentation/s390/TAPE 2003-04-22 16:38:59.000000000 +0100 +++ linux.21rc1-ac2/Documentation/s390/TAPE 1970-01-01 01:00:00.000000000 +0100 @@ -1,122 +0,0 @@ -Channel attached Tape device driver - ------------------------------WARNING----------------------------------------- -This driver is considered to be EXPERIMENTAL. Do NOT use it in -production environments. Feel free to test it and report problems back to us. ------------------------------------------------------------------------------ - -The LINUX for zSeries tape device driver manages channel attached tape drives -which are compatible to IBM 3480 or IBM 3490 magnetic tape subsystems. This -includes various models of these devices (for example the 3490E). - - -Tape driver features - -The device driver supports a maximum of 128 tape devices. -No official LINUX device major number is assigned to the zSeries tape device -driver. It allocates major numbers dynamically and reports them on system -startup. -Typically it will get major number 254 for both the character device front-end -and the block device front-end. - -The tape device driver needs no kernel parameters. All supported devices -present are detected on driver initialization at system startup or module load. -The devices detected are ordered by their subchannel numbers. The device with -the lowest subchannel number becomes device 0, the next one will be device 1 -and so on. - - -Tape character device front-end - -The usual way to read or write to the tape device is through the character -device front-end. The zSeries tape device driver provides two character devices -for each physical device -- the first of these will rewind automatically when -it is closed, the second will not rewind automatically. - -The character device nodes are named /dev/rtibm0 (rewinding) and /dev/ntibm0 -(non-rewinding) for the first device, /dev/rtibm1 and /dev/ntibm1 for the -second, and so on. - -The character device front-end can be used as any other LINUX tape device. You -can write to it and read from it using LINUX facilities such as GNU tar. The -tool mt can be used to perform control operations, such as rewinding the tape -or skipping a file. - -Most LINUX tape software should work with either tape character device. - - -Tape block device front-end - -The tape device may also be accessed as a block device in read-only mode. -This could be used for software installation in the same way as it is used with -other operation systems on the zSeries platform (and most LINUX -distributions are shipped on compact disk using ISO9660 filesystems). - -One block device node is provided for each physical device. These are named -/dev/btibm0 for the first device, /dev/btibm1 for the second and so on. -You should only use the ISO9660 filesystem on LINUX for zSeries tapes because -the physical tape devices cannot perform fast seeks and the ISO9660 system is -optimized for this situation. - - -Tape block device example - -In this example a tape with an ISO9660 filesystem is created using the first -tape device. ISO9660 filesystem support must be built into your system kernel -for this. -The mt command is used to issue tape commands and the mkisofs command to -create an ISO9660 filesystem: - -- create a LINUX directory (somedir) with the contents of the filesystem - mkdir somedir - cp contents somedir - -- insert a tape - -- ensure the tape is at the beginning - mt -f /dev/ntibm0 rewind - -- set the blocksize of the character driver. The blocksize 2048 bytes - is commonly used on ISO9660 CD-Roms - mt -f /dev/ntibm0 setblk 2048 - -- write the filesystem to the character device driver - mkisofs -o /dev/ntibm0 somedir - -- rewind the tape again - mt -f /dev/ntibm0 rewind - -- Now you can mount your new filesystem as a block device: - mount -t iso9660 -o ro,block=2048 /dev/btibm0 /mnt - -TODO List - - - Driver has to be stabelized still - -BUGS - -This driver is considered BETA, which means some weaknesses may still -be in it. -If an error occurs which cannot be handled by the code you will get a -sense-data dump.In that case please do the following: - -1. set the tape driver debug level to maximum: - echo 6 >/proc/s390dbf/tape/level - -2. re-perform the actions which produced the bug. (Hopefully the bug will - reappear.) - -3. get a snapshot from the debug-feature: - cat /proc/s390dbf/tape/hex_ascii >somefile - -4. Now put the snapshot together with a detailed description of the situation - that led to the bug: - - Which tool did you use? - - Which hardware do you have? - - Was your tape unit online? - - Is it a shared tape unit? - -5. Send an email with your bug report to: - mailto:Linux390@de.ibm.com - - diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/sched-coding.txt linux.21rc1-ac2/Documentation/sched-coding.txt --- linux.21rc1/Documentation/sched-coding.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/sched-coding.txt 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,129 @@ + Reference for various scheduler-related methods in the O(1) scheduler + Robert Love , MontaVista Software + + +Note most of these methods are local to kernel/sched.c - this is by design. +The scheduler is meant to be self-contained and abstracted away. This document +is primarily for understanding the scheduler, not interfacing to it. Some of +the discussed interfaces, however, are general process/scheduling methods. +They are typically defined in include/linux/sched.h. + + +Main Scheduling Methods +----------------------- + +void load_balance(runqueue_t *this_rq, int idle) + Attempts to pull tasks from one cpu to another to balance cpu usage, + if needed. This method is called explicitly if the runqueues are + inbalanced or periodically by the timer tick. Prior to calling, + the current runqueue must be locked and interrupts disabled. + +void schedule() + The main scheduling function. Upon return, the highest priority + process will be active. + + +Locking +------- + +Each runqueue has its own lock, rq->lock. When multiple runqueues need +to be locked, lock acquires must be ordered by ascending &runqueue value. + +A specific runqueue is locked via + + task_rq_lock(task_t pid, unsigned long *flags) + +which disables preemption, disables interrupts, and locks the runqueue pid is +running on. Likewise, + + task_rq_unlock(task_t pid, unsigned long *flags) + +unlocks the runqueue pid is running on, restores interrupts to their previous +state, and reenables preemption. + +The routines + + double_rq_lock(runqueue_t *rq1, runqueue_t *rq2) + +and + + double_rq_unlock(runqueue_t *rq1, runqueue_t rq2) + +safely lock and unlock, respectively, the two specified runqueues. They do +not, however, disable and restore interrupts. Users are required to do so +manually before and after calls. + + +Values +------ + +MAX_PRIO + The maximum priority of the system, stored in the task as task->prio. + Lower priorities are higher. Normal (non-RT) priorities range from + MAX_RT_PRIO to (MAX_PRIO - 1). +MAX_RT_PRIO + The maximum real-time priority of the system. Valid RT priorities + range from 0 to (MAX_RT_PRIO - 1). +MAX_USER_RT_PRIO + The maximum real-time priority that is exported to user-space. Should + always be equal to or less than MAX_RT_PRIO. Setting it less allows + kernel threads to have higher priorities than any user-space task. +MIN_TIMESLICE +MAX_TIMESLICE + Respectively, the minimum and maximum timeslices (quanta) of a process. + +Data +---- + +struct runqueue + The main per-CPU runqueue data structure. +struct task_struct + The main per-process data structure. + + +General Methods +--------------- + +cpu_rq(cpu) + Returns the runqueue of the specified cpu. +this_rq() + Returns the runqueue of the current cpu. +task_rq(task) + Returns the runqueue which holds the specified task. +cpu_curr(cpu) + Returns the task currently running on the given cpu. +rt_task(task) + Returns true if task is real-time, false if not. +task_cpu(task) + + +Process Control Methods +----------------------- + +void set_user_nice(task_t *p, long nice) + Sets the "nice" value of task p to the given value. +int setscheduler(pid_t pid, int policy, struct sched_param *param) + Sets the scheduling policy and parameters for the given pid. +void set_cpus_allowed(task_t *p, unsigned long new_mask) + Sets a given task's CPU affinity and migrates it to a proper cpu. + Callers must have a valid reference to the task and assure the + task not exit prematurely. No locks can be held during the call. +set_task_state(tsk, state_value) + Sets the given task's state to the given value. +set_current_state(state_value) + Sets the current task's state to the given value. +void set_tsk_need_resched(struct task_struct *tsk) + Sets need_resched in the given task. +void clear_tsk_need_resched(struct task_struct *tsk) + Clears need_resched in the given task. +void set_need_resched() + Sets need_resched in the current task. +void set_task_cpu(task, cpu) + Sets task->cpu to cpu on SMP. Noop on UP. +void clear_need_resched() + Clears need_resched in the current task. +int need_resched() + Returns true if need_resched is set in the current task, false + otherwise. +yield() + Place the current process at the end of the runqueue and call schedule. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/sched-design.txt linux.21rc1-ac2/Documentation/sched-design.txt --- linux.21rc1/Documentation/sched-design.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/sched-design.txt 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,165 @@ + Goals, Design and Implementation of the + new ultra-scalable O(1) scheduler + + + This is an edited version of an email Ingo Molnar sent to + lkml on 4 Jan 2002. It describes the goals, design, and + implementation of Ingo's new ultra-scalable O(1) scheduler. + Last Updated: 18 April 2002. + + +Goal +==== + +The main goal of the new scheduler is to keep all the good things we know +and love about the current Linux scheduler: + + - good interactive performance even during high load: if the user + types or clicks then the system must react instantly and must execute + the user tasks smoothly, even during considerable background load. + + - good scheduling/wakeup performance with 1-2 runnable processes. + + - fairness: no process should stay without any timeslice for any + unreasonable amount of time. No process should get an unjustly high + amount of CPU time. + + - priorities: less important tasks can be started with lower priority, + more important tasks with higher priority. + + - SMP efficiency: no CPU should stay idle if there is work to do. + + - SMP affinity: processes which run on one CPU should stay affine to + that CPU. Processes should not bounce between CPUs too frequently. + + - plus additional scheduler features: RT scheduling, CPU binding. + +and the goal is also to add a few new things: + + - fully O(1) scheduling. Are you tired of the recalculation loop + blowing the L1 cache away every now and then? Do you think the goodness + loop is taking a bit too long to finish if there are lots of runnable + processes? This new scheduler takes no prisoners: wakeup(), schedule(), + the timer interrupt are all O(1) algorithms. There is no recalculation + loop. There is no goodness loop either. + + - 'perfect' SMP scalability. With the new scheduler there is no 'big' + runqueue_lock anymore - it's all per-CPU runqueues and locks - two + tasks on two separate CPUs can wake up, schedule and context-switch + completely in parallel, without any interlocking. All + scheduling-relevant data is structured for maximum scalability. + + - better SMP affinity. The old scheduler has a particular weakness that + causes the random bouncing of tasks between CPUs if/when higher + priority/interactive tasks, this was observed and reported by many + people. The reason is that the timeslice recalculation loop first needs + every currently running task to consume its timeslice. But when this + happens on eg. an 8-way system, then this property starves an + increasing number of CPUs from executing any process. Once the last + task that has a timeslice left has finished using up that timeslice, + the recalculation loop is triggered and other CPUs can start executing + tasks again - after having idled around for a number of timer ticks. + The more CPUs, the worse this effect. + + Furthermore, this same effect causes the bouncing effect as well: + whenever there is such a 'timeslice squeeze' of the global runqueue, + idle processors start executing tasks which are not affine to that CPU. + (because the affine tasks have finished off their timeslices already.) + + The new scheduler solves this problem by distributing timeslices on a + per-CPU basis, without having any global synchronization or + recalculation. + + - batch scheduling. A significant proportion of computing-intensive tasks + benefit from batch-scheduling, where timeslices are long and processes + are roundrobin scheduled. The new scheduler does such batch-scheduling + of the lowest priority tasks - so nice +19 jobs will get + 'batch-scheduled' automatically. With this scheduler, nice +19 jobs are + in essence SCHED_IDLE, from an interactiveness point of view. + + - handle extreme loads more smoothly, without breakdown and scheduling + storms. + + - O(1) RT scheduling. For those RT folks who are paranoid about the + O(nr_running) property of the goodness loop and the recalculation loop. + + - run fork()ed children before the parent. Andrea has pointed out the + advantages of this a few months ago, but patches for this feature + do not work with the old scheduler as well as they should, + because idle processes often steal the new child before the fork()ing + CPU gets to execute it. + + +Design +====== + +the core of the new scheduler are the following mechanizms: + + - *two*, priority-ordered 'priority arrays' per CPU. There is an 'active' + array and an 'expired' array. The active array contains all tasks that + are affine to this CPU and have timeslices left. The expired array + contains all tasks which have used up their timeslices - but this array + is kept sorted as well. The active and expired array is not accessed + directly, it's accessed through two pointers in the per-CPU runqueue + structure. If all active tasks are used up then we 'switch' the two + pointers and from now on the ready-to-go (former-) expired array is the + active array - and the empty active array serves as the new collector + for expired tasks. + + - there is a 64-bit bitmap cache for array indices. Finding the highest + priority task is thus a matter of two x86 BSFL bit-search instructions. + +the split-array solution enables us to have an arbitrary number of active +and expired tasks, and the recalculation of timeslices can be done +immediately when the timeslice expires. Because the arrays are always +access through the pointers in the runqueue, switching the two arrays can +be done very quickly. + +this is a hybride priority-list approach coupled with roundrobin +scheduling and the array-switch method of distributing timeslices. + + - there is a per-task 'load estimator'. + +one of the toughest things to get right is good interactive feel during +heavy system load. While playing with various scheduler variants i found +that the best interactive feel is achieved not by 'boosting' interactive +tasks, but by 'punishing' tasks that want to use more CPU time than there +is available. This method is also much easier to do in an O(1) fashion. + +to establish the actual 'load' the task contributes to the system, a +complex-looking but pretty accurate method is used: there is a 4-entry +'history' ringbuffer of the task's activities during the last 4 seconds. +This ringbuffer is operated without much overhead. The entries tell the +scheduler a pretty accurate load-history of the task: has it used up more +CPU time or less during the past N seconds. [the size '4' and the interval +of 4x 1 seconds was found by lots of experimentation - this part is +flexible and can be changed in both directions.] + +the penalty a task gets for generating more load than the CPU can handle +is a priority decrease - there is a maximum amount to this penalty +relative to their static priority, so even fully CPU-bound tasks will +observe each other's priorities, and will share the CPU accordingly. + +the SMP load-balancer can be extended/switched with additional parallel +computing and cache hierarchy concepts: NUMA scheduling, multi-core CPUs +can be supported easily by changing the load-balancer. Right now it's +tuned for my SMP systems. + +i skipped the prev->mm == next->mm advantage - no workload i know of shows +any sensitivity to this. It can be added back by sacrificing O(1) +schedule() [the current and one-lower priority list can be searched for a +that->mm == current->mm condition], but costs a fair number of cycles +during a number of important workloads, so i wanted to avoid this as much +as possible. + +- the SMP idle-task startup code was still racy and the new scheduler +triggered this. So i streamlined the idle-setup code a bit. We do not call +into schedule() before all processors have started up fully and all idle +threads are in place. + +- the patch also cleans up a number of aspects of sched.c - moves code +into other areas of the kernel where it's appropriate, and simplifies +certain code paths and data constructs. As a result, the new scheduler's +code is smaller than the old one. + + Ingo diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/Documentation/vm/overcommit-accounting linux.21rc1-ac2/Documentation/vm/overcommit-accounting --- linux.21rc1/Documentation/vm/overcommit-accounting 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/Documentation/vm/overcommit-accounting 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,70 @@ +* This describes the overcommit management facility in the latest kernel + tree (FIXME: actually it also describes the stuff that isnt yet done) + +The Linux kernel supports four overcommit handling modes + +0 - Heuristic overcommit handling. Obvious overcommits of + address space are refused. Used for a typical system. It + ensures a seriously wild allocation fails while allowing + overcommit to reduce swap usage + +1 - No overcommit handling. Appropriate for some scientific + applications + +2 - (NEW) strict overcommit. The total address space commit + for the system is not permitted to exceed swap + half ram. + In almost all situations this means a process will not be + killed while accessing pages but only by malloc failures + that are reported back by the kernel mmap/brk code. + +3 - (NEW) paranoid overcommit The total address space commit + for the system is not permitted to exceed swap. The machine + will never kill a process accessing pages it has mapped + except due to a bug (ie report it!) + +Gotchas +------- + +The C language stack growth does an implicit mremap. If you want absolute +guarantees and run close to the edge you MUST mmap your stack for the +largest size you think you will need. For typical stack usage is does +not matter much but its a corner case if you really really care + +In modes 2 and 3 the MAP_NORESERVE flag is ignored. + + +How It Works +------------ + +The overcommit is based on the following rules + +For a file backed map + SHARED or READ-only - 0 cost (the file is the map not swap) + PRIVATE WRITABLE - size of mapping per instance + +For an anonymous or /dev/zero map + SHARED - size of mapping + PRIVATE READ-only - 0 cost (but of little use) + PRIVATE WRITABLE - size of mapping per instance + +Additional accounting + Pages made writable copies by mmap + shmfs memory drawn from the same pool + +Status +------ + +o We account mmap memory mappings +o We account mprotect changes in commit +o We account mremap changes in size +o We account brk +o We account munmap +o We report the commit status in /proc +o Account and check on fork +o Review stack handling/building on exec +o SHMfs accounting +o Implement actual limit enforcement + +To Do +----- +o Account ptrace pages (this is hard) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/block/cciss.c linux.21rc1-ac2/drivers/block/cciss.c --- linux.21rc1/drivers/block/cciss.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/block/cciss.c 2003-04-25 13:32:34.000000000 +0100 @@ -44,12 +44,12 @@ #include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "HP CISS Driver (v 2.4.42)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,42) +#define DRIVER_NAME "HP CISS Driver (v 2.4.44)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,44) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Charles M. White III - Hewlett-Packard Company"); -MODULE_DESCRIPTION("Driver for HP SA5xxx SA6xxx Controllers version 2.4.42"); +MODULE_DESCRIPTION("Driver for HP SA5xxx SA6xxx Controllers version 2.4.44"); MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"); MODULE_LICENSE("GPL"); @@ -73,6 +73,8 @@ 0x0E11, 0x409B, 0, 0, 0}, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x409C, 0, 0, 0}, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, + 0x0E11, 0x409D, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); @@ -90,11 +92,13 @@ { 0x40830E11, "Smart Array 5312", &SA5B_access}, { 0x409A0E11, "Smart Array 641", &SA5_access}, { 0x409B0E11, "Smart Array 642", &SA5_access}, - { 0x409C0E11, "Smart Array 6400", &SA5_access}, + { 0x409C0E11, "Smart Array 6402", &SA5_access}, + { 0x409C0E11, "Smart Array 6404/256", &SA5_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ -#define MAX_CONFIG_WAIT 1000 +#define MAX_CONFIG_WAIT 30000 +#define MAX_IOCTL_CONFIG_WAIT 1000 /*define how many times we will try a command because of bus resets */ #define MAX_CMD_RETRIES 3 @@ -103,7 +107,7 @@ #define NR_CMDS 128 /* #commands that can be outstanding */ #define MAX_CTLR 8 -#define CCISS_DMA_MASK 0xFFFFFFFF /* 32 bit DMA */ +#define CCISS_DMA_MASK 0xFFFFFFFFFFFFFFFF /* 64 bit DMA */ static ctlr_info_t *hba[MAX_CTLR]; @@ -578,7 +582,7 @@ &(c->cfgtable->HostWrite.CoalIntCount)); writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); - for(i=0;ivaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) break; @@ -586,8 +590,11 @@ udelay(1000); } spin_unlock_irqrestore(&io_request_lock, flags); - if (i >= MAX_CONFIG_WAIT) - return -EFAULT; + if (i >= MAX_IOCTL_CONFIG_WAIT) + /* there is an unlikely case where this can happen, + * involving hot replacing a failed 144 GB drive in a + * RAID 5 set just as we attempt this ioctl. */ + return -EAGAIN; return 0; } case CCISS_GETNODENAME: @@ -627,7 +634,7 @@ writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); - for(i=0;ivaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) break; @@ -635,8 +642,11 @@ udelay(1000); } spin_unlock_irqrestore(&io_request_lock, flags); - if (i >= MAX_CONFIG_WAIT) - return -EFAULT; + if (i >= MAX_IOCTL_CONFIG_WAIT) + /* there is an unlikely case where this can happen, + * involving hot replacing a failed 144 GB drive in a + * RAID 5 set just as we attempt this ioctl. */ + return -EAGAIN; return 0; } @@ -1345,15 +1355,7 @@ sizeof(ReportLunData_struct), 0, 0, 0 ); if (return_code == IO_OK) { - /* printk("LUN Data\n--------------------------\n"); */ - listlength |= (0xff & - (unsigned int)(ld_buff->LUNListLength[0])) << 24; - listlength |= (0xff & - (unsigned int)(ld_buff->LUNListLength[1])) << 16; - listlength |= (0xff & - (unsigned int)(ld_buff->LUNListLength[2])) << 8; - listlength |= 0xff & - (unsigned int)(ld_buff->LUNListLength[3]); + listlength = be32_to_cpu(*((__u32 *) &ld_buff->LUNListLength[0])); } else { /* reading number of logical volumes failed */ printk(KERN_WARNING "cciss: report logical volume" @@ -2435,25 +2437,54 @@ c->io_mem_addr = 0; c->io_mem_length = 0; } +static int find_PCI_BAR_index(struct pci_dev *pdev, + unsigned long pci_bar_addr) +{ + int i, offset, mem_type, bar_type; + if (pci_bar_addr == PCI_BASE_ADDRESS_0) /* looking for BAR zero? */ + return 0; + offset = 0; + for (i=0; ivendor; device_id = pdev->device; irq = pdev->irq; - for(i=0; i<6; i++) - addr[i] = pdev->resource[i].start; - if (pci_enable_device(pdev)) { printk(KERN_ERR "cciss: Unable to Enable PCI device\n"); return -1; @@ -2479,12 +2510,12 @@ return -1; } /* search for our IO range so we can protect it */ - for (i=0; i<6; i++) { + for (i=0; iresource[i].flags & 0x01) { - c->io_mem_addr = pdev->resource[i].start; - c->io_mem_length = pdev->resource[i].end - - pdev->resource[i].start +1; + if (pci_resource_flags(pdev, i) & 0x01) { + c->io_mem_addr = pci_resource_start(pdev, i); + c->io_mem_length = pci_resource_end(pdev, i) - + pci_resource_start(pdev, i) + 1; #ifdef CCISS_DEBUG printk("IO value found base_addr[%d] %lx %lx\n", i, c->io_mem_addr, c->io_mem_length); @@ -2508,7 +2539,7 @@ printk("device_id = %x\n", device_id); printk("command = %x\n", command); for(i=0; i<6; i++) - printk("addr[%d] = %x\n", i, addr[i]); + printk("addr[%d] = %x\n", i, pci_resource_start(pdev, i); printk("revision = %x\n", revision); printk("irq = %x\n", irq); printk("cache_line_size = %x\n", cache_line_size); @@ -2523,7 +2554,7 @@ * table */ - c->paddr = addr[0] ; /* addressing mode bits already removed */ + c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */ #ifdef CCISS_DEBUG printk("address 0 = %x\n", c->paddr); #endif /* CCISS_DEBUG */ @@ -2532,21 +2563,27 @@ /* get the address index number */ cfg_base_addr = readl(c->vaddr + SA5_CTCFG_OFFSET); /* I am not prepared to deal with a 64 bit address value */ - cfg_base_addr &= 0xffff; + cfg_base_addr &= (__u32) 0x0000ffff; #ifdef CCISS_DEBUG printk("cfg base address = %x\n", cfg_base_addr); #endif /* CCISS_DEBUG */ - cfg_base_addr_index = (cfg_base_addr - PCI_BASE_ADDRESS_0)/4; + cfg_base_addr_index = + find_PCI_BAR_index(pdev, cfg_base_addr); #ifdef CCISS_DEBUG printk("cfg base address index = %x\n", cfg_base_addr_index); #endif /* CCISS_DEBUG */ + if (cfg_base_addr_index == -1) { + printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n"); + release_io_mem(hba[i]); + return -1; + } cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET); #ifdef CCISS_DEBUG printk("cfg offset = %x\n", cfg_offset); #endif /* CCISS_DEBUG */ c->cfgtable = (CfgTable_struct *) - remap_pci_mem((addr[cfg_base_addr_index] & 0xfffffff0) + remap_pci_mem(pci_resource_start(pdev, cfg_base_addr_index) + cfg_offset, sizeof(CfgTable_struct)); c->board_id = board_id; @@ -2583,11 +2620,17 @@ &(c->cfgtable->HostWrite.TransportRequest)); writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + /* Here, we wait, possibly for a long time, (4 secs or more). + * In some unlikely cases, (e.g. A failed 144 GB drive in a + * RAID 5 set was hot replaced just as we're coming in here) it + * can take that long. Normally (almost always) we will wait + * less than 1 sec. */ for(i=0;ivaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) break; /* delay and try again */ - udelay(1000); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); } #ifdef CCISS_DEBUG @@ -2661,10 +2704,7 @@ printk("LUN Data\n--------------------------\n"); #endif /* CCISS_DEBUG */ - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; - listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; - listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); + listlength = be32_to_cpu(*((__u32 *) &ld_buff->LUNListLength[0])); } else { /* reading number of logical volumes failed */ printk(KERN_WARNING "cciss: report logical volume" " command failed\n"); @@ -2834,17 +2874,6 @@ hba[i]->ctlr = i; hba[i]->pdev = pdev; - /* configure PCI DMA stuff */ - if (!pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) - printk("cciss: using DAC cycles\n"); - else if (!pci_set_dma_mask(pdev, (u64) 0xffffffff)) - printk("cciss: not using DAC cycles\n"); - else { - printk("cciss: no suitable DMA available\n"); - free_hba(i); - return -ENODEV; - } - if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &cciss_fops)) { printk(KERN_ERR "cciss: Unable to get major number " "%d for %s\n", MAJOR_NR+i, hba[i]->devname); @@ -3021,12 +3050,8 @@ printk(KERN_INFO DRIVER_NAME "\n"); /* Register for out PCI devices */ - if (pci_register_driver(&cciss_pci_driver) > 0 ) - return 0; - else - return -ENODEV; - - } + return pci_module_init(&cciss_pci_driver); +} EXPORT_NO_SYMBOLS; static int __init init_cciss_module(void) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/block/cciss.h linux.21rc1-ac2/drivers/block/cciss.h --- linux.21rc1/drivers/block/cciss.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/block/cciss.h 2003-04-25 16:05:08.000000000 +0100 @@ -45,8 +45,8 @@ char firm_ver[4]; // Firmware version struct pci_dev *pdev; __u32 board_id; - ulong vaddr; - __u32 paddr; + unsigned long vaddr; + unsigned long paddr; unsigned long io_mem_addr; unsigned long io_mem_length; CfgTable_struct *cfgtable; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/block/DAC960.c linux.21rc1-ac2/drivers/block/DAC960.c --- linux.21rc1/drivers/block/DAC960.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/block/DAC960.c 2003-04-22 16:44:36.000000000 +0100 @@ -1133,6 +1133,26 @@ DAC960PU/PD/PL 3.51 and above DAC960PU/PD/PL/P 2.73 and above */ +#if defined(__alpha__) + /* + DEC Alpha machines were often equipped with DAC960 cards that were + OEMed from Mylex, and had their own custom firmware. Version 2.70, + the last custom FW revision to be released by DEC for these older + controllers, appears to work quite well with this driver. + + Cards tested successfully were several versions each of the PD and + PU, called by DEC the KZPSC and KZPAC, respectively, and having + the Manufacturer Numbers (from Mylex), usually on a sticker on the + back of the board, of: + + KZPSC D040347 (1ch) or D040348 (2ch) or D040349 (3ch) + KZPAC D040395 (1ch) or D040396 (2ch) or D040397 (3ch) + */ +# define FIRMWARE_27x "2.70" +#else +# define FIRMWARE_27x "2.73" +#endif + if (Enquiry2.FirmwareID.MajorVersion == 0) { Enquiry2.FirmwareID.MajorVersion = @@ -1152,7 +1172,7 @@ (Controller->FirmwareVersion[0] == '3' && strcmp(Controller->FirmwareVersion, "3.51") >= 0) || (Controller->FirmwareVersion[0] == '2' && - strcmp(Controller->FirmwareVersion, "2.73") >= 0))) + strcmp(Controller->FirmwareVersion, FIRMWARE_27x) >= 0))) { DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION"); DAC960_Error("Firmware Version = '%s'\n", Controller, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/block/elevator.c linux.21rc1-ac2/drivers/block/elevator.c --- linux.21rc1/drivers/block/elevator.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/block/elevator.c 2003-04-22 16:44:36.000000000 +0100 @@ -27,6 +27,25 @@ #include #include + +static int compatible(struct request *req, struct buffer_head *bh, + request_queue_t *q, int rw, + int count, int max_sectors) +{ + if (q->head_active) + return 0; + if (req->waiting) + return 0; + if (req->rq_dev != bh->b_rdev) + return 0; + if (req->cmd != rw) + return 0; + if (req->nr_sectors + count > max_sectors) + return 0; + return 1; +} + + /* * This is a bit tricky. It's given that bh and rq are for the same * device, but the next request might of course not be. Run through @@ -83,22 +102,38 @@ struct list_head *entry = &q->queue_head; unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE; struct request *__rq; - int backmerge_only = 0; + + /* + * Quick one-entry cache of last merge + * nb. we do no latency accounting this way. + */ + + if (q->last_request) { + struct request *__rq = q->last_request; - while (!backmerge_only && (entry = entry->prev) != head) { + if (compatible(__rq, bh, q, rw, count, max_sectors)) { + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) { + *req = __rq; + return ELEVATOR_BACK_MERGE; + } + } + } + + + while ((entry = entry->prev) != head) { __rq = blkdev_entry_to_request(entry); /* * we can't insert beyond a zero sequence point */ if (__rq->elevator_sequence <= 0) - backmerge_only = 1; + break; if (__rq->waiting) continue; if (__rq->rq_dev != bh->b_rdev) continue; - if (!*req && bh_rq_in_between(bh, __rq, &q->queue_head) && !backmerge_only) + if (!*req && bh_rq_in_between(bh, __rq, &q->queue_head)) *req = __rq; if (__rq->cmd != rw) continue; @@ -108,7 +143,7 @@ ret = ELEVATOR_BACK_MERGE; *req = __rq; break; - } else if (__rq->sector - count == bh->b_rsector && !backmerge_only) { + } else if (__rq->sector - count == bh->b_rsector) { ret = ELEVATOR_FRONT_MERGE; __rq->elevator_sequence--; *req = __rq; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/block/ll_rw_blk.c linux.21rc1-ac2/drivers/block/ll_rw_blk.c --- linux.21rc1/drivers/block/ll_rw_blk.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/block/ll_rw_blk.c 2003-04-22 17:01:40.000000000 +0100 @@ -360,8 +360,12 @@ { if (q->plugged) { q->plugged = 0; - if (!list_empty(&q->queue_head)) + if (!list_empty(&q->queue_head)) { + if (q->last_request == + blkdev_entry_next_request(&q->queue_head)) + q->last_request = NULL; q->request_fn(q); + } } } @@ -491,6 +495,7 @@ q->plug_tq.routine = &generic_unplug_device; q->plug_tq.data = q; q->plugged = 0; + q->last_request = NULL; /* * These booleans describe the queue properties. We set the * default (and most common) values here. Other drivers can @@ -810,6 +815,7 @@ * inserted at elevator_merge time */ list_add(&req->queue, insert_here); + q->last_request = req; } /* @@ -829,6 +835,9 @@ */ if (q) { list_add(&req->queue, &q->rq[rw].free); + if (q->last_request == req) + q->last_request = NULL; + if (++q->rq[rw].count >= q->batch_requests && waitqueue_active(&q->wait_for_requests[rw])) wake_up(&q->wait_for_requests[rw]); @@ -867,6 +876,7 @@ req->bhtail = next->bhtail; req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors; list_del(&next->queue); + q->last_request = req; /* One last thing: we have removed a request, so we now have one less expected IO to complete for accounting purposes. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/cdrom/cdu31a.c linux.21rc1-ac2/drivers/cdrom/cdu31a.c --- linux.21rc1/drivers/cdrom/cdu31a.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/cdrom/cdu31a.c 2003-04-22 16:44:36.000000000 +0100 @@ -1361,6 +1361,8 @@ res_reg[0] = 0; res_reg[1] = 0; *res_size = 0; + /* Make sure that bytesleft doesn't exceed the buffer size */ + if (nblocks > 4) nblocks = 4; bytesleft = nblocks * 512; offset = 0; @@ -1384,9 +1386,9 @@ readahead_buffer + (2048 - readahead_dataleft), readahead_dataleft); - readahead_dataleft = 0; bytesleft -= readahead_dataleft; offset += readahead_dataleft; + readahead_dataleft = 0; } else { /* The readahead will fill the whole buffer, get the data and return. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/agp/agpgart_be.c linux.21rc1-ac2/drivers/char/agp/agpgart_be.c --- linux.21rc1/drivers/char/agp/agpgart_be.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/agp/agpgart_be.c 2003-04-25 13:30:01.000000000 +0100 @@ -23,6 +23,12 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ + +/* + * Intel(R) 855GM/852GM and 865G support, added by + * David Dawes . + */ + #include #include #include @@ -577,7 +583,7 @@ for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) SetPageReserved(page); - agp_bridge.gatt_table_real = (unsigned long *) table; + agp_bridge.gatt_table_real = (u32 *) table; agp_gatt_table = (void *)table; #ifdef CONFIG_X86 err = change_page_attr(virt_to_page(table), 1<device != PCI_DEVICE_ID_INTEL_830_M_0 && + agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_845_G_0) { + switch (gmch_ctrl & I855_GMCH_GMS_MASK) { + case I855_GMCH_GMS_STOLEN_1M: + gtt_entries = MB(1) - KB(132); + break; + case I855_GMCH_GMS_STOLEN_4M: + gtt_entries = MB(4) - KB(132); + break; + case I855_GMCH_GMS_STOLEN_8M: + gtt_entries = MB(8) - KB(132); + break; + case I855_GMCH_GMS_STOLEN_16M: + gtt_entries = MB(16) - KB(132); + break; + case I855_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(132); + break; + default: + gtt_entries = 0; + break; + } + } else + { + switch (gmch_ctrl & I830_GMCH_GMS_MASK) { + case I830_GMCH_GMS_STOLEN_512: + gtt_entries = KB(512) - KB(132); + break; + case I830_GMCH_GMS_STOLEN_1024: + gtt_entries = MB(1) - KB(132); + break; + case I830_GMCH_GMS_STOLEN_8192: + gtt_entries = MB(8) - KB(132); + break; + case I830_GMCH_GMS_LOCAL: + rdct = INREG8(intel_i830_private.registers, + I830_RDRAM_CHANNEL_TYPE); + gtt_entries = (I830_RDRAM_ND(rdct) + 1) * + MB(ddt[I830_RDRAM_DDT(rdct)]); + local = 1; + break; + default: + gtt_entries = 0; + break; + } } + if (gtt_entries > 0) + printk(KERN_INFO PFX "Detected %dK %s memory.\n", + gtt_entries / KB(1), local ? "local" : "stolen"); + else + printk(KERN_INFO PFX + "No pre-allocated video memory detected.\n"); gtt_entries /= KB(4); intel_i830_private.gtt_entries = gtt_entries; @@ -1192,9 +1228,16 @@ u16 gmch_ctrl; aper_size_info_fixed *values; - pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); values = A_SIZE_FIX(agp_bridge.aperture_sizes); + if (agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_830_M_0 && + agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_845_G_0) { + agp_bridge.previous_size = agp_bridge.current_size = (void *) values; + agp_bridge.aperture_size_idx = 0; + return(values[0].size); + } + + pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl); if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { agp_bridge.previous_size = agp_bridge.current_size = (void *) values; agp_bridge.aperture_size_idx = 0; @@ -2450,8 +2493,8 @@ return retval; } - agp_bridge.gatt_table_real = page_dir.real; - agp_bridge.gatt_table = page_dir.remapped; + agp_bridge.gatt_table_real = (u32 *)page_dir.real; + agp_bridge.gatt_table = (u32 *)page_dir.remapped; agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real); /* Get the address for the gart region. @@ -2477,8 +2520,8 @@ { amd_page_map page_dir; - page_dir.real = agp_bridge.gatt_table_real; - page_dir.remapped = agp_bridge.gatt_table; + page_dir.real = (unsigned long *)agp_bridge.gatt_table_real; + page_dir.remapped = (unsigned long *)agp_bridge.gatt_table; amd_free_gatt_pages(); amd_free_page_map(&page_dir); @@ -3559,8 +3602,8 @@ return retval; } - agp_bridge.gatt_table_real = page_dir.real; - agp_bridge.gatt_table = page_dir.remapped; + agp_bridge.gatt_table_real = (u32 *)page_dir.real; + agp_bridge.gatt_table = (u32 *)page_dir.remapped; agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real); /* Get the address for the gart region. @@ -3588,8 +3631,8 @@ { serverworks_page_map page_dir; - page_dir.real = agp_bridge.gatt_table_real; - page_dir.remapped = agp_bridge.gatt_table; + page_dir.real = (unsigned long *)agp_bridge.gatt_table_real; + page_dir.remapped = (unsigned long *)agp_bridge.gatt_table; serverworks_free_gatt_pages(); serverworks_free_page_map(&page_dir); @@ -4516,15 +4559,38 @@ { PCI_DEVICE_ID_INTEL_830_M_0, PCI_VENDOR_ID_INTEL, INTEL_I830_M, - "Intel", - "i830M", + "Intel(R)", + "830M", intel_830mp_setup }, - { PCI_DEVICE_ID_INTEL_845_G_0, + + { PCI_DEVICE_ID_INTEL_845_G_0, PCI_VENDOR_ID_INTEL, INTEL_I845_G, - "Intel", - "i845G", + "Intel(R)", + "845G", + intel_845_setup }, + + { PCI_DEVICE_ID_INTEL_855_GM_0, + PCI_VENDOR_ID_INTEL, + INTEL_I855_PM, + "Intel(R)", + "855PM", + intel_845_setup }, + + { PCI_DEVICE_ID_INTEL_855_PM_0, + PCI_VENDOR_ID_INTEL, + INTEL_I855_PM, + "Intel(R)", + "855PM", + intel_845_setup }, + + { PCI_DEVICE_ID_INTEL_865_G_0, + PCI_VENDOR_ID_INTEL, + INTEL_I865_G, + "Intel(R)", + "865G", intel_845_setup }, + { PCI_DEVICE_ID_INTEL_840_0, PCI_VENDOR_ID_INTEL, INTEL_I840, @@ -4565,6 +4631,12 @@ "SiS", "740", sis_generic_setup }, + { PCI_DEVICE_ID_SI_651, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "651", + sis_generic_setup }, { PCI_DEVICE_ID_SI_650, PCI_VENDOR_ID_SI, SIS_GENERIC, @@ -4595,6 +4667,12 @@ "SiS", "745", sis_generic_setup }, + { PCI_DEVICE_ID_SI_745, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "746", + sis_generic_setup }, { PCI_DEVICE_ID_SI_730, PCI_VENDOR_ID_SI, SIS_GENERIC, @@ -4658,6 +4736,12 @@ "Via", "MVP3", via_generic_setup }, + { PCI_DEVICE_ID_VIA_8601_0, + PCI_VENDOR_ID_VIA, + VIA_APOLLO_PLE133, + "Via", + "Apollo PLE133", + via_generic_setup }, { PCI_DEVICE_ID_VIA_82C691_0, PCI_VENDOR_ID_VIA, VIA_APOLLO_PRO, @@ -4682,17 +4766,17 @@ "Via", "Apollo Pro KT266", via_generic_setup }, - { PCI_DEVICE_ID_VIA_8377_0, + { PCI_DEVICE_ID_VIA_8377_0, PCI_VENDOR_ID_VIA, VIA_APOLLO_KT400, "Via", "Apollo Pro KT400", via_generic_setup }, - { PCI_DEVICE_ID_VIA_P4X333, + { PCI_DEVICE_ID_VIA_8377_0, PCI_VENDOR_ID_VIA, - VIA_APOLLO_P4X400, + VIA_APOLLO_KT400, "Via", - "Apollo P4X400", + "Apollo Pro KT400", via_generic_setup }, { 0, PCI_VENDOR_ID_VIA, @@ -4888,10 +4972,14 @@ * with an external graphics * card. It will be initialized later */ + printk(KERN_ERR PFX "Detected an " + "Intel(R) 845G, but could not find the" + " secondary device. Assuming a " + "non-integrated video card.\n"); agp_bridge.type = INTEL_I845_G; break; } - printk(KERN_INFO PFX "Detected an Intel " + printk(KERN_INFO PFX "Detected an Intel(R) " "845G Chipset.\n"); agp_bridge.type = INTEL_I810; return intel_i830_setup(i810_dev); @@ -4913,10 +5001,118 @@ agp_bridge.type = INTEL_I830_M; break; } - printk(KERN_INFO PFX "Detected an Intel " + printk(KERN_INFO PFX "Detected an Intel(R) " "830M Chipset.\n"); agp_bridge.type = INTEL_I810; return intel_i830_setup(i810_dev); + case PCI_DEVICE_ID_INTEL_855_GM_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_855_GM_1, NULL); + if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_855_GM_1, i810_dev); + } + if (i810_dev == NULL) { + /* + * We probably have an 855PM chipset + * with an external graphics + * card. It will be initialized later. + */ + agp_bridge.type = INTEL_I855_PM; + break; + } + { + u32 capval = 0; + const char *name = "855GM/852GM"; + + pci_read_config_dword(dev, I85X_CAPID, &capval); + switch ((capval >> I85X_VARIANT_SHIFT) & + I85X_VARIANT_MASK) { + case I855_GME: + name = "855GME"; + break; + case I855_GM: + name = "855GM"; + break; + case I852_GME: + name = "852GME"; + break; + case I852_GM: + name = "852GM"; + break; + } + printk(KERN_INFO PFX "Detected an Intel(R) " + "%s Chipset.\n", name); + } + agp_bridge.type = INTEL_I810; + return intel_i830_setup(i810_dev); + case PCI_DEVICE_ID_INTEL_855_PM_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_855_PM_1, NULL); + if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_855_PM_1, i810_dev); + } + if (i810_dev == NULL) { + /* + * We probably have an 855PM chipset + * with an external graphics + * card. It will be initialized later. + */ + agp_bridge.type = INTEL_I855_PM; + break; + } + { + u32 capval = 0; + const char *name = "855PM/852PM"; + + pci_read_config_dword(dev, I85X_CAPID, &capval); + switch ((capval >> I85X_VARIANT_SHIFT) & + I85X_VARIANT_MASK) { + case I855_PME: + name = "855PME"; + break; + case I855_PM: + name = "855PM"; + break; + case I852_PME: + name = "852PME"; + break; + case I852_PM: + name = "852PM"; + break; + } + printk(KERN_INFO PFX "Detected an Intel(R) " + "%s Chipset.\n", name); + } + agp_bridge.type = INTEL_I810; + return intel_i830_setup(i810_dev); + case PCI_DEVICE_ID_INTEL_865_G_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_865_G_1, NULL); + if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) { + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_865_G_1, i810_dev); + } + + if (i810_dev == NULL) { + /* + * We probably have a 865G chipset + * with an external graphics + * card. It will be initialized later + */ + printk(KERN_ERR PFX "Detected an " + "Intel(R) 865G, but could not" + " find the" + " secondary device. Assuming a " + "non-integrated video card.\n"); + agp_bridge.type = INTEL_I865_G; + break; + } + printk(KERN_INFO PFX "Detected an Intel(R) " + "865G Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i830_setup(i810_dev); default: break; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/agp/agp.h linux.21rc1-ac2/drivers/char/agp/agp.h --- linux.21rc1/drivers/char/agp/agp.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/agp/agp.h 2003-04-23 14:30:14.000000000 +0100 @@ -166,6 +166,9 @@ #ifndef PCI_DEVICE_ID_VIA_8363_0 #define PCI_DEVICE_ID_VIA_8363_0 0x0305 #endif +#ifndef PCI_DEVICE_ID_VIA_8601_0 +#define PCI_DEVICE_ID_VIA_8601_0 0x0601 +#endif #ifndef PCI_DEVICE_ID_VIA_82C694X_0 #define PCI_DEVICE_ID_VIA_82C694X_0 0x0605 #endif @@ -184,6 +187,24 @@ #ifndef PCI_DEVICE_ID_INTEL_830_M_1 #define PCI_DEVICE_ID_INTEL_830_M_1 0x3577 #endif +#ifndef PCI_DEVICE_ID_INTEL_855_GM_0 +#define PCI_DEVICE_ID_INTEL_855_GM_0 0x3580 +#endif +#ifndef PCI_DEVICE_ID_INTEL_855_GM_1 +#define PCI_DEVICE_ID_INTEL_855_GM_1 0x3582 +#endif +#ifndef PCI_DEVICE_ID_INTEL_855_PM_0 +#define PCI_DEVICE_ID_INTEL_855_PM_0 0x3340 +#endif +#ifndef PCI_DEVICE_ID_INTEL_855_PM_1 +#define PCI_DEVICE_ID_INTEL_855_PM_1 0x3342 +#endif +#ifndef PCI_DEVICE_ID_INTEL_865_G_0 +#define PCI_DEVICE_ID_INTEL_865_G_0 0x2570 +#endif +#ifndef PCI_DEVICE_ID_INTEL_865_G_1 +#define PCI_DEVICE_ID_INTEL_865_G_1 0x2572 +#endif #ifndef PCI_DEVICE_ID_INTEL_820_0 #define PCI_DEVICE_ID_INTEL_820_0 0x2500 #endif @@ -280,6 +301,28 @@ #define INTEL_NBXCFG 0x50 #define INTEL_ERRSTS 0x91 +/* Intel 855GM/852GM registers */ +#define I855_GMCH_CTRL 0x52 +#define I855_GMCH_ENABLED 0x4 +#define I855_GMCH_GMS_MASK (0x7 << 4) +#define I855_GMCH_GMS_STOLEN_0M 0x0 +#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) +#define I85X_CAPID 0x44 +#define I85X_VARIANT_MASK 0x7 +#define I85X_VARIANT_SHIFT 5 +#define I855_GME 0x0 +#define I855_GM 0x4 +#define I852_GME 0x2 +#define I852_GM 0x5 +#define I855_PME 0x0 +#define I855_PM 0x4 +#define I852_PME 0x2 +#define I852_PM 0x5 + /* intel i830 registers */ #define I830_GMCH_CTRL 0x52 #define I830_GMCH_ENABLED 0x4 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/amiserial.c linux.21rc1-ac2/drivers/char/amiserial.c --- linux.21rc1/drivers/char/amiserial.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/amiserial.c 2003-04-22 16:44:36.000000000 +0100 @@ -1544,7 +1544,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cd1865/cd1865.c linux.21rc1-ac2/drivers/char/cd1865/cd1865.c --- linux.21rc1/drivers/char/cd1865/cd1865.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cd1865/cd1865.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,2913 @@ +/* -*- linux-c -*- */ +/* + * This code was modified from + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * Modifications (C) 2002 Telford Tools, Inc. (martillo@telfordtools.com) + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#define VERSION "2.11" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdsiolx.h" +#include "../cd1865.h" /* will move all files up one level */ +#include "siolx.h" +#include "plx9060.h" + +#define SIOLX_NORMAL_MAJOR 254 /* One is needed */ +#define SIOLX_ID 0x10 +#define CD186x_MSMR 0x61 /* modem/timer iack */ +#define CD186x_TSMR 0x62 /* tx iack */ +#define CD186x_RSMR 0x63 /* rx iack */ + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SIOLX_PARANOIA_CHECK + +/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) + When the IRQ routine leaves the chip in a state that is keeps on + requiring attention, the timer doesn't help either. */ +#undef SIOLX_TIMER +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SIOLX_REPORT_FIFO +#undef SIOLX_REPORT_OVERRUN + +#ifdef CONFIG_SIOLX_RTSCTS /* may need to set this */ +#define SIOLX_CRTSCTS(bla) 1 +#else +#define SIOLX_CRTSCTS(tty) C_CRTSCTS(tty) +#endif + +/* Used to be outb (0xff, 0x80); */ +#define short_pause() udelay (1) + +#define SIOLX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +DECLARE_TASK_QUEUE(tq_siolx); + +#undef RS_EVENT_WRITE_WAKEUP +#define RS_EVENT_WRITE_WAKEUP 0 + +#define SIOLX_TYPE_NORMAL 1 +#define SIOLX_TYPE_CALLOUT 2 + +#define BD_8000P 1 +#define BD_16000P 2 +#define BD_8000C 3 +#define BD_16000C 4 +#define BD_MAX BD_16000C + +static struct siolx_board *SiolxIrqRoot[SIOLX_NUMINTS]; + +static char *sio16_board_type[] = +{ + "unknown", + " 8000P ", + "16000P ", + " 8000C ", + "16000C " +}; +static struct tty_driver siolx_driver, siolx_callout_driver; +static int siolx_refcount; +static unsigned char * tmp_buf; +static DECLARE_MUTEX(tmp_buf_sem); +static unsigned long baud_table[] = +{ + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0, +}; +static int siolx_debug = 0; /* turns on lots of */ + /* debugging messages*/ +static int siolx_major = SIOLX_NORMAL_MAJOR; +#ifdef MODULE +static int siolx_minorstart = 256; +#endif +static int siolx_vendor_id = PCI_VENDOR_ID_PLX; +static int siolx_device_id = PCI_DEVICE_ID_PLX_9060SD; +static int siolx_subsystem_vendor = AURASUBSYSTEM_VENDOR_ID; +static int siolx_subsystem_pci_device = AURASUBSYSTEM_MPASYNCPCI; +static int siolx_subsystem_cpci_device = AURASUBSYSTEM_MPASYNCcPCI; +static int siolx_bhindex = SIOLX_BH; /* if this softinterrupt slot is filled */ + +MODULE_PARM(siolx_vendor_id, "i"); +MODULE_PARM(siolx_device_id, "i"); +#ifdef MODULE +MODULE_PARM(siolx_minorstart, "i"); +#endif +MODULE_PARM(siolx_major, "i"); +MODULE_PARM(siolx_subsystem_vendor, "i"); +MODULE_PARM(siolx_subsystem_pci_device, "i"); +MODULE_PARM(siolx_subsystem_cpci_device, "i"); +MODULE_PARM(siolx_bhindex, "i"); + +static struct siolx_board *siolx_board_root; +static struct siolx_board *siolx_board_last; +static struct siolx_port *siolx_port_root; +static struct siolx_port *siolx_port_last; +static unsigned int NumSiolxPorts; +static struct tty_struct **siolx_table; /* make dynamic */ +static struct termios **siolx_termios; +static struct termios **siolx_termios_locked; +static int siolx_driver_registered; +static int siolx_callout_driver_registered; + +#ifdef SIOLX_TIMER +static struct timer_list missed_irq_timer; +static void siolx_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#endif + +extern struct tty_driver *get_tty_driver(kdev_t device); + +static inline int port_No_by_chip (struct siolx_port const * port) +{ + return SIOLX_PORT(port->boardport); +} + +/* Describe the current board and port configuration */ + +static int siolx_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct siolx_port *port = siolx_port_root; + off_t begin = 0; + int len = 0; + unsigned int typeno; + char *revision = "$Revision: 1.11 $"; + + len += sprintf(page, "SIOLX Version %s. %s\n", VERSION, revision); + len += sprintf(page+len, "TTY MAJOR = %d, CUA MAJOR = %d.\n", + siolx_driver.major, siolx_callout_driver.major); + + for (port = siolx_port_root; port != NULL; port = port->next_by_global_list) + { + typeno = port->board->boardtype; + if(typeno > BD_MAX) + { + typeno = 0; + } + len += sprintf(page+len, + "%3.3d: bd %2.2d: %s: ch %d: pt %2.2d/%d: tp %4.4d%c: bs %2.2d: sl %2.2d: ir %2.2d: fl %c%c%c%c%c\n", + siolx_driver.minor_start + port->driverport, + port->board->boardnumber, + sio16_board_type[typeno], + port->board->chipnumber, + port->boardport, + port_No_by_chip(port), /* port relative to chip */ + port->board->chiptype, + port->board->chiprev, + port->board->pdev.bus->number, + PCI_SLOT(port->board->pdev.devfn), + port->board->irq, + (port->flags & ASYNC_INITIALIZED) ? 'I' : ' ', + (port->flags & ASYNC_CALLOUT_ACTIVE) ? 'D' : ' ', + (port->flags & ASYNC_NORMAL_ACTIVE) ? 'T' : ' ', + (port->flags & ASYNC_CLOSING) ? 'C' : ' ', + port->board->reario ? 'R' : ' '); + if (len+begin > off+count) + { + goto done; + } + if (len+begin < off) + { + begin += len; + len = 0; + } + } + *eof = 1; + done: + if (off >= len+begin) + { + return 0; + } + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +#ifndef MODULE +static int GetMinorStart(void) /* minor start can be determined on fly when driver linked to kernel */ +{ + struct tty_driver *ttydriver; + int minor_start = 0; + kdev_t device; + + device = MKDEV(siolx_major, minor_start); + while(ttydriver = get_tty_driver(device), ttydriver != NULL) + { + minor_start += ttydriver->num; + device = MKDEV(TTY_MAJOR, minor_start); + } + return minor_start; + +} +#endif + +/* only once per board chain */ +void SiolxResetBoard(struct siolx_board * bp, struct pci_dev *pdev) +{ + register unsigned int regvalue; + unsigned char savedvalue; + /* + * Yuch. Here's the deal with the reset bits in the + * ECNTL register of the 9060SD. + * + * It appears that LCLRST resets the PLX local configuration + * registers (not the PCI configuration registers) to their + * default values. We need to use LCLRST because it + * is the command (I think) that pulls the local reset + * line on the local bus side of the 9060SD. + * + * Unfortunately, by resetting the PLX local configuration + * registers, we can't use the damn board. So we must + * reinitialize them. The easiest way to do that is to run + * the LDREG command. Unfortunately, it has the side effect + * of reinitializing the PCI configuration registers. It seems, + * however that only the value stowed in ILINE gets choked; all + * of the others seem to be properly preserved. + * + * So, what the code does now is to get a copy of ILINE by + * hand, and then restore it after reloading the registers. + */ + + bp->pdev = *pdev; + bp->plx_vaddr = (unsigned long) ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); + if(bp->plx_vaddr) + { + regvalue = readl(bp->plx_vaddr + PLX_ECNTL); + regvalue &= ~PLX_ECNTLLDREG; + regvalue |= PLX_ECNTLLCLRST; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + udelay(200); + regvalue &= ~PLX_ECNTLLCLRST; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &savedvalue); + regvalue |= PLX_ECNTLLDREG; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + udelay(200); + regvalue &= ~PLX_ECNTLLDREG; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, savedvalue); + regvalue |= PLX_ECNTLINITSTAT; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + writel(0, bp->plx_vaddr + PLX_ICSR); + } +} + +void SiolxShutdownBoard(struct siolx_board * bp) +{ + register unsigned int regvalue; + unsigned char savedvalue; + struct pci_dev *pdev; + + if(bp->chipnumber == 0) /* only shutdown first in a chain */ + { + pdev = &bp->pdev; + + writel(0, bp->plx_vaddr + PLX_ICSR); + regvalue = readl(bp->plx_vaddr + PLX_ECNTL); + regvalue &= ~PLX_ECNTLLDREG; + regvalue |= PLX_ECNTLLCLRST; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + udelay(200); + regvalue &= ~PLX_ECNTLLCLRST; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &savedvalue); + regvalue |= PLX_ECNTLLDREG; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + udelay(200); + regvalue &= ~PLX_ECNTLLDREG; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, savedvalue); + regvalue |= PLX_ECNTLINITSTAT; + writel(regvalue, bp->plx_vaddr + PLX_ECNTL); + writel(0, bp->plx_vaddr + PLX_ICSR); + iounmap((void*)bp->plx_vaddr); + bp->plx_vaddr = 0; + } +} + +static inline int siolx_paranoia_check(struct siolx_port const * port, + kdev_t device, const char *routine) +{ +#ifdef SIOLX_PARANOIA_CHECK + static const char *badmagic = + KERN_ERR "siolx: Warning: bad siolx port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "siolx: Warning: null siolx port for device %s in %s\n"; + + if (!port) + { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SIOLX_MAGIC) + { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for siolx Aurora Asynchronous Adapter driver. + * + */ + +/* Get board number from pointer */ +static inline int board_No (struct siolx_board * bp) +{ + return bp->boardnumber; /* note same for all chips/boards in a chain */ +} + + +/* Get port number from pointer */ +static inline int port_No (struct siolx_port const * port) +{ + return port->driverport; /* offset from minor start */ +} + +/* Get pointer to board from pointer to port */ +static inline struct siolx_board * port_Board(struct siolx_port const * port) +{ + return port->board; /* same for ports on both chips on a board */ +} + + +/* Input Byte from CL CD186x register */ +static inline unsigned char siolx_in(struct siolx_board * bp, unsigned short reg) +{ + return readb (bp->base + reg); +} + + +/* Output Byte to CL CD186x register */ +static inline void siolx_out(struct siolx_board * bp, unsigned short reg, + unsigned char val) +{ + writeb(val, bp->base + reg); +} + + +/* Wait for Channel Command Register ready */ +static int siolx_wait_CCR(struct siolx_board * bp) +{ + unsigned long delay; + + for (delay = SIOLX_CCR_TIMEOUT; delay; delay--) + { + udelay(1); + if (!siolx_in(bp, CD186x_CCR)) + { + return 0; + } + } + printk(KERN_ERR "siolx:board %d: timeout waiting for CCR.\n", board_No(bp)); + return -1; +} + +/* Wait for ready */ +static int siolx_wait_GIVR(struct siolx_board * bp) +{ + unsigned long delay; + + for (delay = SIOLX_CCR_TIMEOUT; delay; delay--) + { + udelay(1); + if (siolx_in(bp, CD186x_GIVR) == (unsigned char) 0xff) + { + return 0; + } + } + printk(KERN_ERR "siolx: board %d: timeout waiting for GIVR.\n", board_No(bp)); + return -1; +} + +static inline void siolx_release_io_range(struct siolx_board * bp) +{ + if((bp->chipnumber == 0) && bp->vaddr) /* only release from first board in a chain */ + { + iounmap((void*)bp->vaddr); + bp->vaddr = 0; + } +} + +/* Must be called with enabled interrupts */ + +static inline void siolx_long_delay(unsigned long delay) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(delay); +} + +/* Reset and setup CD186x chip */ +static int siolx_init_CD186x(struct siolx_board * bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + int rev; + int chip; + + save_flags(flags); /* not sure of need to turn off ints */ + cli(); + if(siolx_wait_CCR(bp)) + { + restore_flags(flags); + return 0; /* Wait for CCR ready */ + } + siolx_out(bp, CD186x_CAR, 0); + siolx_out(bp, CD186x_GIVR, 0); + siolx_out(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + if(siolx_wait_GIVR(bp)) + { + restore_flags(flags); + return 0; + } + sti(); + siolx_long_delay(HZ/20); /* Delay 0.05 sec */ + cli(); + siolx_out(bp, CD186x_GIVR, SIOLX_ID | (bp->chipnumber ? 0x80 : 0)); /* Set ID for this chip */ +#if 0 + siolx_out(bp, CD186x_GICR, 0); /* Clear all bits */ +#endif + scaler = SIOLX_OSCFREQ/1000; + siolx_out(bp, CD186x_PPRH, scaler >> 8); + siolx_out(bp, CD186x_PPRL, scaler & 0xff); + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (siolx_in(bp, CD186x_GFRCR)) + { + case 0x82: + chip = 1864; + rev='A'; + break; + case 0x83: + chip = 1865; + rev='A'; + break; + case 0x84: + chip = 1865; + rev='B'; + break; + case 0x85: + chip = 1865; + rev='C'; + break; /* Does not exist at this time */ + default: + chip=-1; + rev='x'; + break; + } + +#if SIOLX_DEBUG > 2 + printk (KERN_DEBUG " GFCR = 0x%02x\n", siolx_in(bp, CD186x_GFRCR) ); +#endif + + siolx_out(bp, CD186x_MSMR, CD186x_MRAR); /* load up match regs with address regs */ + siolx_out(bp, CD186x_TSMR, CD186x_TRAR); + siolx_out(bp, CD186x_RSMR, CD186x_RRAR); + +#if 0 + DEBUGPRINT((KERN_ALERT "match reg values are msmr %x, tsmr %x, rsmr %x.\n", + siolx_in(bp, CD186x_MSMR), + siolx_in(bp, CD186x_TSMR), + siolx_in(bp, CD186x_RSMR))); +#endif + + siolx_out(bp, CD186x_SRCR, SRCR_AUTOPRI | SRCR_GLOBPRI | SRCR_REGACKEN); + /* Setting up prescaler. We need 4 ticks per 1 ms */ + + printk(KERN_INFO"siolx: CD%4.4d%c detected at 0x%lx, IRQ %d, on Aurora asynchronous adapter board %d, chip number %d.\n", + chip, rev, bp->base, bp->irq, board_No(bp), bp->chipnumber); + + bp->chiptype = chip; + bp->chiprev = rev; + + restore_flags(flags); + return rv; +} + + +#ifdef SIOLX_TIMER +void missed_irq (unsigned long data) +{ + if (siolx_in ((struct siolx_board *)data, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)) + { + printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); + siolx_interrupt (((struct siolx_board *)data)->irq, + NULL, NULL); + } + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +} +#endif + +/* Main probing routine, also sets irq. */ +static int siolx_probe(struct siolx_board *bp) +{ + unsigned char val1, val2; + + /* Are the I/O ports here ? */ + siolx_out(bp, CD186x_PPRL, 0x5a); + short_pause (); + val1 = siolx_in(bp, CD186x_PPRL); + + siolx_out(bp, CD186x_PPRL, 0xa5); + short_pause (); + val2 = siolx_in(bp, CD186x_PPRL); + + if ((val1 != 0x5a) || (val2 != 0xa5)) + { + printk(KERN_INFO + "siolx: cd serial chip not found at base %ld.\n", + bp->base); + return 1; + } + + /* Reset CD186x */ + if (!siolx_init_CD186x(bp)) + { + return -EIO; + } + +#ifdef SIOLX_TIMER + init_timer (&missed_irq_timer); + missed_irq_timer.function = missed_irq; + missed_irq_timer.data = (unsigned long) bp; + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +#endif + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +static inline void siolx_mark_event(struct siolx_port * port, int event) +{ + /* + * I'm not quite happy with current scheme all serial + * drivers use their own BH routine. + * It seems this easily can be done with one BH routine + * serving for all serial drivers. + * For now I must introduce another one - SIOLX_BH. + * Still hope this will be changed in near future. + * -- Dmitry. + */ + /* I use a module parameter that can be set at module + * load time so that this driver can be downloaded into + * a kernel where the value of SIOLX_BX has been allocated + * to something else. This kludge was not necessary + * in the ASLX driver because AURORA_BH had already + * been allocated for the sparc and there was no + * similar driver for x86 while the ASLX driver probably + * will not work for the SPARC and is not guaranteed to + * do so (at some point I should clean this situation up) -- Joachim*/ + set_bit(event, &port->event); + queue_task(&port->tqueue, &tq_siolx); + mark_bh(siolx_bhindex); +} + +static inline struct siolx_port * siolx_get_port(struct siolx_board * bp, + unsigned char const * what) +{ + unsigned char channel; + struct siolx_port * port; + + channel = siolx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + if (channel < CD186x_NCH) + { + port = bp->portlist; + while(port) + { + if(channel == 0) + { + break; + } + port = port->next_by_board; + --channel; + } + + if(port && (port->flags & ASYNC_INITIALIZED)) /* port should be opened */ + { + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +static inline void siolx_receive_exc(struct siolx_board * bp) +{ + struct siolx_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch; + + if (!(port = siolx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + return; + } + +#ifdef SIOLX_REPORT_OVERRUN + status = siolx_in(bp, CD186x_RCSR); + if (status & RCSR_OE) + { + port->overrun++; +#if SIOLX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); +#endif + } + status &= port->mark_mask; +#else + status = siolx_in(bp, CD186x_RCSR) & port->mark_mask; +#endif + ch = siolx_in(bp, CD186x_RDR); + if (!status) + { + return; + } + if (status & RCSR_TOUT) + { + printk(KERN_INFO "siolx: board %d: chip %d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), bp->chipnumber, port_No(port)); + return; + + } + else if (status & RCSR_BREAK) + { +#ifdef SIOLX_DEBUG + printk(KERN_DEBUG "siolx: board %d: chip %d: port %d: Handling break...\n", + board_No(bp), bp->chipnumber, port_No(port)); +#endif + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (port->flags & ASYNC_SAK) + { + do_SAK(tty); + } + + } + else if (status & RCSR_PE) + { + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + } + else if (status & RCSR_FE) + { + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + } + + else if (status & RCSR_OE) + { + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + } + + else + { + *tty->flip.flag_buf_ptr++ = 0; + } + + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +static inline void siolx_receive(struct siolx_board * bp) +{ + struct siolx_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = siolx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + + count = siolx_in(bp, CD186x_RDCR); + +#ifdef SIOLX_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) + { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + { + printk(KERN_INFO "siolx: board %d: chip %d: port %d: Working around flip buffer overflow.\n", + board_No(bp), bp->chipnumber, port_No(port)); + break; + } + *tty->flip.char_buf_ptr++ = siolx_in(bp, CD186x_RDR); + *tty->flip.flag_buf_ptr++ = 0; + tty->flip.count++; + } + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static inline void siolx_transmit(struct siolx_board * bp) +{ + struct siolx_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = siolx_get_port(bp, "Transmit"))) + return; + + tty = port->tty; + + if(port->IER & IER_TXEMPTY) + { + /* FIFO drained */ +#if 0 + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); +#endif + port->IER &= ~IER_TXEMPTY; + siolx_out(bp, CD186x_IER, port->IER); + return; + } + + if(((port->xmit_cnt <= 0) && !port->break_length) || + tty->stopped || tty->hw_stopped) + { +#if 0 + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); +#endif + port->IER &= ~IER_TXRDY; + siolx_out(bp, CD186x_IER, port->IER); + return; + } + + if (port->break_length) + { + if (port->break_length > 0) + { + if (port->COR2 & COR2_ETC) + { + siolx_out(bp, CD186x_TDR, CD186x_C_ESC); + siolx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = MIN(port->break_length, 0xff); + siolx_out(bp, CD186x_TDR, CD186x_C_ESC); + siolx_out(bp, CD186x_TDR, CD186x_C_DELAY); + siolx_out(bp, CD186x_TDR, count); + if (!(port->break_length -= count)) + { + port->break_length--; + } + } + else + { + siolx_out(bp, CD186x_TDR, CD186x_C_ESC); + siolx_out(bp, CD186x_TDR, CD186x_C_EBRK); + siolx_out(bp, CD186x_COR2, port->COR2); + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + return; + } + + count = CD186x_NFIFO; + do + { + siolx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + { + break; + } + } while (--count > 0); + + if (port->xmit_cnt <= 0) + { +#if 0 + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); +#endif + port->IER &= ~IER_TXRDY; + siolx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + { + siolx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } +} + + +static inline void siolx_check_modem(struct siolx_board * bp) +{ + struct siolx_port *port; + struct tty_struct *tty; + unsigned char mcr; + +#ifdef SIOLX_DEBUG + printk (KERN_DEBUG "Modem intr. "); +#endif + if (!(port = siolx_get_port(bp, "Modem"))) + { + return; + } + + tty = port->tty; + + mcr = siolx_in(bp, CD186x_MCR); + DEBUGPRINT((KERN_ALERT "mcr = %02x.\n", mcr)); + + if ((mcr & MCR_CDCHG)) + { +#ifdef SIOLX_DEBUG + DEBUGPRINT((KERN_DEBUG "CD just changed... ")); +#endif + if (siolx_in(bp, CD186x_MSVR) & MSVR_CD) + { +#ifdef SIOLX_DEBUG + DEBUGPRINT(( "Waking up guys in open.\n")); +#endif + wake_up_interruptible(&port->open_wait); /* note no linefeed in previous print */ + } + else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) + { +#ifdef SIOLX_DEBUG + DEBUGPRINT(( "Sending HUP.\n")); /* note no linefeed in previous print */ +#endif + MOD_INC_USE_COUNT; + if (schedule_task(&port->tqueue_hangup) == 0) + { + MOD_DEC_USE_COUNT; + } + } + else + { +#ifdef SIOLX_DEBUG + DEBUGPRINT(("Don't need to send HUP.\n")); /* note no linefeed in previous print */ +#endif + } + } + +#ifdef SIOLX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) + { + if (siolx_in(bp, CD186x_MSVR) & MSVR_CTS) + { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + siolx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } + else + { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + siolx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) + { + if (siolx_in(bp, CD186x_MSVR) & MSVR_DSR) + { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + { + siolx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } + } + else + { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + siolx_out(bp, CD186x_IER, port->IER); + } +#endif /* SIOLX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + siolx_out(bp, CD186x_MCR, 0); +} + +/* The main interrupt processing routine */ +static void siolx_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + unsigned char status; + unsigned char rcsr; + struct siolx_board *bp; + + if((irq < 0) || (irq >= SIOLX_NUMINTS)) + { + printk(KERN_ALERT "siolx: bad interrupt value %i.\n", irq); + return; + } + /* walk through all the cards on the interrupt that occurred. */ + for(bp = SiolxIrqRoot[irq]; bp != NULL; bp = bp->next_by_interrupt) + + { + while((readl(bp->intstatus) & PLX_ICSRINTACTIVE) != 0) /* work on on board */ + { + status = siolx_in(bp, CD186x_SRSR); + + if(status & SRSR_RREQint) + { + siolx_in(bp, CD186x_RRAR); + rcsr = siolx_in(bp, CD186x_RCSR); + if(rcsr == 0) + { + siolx_receive(bp); + } + else + { + siolx_receive_exc(bp); + } + } + else if (status & SRSR_TREQint) + { + siolx_in(bp, CD186x_TRAR); + siolx_transmit(bp); + } + else if (status & SRSR_MREQint) + { + siolx_in(bp, CD186x_MRAR); + siolx_check_modem(bp); + } + siolx_out(bp, CD186x_EOIR, 1); /* acknowledge the interrupt */ + bp = bp->next_by_chain; /* go to next chip on card -- maybe this one */ + } /* it does not matter if bp changes all in a chain have same next by interrupt */ + } +} + + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void siolx_change_speed(struct siolx_board *bp, struct siolx_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static int again; + + tty = port->tty; + + if(!tty || !tty->termios) + { + return; + } + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + + /* The Siolx board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + /* Must check how to apply these to sio16 boards */ + if (SIOLX_CRTSCTS(tty)) + { + port->MSVR = (MSVR_DTR | (siolx_in(bp, CD186x_MSVR) & MSVR_RTS)); + } + else + { + port->MSVR = (siolx_in(bp, CD186x_MSVR) & MSVR_RTS); + } +#ifdef DEBUG_SIOLX + DEBUGPRINT((KERN_DEBUG "siolx: got MSVR=%02x.\n", port->MSVR)); +#endif + baud = C_BAUD(tty); + + if (baud & CBAUDEX) + { + baud &= ~CBAUDEX; + if((baud < 1) || (baud > 2)) + { + port->tty->termios->c_cflag &= ~CBAUDEX; + } + else + { + baud += 15; + } + } + if (baud == 15) + { + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + { + baud ++; + } + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + { + baud += 2; + } + } + + + if (!baud_table[baud]) + { + /* Drop DTR & exit */ +#ifdef SIOLX_DEBUG + DEBUGPRINT((KERN_DEBUG "siolx: Dropping DTR... Hmm....\n")); +#endif + if (!SIOLX_CRTSCTS (tty)) + { + port->MSVR &= ~ MSVR_DTR; + siolx_out(bp, CD186x_MSVR, port->MSVR ); + } +#ifdef DEBUG_SIOLX + else + { + DEBUGPRINT((KERN_DEBUG "siolx: Can't drop DTR: no DTR.\n")); + } +#endif + return; + } + else + { + /* Set DTR on */ + if (!SIOLX_CRTSCTS (tty)) + { + port ->MSVR |= MSVR_DTR; + } + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = port->custom_divisor ; + if(tmp) + { + DEBUGPRINT((KERN_INFO "siolx: board %d: chip %d: port %d: Using custom baud rate divisor %ld. \n" + "This is an untested option, please be carefull.\n", + board_No(bp), + bp->chipnumber, + port_No(port), tmp)); + } + else + { + tmp = (((SIOLX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + CD186x_TPC/2) / CD186x_TPC); + } + + if ((tmp < 0x10) && time_before(again, jiffies)) + { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) + { + DEBUGPRINT((KERN_INFO "siolx: board %d: chip %d: port %d:Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n" + "Read siolx.txt for more info.\n", + board_No(bp), bp->chipnumber, + port_No (port), tmp)); + } else + { + DEBUGPRINT((KERN_INFO "siolx: board %d: chip %d: port %d: Baud rate divisor is %ld. \n" + " Warning: overstressing Cirrus chip. " + " This might not work.\n" + " Read siolx.txt for more info.\n", + board_No(bp), bp->chipnumber, port_No (port), tmp)); + } + } + + siolx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + siolx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + siolx_out(bp, CD186x_RBPRL, tmp & 0xff); + siolx_out(bp, CD186x_TBPRL, tmp & 0xff); + + if (port->custom_divisor) + { + baud = (SIOLX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor; + baud = ( baud + 5 ) / 10; + } else + { + baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + } + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SIOLX_TPS + SIOLX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + siolx_out(bp, CD186x_RTPR, tmp); + + switch (C_CSIZE(tty)) + { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + { + cor1 |= COR1_2SB; + } + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) + { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + { + cor1 |= COR1_ODDP; + } + if (I_INPCK(tty)) + { + cor1 &= ~COR1_IGNORE; + } + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + { + port->mark_mask |= RCSR_FE | RCSR_PE; + } + if (I_BRKINT(tty) || I_PARMRK(tty)) + { + port->mark_mask |= RCSR_BREAK; + } + if (I_IGNPAR(tty)) + { + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + } + if (I_IGNBRK(tty)) + { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + { + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) + { +#ifdef SIOLX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(siolx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) + { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + { + port->COR2 |= COR2_IXM; + } + siolx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + siolx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + siolx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + siolx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) + { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + { + /* Enable receiver */ + port->IER |= IER_RXD; + } + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= SIOLX_RXFIFO; + /* Setting up CD186x channel registers */ + siolx_out(bp, CD186x_COR1, cor1); + siolx_out(bp, CD186x_COR2, port->COR2); + siolx_out(bp, CD186x_COR3, cor3); + /* Make CD186x know about registers change */ + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ +#ifdef DEBUG_SIOLX + DEBUGPRINT((KERN_ALERT "siolx: Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2)); +#endif + siolx_out(bp, CD186x_MCOR1, mcor1); + siolx_out(bp, CD186x_MCOR2, mcor2); + /* Enable CD186x transmitter & receiver */ + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + siolx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + siolx_out(bp, CD186x_MSVR, port->MSVR); +} + + +/* Must be called with interrupts enabled */ +static int siolx_setup_port(struct siolx_board *bp, struct siolx_port *port) +{ + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + { + return 0; + } + + if (!port->xmit_buf) + { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + if (!(tmp = get_free_page(GFP_KERNEL))) + { + return -ENOMEM; + } + + if (port->xmit_buf) + { + free_page(tmp); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + save_flags(flags); cli(); + + if (port->tty) + { + clear_bit(TTY_IO_ERROR, &port->tty->flags); + } + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + siolx_change_speed(bp, port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void siolx_shutdown_port(struct siolx_board *bp, struct siolx_port *port) +{ + struct tty_struct *tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + { + return; + } + +#ifdef SIOLX_REPORT_OVERRUN + DEBUGPRINT((KERN_INFO "siolx: board %d: chip %d: port %d: Total %ld overruns were detected.\n", + board_No(bp), bp->chipnumber, port_No(port), port->overrun)); +#endif +#ifdef SIOLX_REPORT_FIFO + { + int i; + + DEBUGPRINT((KERN_INFO "siolx: board %d: chip %d: port %d: FIFO hits [ ", + board_No(bp), bp->chipnumber, port_No(port))); + for (i = 0; i < 10; i++) + { + DEBUGPRINT(("%ld ", port->hits[i])); + } + DEBUGPRINT(("].\n")); + } +#endif + if (port->xmit_buf) + { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + + if (!(tty = port->tty) || C_HUPCL(tty)) + { + /* Drop DTR */ + siolx_out(bp, CD186x_MSVDTR, 0); + } + + /* Reset port */ + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + siolx_out(bp, CD186x_IER, port->IER); + + if (tty) + { + set_bit(TTY_IO_ERROR, &tty->flags); + } + port->flags &= ~ASYNC_INITIALIZED; + + /* + * If this is the last opened port on the board + * shutdown whole board + */ + MOD_DEC_USE_COUNT; +} + + +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct siolx_port *port) +{ + DECLARE_WAITQUEUE(wait, current); + struct siolx_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) + { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + { + return -EAGAIN; + } + else + { + return -ERESTARTSYS; + } + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SIOLX_TYPE_CALLOUT) + { + if (port->flags & ASYNC_NORMAL_ACTIVE) + { + return -EBUSY; + } + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + { + return -EBUSY; + } + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + { + return -EBUSY; + } + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) + { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + { + return -EBUSY; + } + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) + { + if (port->normal_termios.c_cflag & CLOCAL) + { + do_clocal = 1; + } + } + else + { + if (C_CLOCAL(tty)) + { + do_clocal = 1; + } + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + { + port->count--; + } + sti(); + port->blocked_open++; + while (1) + { + cli(); + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + CD = siolx_in(bp, CD186x_MSVR) & MSVR_CD; + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) + { + if (SIOLX_CRTSCTS (tty)) + { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; + siolx_out (bp, CD186x_MSVR, port->MSVR); + } + else + { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + siolx_out (bp, CD186x_MSVR, port->MSVR); + } + } + sti(); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) + { + if (port->flags & ASYNC_HUP_NOTIFY) + { + retval = -EAGAIN; + } + else + { + retval = -ERESTARTSYS; + } + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + { + break; + } + if (signal_pending(current)) + { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + { + port->count++; + } + port->blocked_open--; + if (retval) + { + return retval; + } + + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static inline struct siolx_port *siolx_portstruc(register int line) +{ + register struct siolx_port *pp; + + line -= siolx_driver.minor_start; + for(pp = siolx_port_root; (pp != NULL) && (line >= 0); --line, pp = pp->next_by_global_list) + { + if(line == 0) + { + return pp; + } + } + return NULL; +} + + +static int siolx_open(struct tty_struct * tty, struct file * filp) +{ + int error; + struct siolx_port * port; + struct siolx_board * bp; + unsigned long flags; + + port = siolx_portstruc(MINOR(tty->device)); + + if(port == NULL) + { + return -ENODEV; + } + bp = port->board; + if(bp == NULL) + { + return -ENODEV; + } + +#ifdef DEBUG_SIOLX + printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", + bp->boardnumber, bp, port, siolx_portstruc(MINOR(tty->device))); +#endif + + if (siolx_paranoia_check(port, tty->device, "siolx_open")) + return -ENODEV; + + MOD_INC_USE_COUNT; + + port->count++; + tty->driver_data = port; + port->tty = tty; + + if ((error = siolx_setup_port(bp, port))) + return error; + + if ((error = block_til_ready(tty, filp, port))) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SIOLX_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + siolx_change_speed(bp, port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; + return 0; +} + + +static void siolx_close(struct tty_struct * tty, struct file * filp) +{ + struct siolx_port *port = (struct siolx_port *) tty->driver_data; + struct siolx_board *bp; + unsigned long flags; + unsigned long timeout; + + if (!port || siolx_paranoia_check(port, tty->device, "close")) + return; + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + bp = port_Board(port); + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { + printk(KERN_ERR "sx%d: siolx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "sx%d: siolx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + port->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_IER, port->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while(port->IER & IER_TXEMPTY) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(port->timeout); + if (time_after(jiffies, timeout)) { + printk (KERN_INFO "siolx: Timeout waiting for close\n"); + break; + } + } + + } + siolx_shutdown_port(bp, port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(port->close_delay); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +} + + +static int siolx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + int c, total = 0; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_write")) + return 0; + + bp = port_Board(port); + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!total) + total = -EFAULT; + break; + } + + cli(); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + + buf += c; + count -= c; + total += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) { + restore_flags(flags); + break; + } + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + + buf += c; + count -= c; + total += c; + } + } + + cli(); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); + return total; +} + + +static void siolx_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; + + save_flags(flags); cli(); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + restore_flags(flags); +} + + +static void siolx_flush_chars(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + save_flags(flags); cli(); + port->IER |= IER_TXRDY; + siolx_out(port_Board(port), CD186x_CAR, port_No_by_chip(port)); + siolx_out(port_Board(port), CD186x_IER, port->IER); + restore_flags(flags); +} + + +static int siolx_write_room(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + int ret; + + if (siolx_paranoia_check(port, tty->device, "siolx_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + + +static int siolx_chars_in_buffer(struct tty_struct *tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + + if (siolx_paranoia_check(port, tty->device, "siolx_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + + +static void siolx_flush_buffer(struct tty_struct *tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int siolx_get_modem_info(struct siolx_port * port, unsigned int *value) +{ + struct siolx_board * bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + bp = port_Board(port); + save_flags(flags); cli(); + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + status = siolx_in(bp, CD186x_MSVR); + restore_flags(flags); +#ifdef DEBUG_SIOLX + printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, siolx_in (bp, CD186x_CAR)); + printk (KERN_DEBUG "siolx_port = %p, port = %p\n", siolx_port, port); +#endif + if (SIOLX_CRTSCTS(port->tty)) { + result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + put_user(result,(unsigned int *) value); + return 0; +} + + +static int siolx_set_modem_info(struct siolx_port * port, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + struct siolx_board *bp = port_Board(port); + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + + get_user(arg, (unsigned long *) value); + switch (cmd) { + case TIOCMBIS: + /* if (arg & TIOCM_RTS) + port->MSVR |= MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; */ + + if (SIOLX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + break; + case TIOCMBIC: + /* if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; */ + if (SIOLX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + break; + case TIOCMSET: + /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : + (port->MSVR & ~MSVR_RTS); */ + /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); */ + if (SIOLX_CRTSCTS(port->tty)) { + port->MSVR = (arg & TIOCM_RTS) ? + (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); + } else { + port->MSVR = (arg & TIOCM_DTR) ? + (port->MSVR | MSVR_DTR): + (port->MSVR & ~MSVR_DTR); + } + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); + return 0; +} + + +static inline void siolx_send_break(struct siolx_port * port, unsigned long length) +{ + struct siolx_board *bp = port_Board(port); + unsigned long flags; + + save_flags(flags); cli(); + port->break_length = SIOLX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_COR2, port->COR2); + siolx_out(bp, CD186x_IER, port->IER); + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_CORCHG2); + siolx_wait_CCR(bp); + restore_flags(flags); +} + + +static inline int siolx_set_serial_info(struct siolx_port * port, + struct serial_struct * newinfo) +{ + struct serial_struct tmp; + struct siolx_board *bp = port_Board(port); + int change_speed; + unsigned long flags; + int error; + + error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp)); + if (error) + return error; + + if (copy_from_user(&tmp, newinfo, sizeof(tmp))) + return -EFAULT; + +#if 0 + if ((tmp.irq != bp->irq) || + (tmp.port != bp->base) || + (tmp.type != PORT_CIRRUS) || + (tmp.baud_base != (SIOLX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || + (tmp.custom_divisor != 0) || + (tmp.xmit_fifo_size != CD186x_NFIFO) || + (tmp.flags & ~SIOLX_LEGAL_FLAGS)) + return -EINVAL; +#endif + + change_speed = ((port->flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + change_speed |= (tmp.custom_divisor != port->custom_divisor); + + if (!capable(CAP_SYS_ADMIN)) { + if ((tmp.close_delay != port->close_delay) || + (tmp.closing_wait != port->closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + port->custom_divisor = tmp.custom_divisor; + } else { + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->close_delay = tmp.close_delay; + port->closing_wait = tmp.closing_wait; + port->custom_divisor = tmp.custom_divisor; + } + if (change_speed) { + save_flags(flags); cli(); + siolx_change_speed(bp, port); + restore_flags(flags); + } + return 0; +} + + +static inline int siolx_get_serial_info(struct siolx_port * port, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct siolx_board *bp = port_Board(port); + int error; + + error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)); + if (error) + return error; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port->driverport; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->flags; + tmp.baud_base = (SIOLX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->close_delay * HZ/100; + tmp.closing_wait = port->closing_wait * HZ/100; + tmp.custom_divisor = port->custom_divisor; + tmp.xmit_fifo_size = CD186x_NFIFO; + if (copy_to_user(retinfo, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; +} + + +static int siolx_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + int error; + int retval; + + if (siolx_paranoia_check(port, tty->device, "siolx_ioctl")) + return -ENODEV; + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + siolx_send_break(port, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + siolx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return siolx_get_modem_info(port, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return siolx_set_modem_info(port, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return siolx_get_serial_info(port, (struct serial_struct *) arg); + case TIOCSSERIAL: + return siolx_set_serial_info(port, (struct serial_struct *) arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void siolx_throttle(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_throttle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + + /* Use DTR instead of RTS ! */ + if (SIOLX_CRTSCTS (tty)) + { + port->MSVR &= ~MSVR_DTR; + } + else + { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No (port)); + } + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + if (I_IXOFF(tty)) + { + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_SSCH2); + siolx_wait_CCR(bp); + } + siolx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void siolx_unthrottle(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_unthrottle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + /* XXXX Use DTR INSTEAD???? */ + if (SIOLX_CRTSCTS(tty)) { + port->MSVR |= MSVR_DTR; + } /* Else clause: see remark in "siolx_throttle"... */ + + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + if (I_IXOFF(tty)) { + siolx_wait_CCR(bp); + siolx_out(bp, CD186x_CCR, CCR_SSCH1); + siolx_wait_CCR(bp); + } + siolx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void siolx_stop(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_stop")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + port->IER &= ~IER_TXRDY; + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_IER, port->IER); + restore_flags(flags); +} + + +static void siolx_start(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_start")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + siolx_out(bp, CD186x_CAR, port_No_by_chip(port)); + siolx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_siolx_hangup() -> tty->hangup() -> siolx_hangup() + * + */ +static void do_siolx_hangup(void *private_) +{ + struct siolx_port *port = (struct siolx_port *) private_; + struct tty_struct *tty; + + tty = port->tty; + if (tty) + { + tty_hangup(tty); /* FIXME: module removal race here */ + } + MOD_DEC_USE_COUNT; +} + + +static void siolx_hangup(struct tty_struct * tty) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + struct siolx_board *bp; + + if (siolx_paranoia_check(port, tty->device, "siolx_hangup")) + return; + + bp = port_Board(port); + + siolx_shutdown_port(bp, port); + port->event = 0; + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + + +static void siolx_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct siolx_port *port = (struct siolx_port *)tty->driver_data; + unsigned long flags; + + if (siolx_paranoia_check(port, tty->device, "siolx_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + siolx_change_speed(port_Board(port), port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + siolx_start(tty); + } +} + + +static void do_siolx_bh(void) +{ + run_task_queue(&tq_siolx); +} + + +static void do_softint(void *private_) +{ + struct siolx_port *port = (struct siolx_port *) private_; + struct tty_struct *tty; + + if(!(tty = port->tty)) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +static int siolx_finish_init_drivers(void) +{ + register struct siolx_board *bp; + register unsigned int count; + unsigned int maxport; + struct siolx_port *port; + struct siolx_port *lastport; + int error; + + bp = siolx_board_root; + + while(bp) + { + if(bp->chipnumber == 0) + { + maxport = SIOLX_NPORT; + } + else if((bp->boardtype == BD_16000C) && bp->reario) /* must be second chip of 16000C */ + { + maxport = SIOLX_NPORT/2; + } + else + { + maxport = SIOLX_NPORT; /* must be second chip of 16000P */ + } + + port = NULL; /* probably unnecessary */ + lastport = NULL; + for(count = 0; count < maxport; ++count) + { + port = (struct siolx_port*)kmalloc(sizeof(struct siolx_port), GFP_KERNEL); + if(port == NULL) + { + printk(KERN_ALERT + "siolx: Failed to create port structure on board %p.\n", bp); + break; /* no memory available */ + } + memset(port, 0, sizeof(struct siolx_port)); + + port->callout_termios = siolx_callout_driver.init_termios; + port->normal_termios = siolx_driver.init_termios; + port->magic = SIOLX_MAGIC; + port->tqueue.routine = do_softint; + port->tqueue.data = port; + port->tqueue_hangup.routine = do_siolx_hangup; + port->tqueue_hangup.data = port; + port->close_delay = 50 * HZ/100; + port->closing_wait = 3000 * HZ/100; + init_waitqueue_head(&port->open_wait); + init_waitqueue_head(&port->close_wait); + + port->board = bp; + port->driverport = NumSiolxPorts; + port->boardport = (count + (port->board->chipnumber*SIOLX_NPORT)); /* 0-16 */ + + if(count == 0) + { + bp->portlist = port; + } + else if(lastport) /* if count != 0 lastport should be non-null */ + { + lastport->next_by_board = port; + } + if(siolx_port_root == NULL) + { + siolx_port_root = port; + siolx_port_last = port; + } + else + { + siolx_port_last->next_by_global_list = port; + siolx_port_last = port; + } + lastport = port; + ++NumSiolxPorts; + } + bp = bp->next_by_global_list; + } + + siolx_driver.num = NumSiolxPorts; + + siolx_table = (struct tty_struct **) kmalloc(NumSiolxPorts*sizeof(struct tty_struct *), GFP_KERNEL); + if(siolx_table == NULL) + { + printk(KERN_ALERT "siolx: Could not allocate memory for siolx_table.\n"); + return 1; + } + memset(siolx_table, 0, NumSiolxPorts*sizeof(struct tty_struct *)); + + siolx_termios = (struct termios **) kmalloc(NumSiolxPorts*sizeof(struct termios *), GFP_KERNEL); + if(siolx_termios == NULL) + { + printk(KERN_ALERT "siolx: Could not allocate memory for siolx_termios.\n"); + return 1; + } + memset(siolx_termios, 0, NumSiolxPorts*sizeof(struct termios *)); + + siolx_termios_locked = (struct termios **) kmalloc(NumSiolxPorts*sizeof(struct termios *), GFP_KERNEL); + if(siolx_termios_locked == NULL) + { + printk(KERN_ALERT "siolx: Could not allocate memory for siolx_termios_locked.\n"); + return 1; + } + memset(siolx_termios_locked, 0, NumSiolxPorts*sizeof(struct termios *)); + + siolx_driver.table = siolx_table; /* will be changed */ + siolx_driver.termios = siolx_termios; /* will be changed */ + siolx_driver.termios_locked = siolx_termios_locked; /* will be changed */ + + if ((error = tty_register_driver(&siolx_driver))) + { + if(tmp_buf) + { + free_page((unsigned long)tmp_buf); + tmp_buf = 0; + } + printk(KERN_ERR "siolx: Couldn't register Aurora Asynchronous Adapter driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&siolx_callout_driver))) + { + if(tmp_buf) + { + free_page((unsigned long)tmp_buf); + tmp_buf = NULL; + } + tty_unregister_driver(&siolx_driver); + printk(KERN_ERR "siolx: Couldn't register Aurora Asynchronous Adapter callout driver, error = %d\n", + error); + return 1; + } + siolx_driver_registered = 1; + siolx_callout_driver_registered = 1; + return 0; /* success */ +} + +static int siolx_init_drivers(void) +{ + if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) + { + printk(KERN_ERR "siolx: Couldn't get free page.\n"); + return 1; + } + init_bh(siolx_bhindex, do_siolx_bh); + memset(&siolx_driver, 0, sizeof(siolx_driver)); + siolx_driver.magic = TTY_DRIVER_MAGIC; + + siolx_driver.driver_name = "aurasiolx"; + siolx_driver.name = "ttyS"; + siolx_driver.major = siolx_major; +#ifdef MODULE + siolx_driver.minor_start = siolx_minorstart; /* changed from command line */ +#else + siolx_driver.minor_start = GetMinorStart(); +#endif + siolx_driver.num = 0; /* will be changed */ + + siolx_driver.type = TTY_DRIVER_TYPE_SERIAL; + siolx_driver.subtype = SIOLX_TYPE_NORMAL; + siolx_driver.init_termios = tty_std_termios; + siolx_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + siolx_driver.flags = TTY_DRIVER_REAL_RAW; + siolx_driver.refcount = &siolx_refcount; + + siolx_driver.table = siolx_table; /* will be changed */ + siolx_driver.termios = siolx_termios; /* will be changed */ + siolx_driver.termios_locked = siolx_termios_locked; /* will be changed */ + + siolx_driver.open = siolx_open; + siolx_driver.close = siolx_close; + siolx_driver.write = siolx_write; + siolx_driver.put_char = siolx_put_char; + siolx_driver.flush_chars = siolx_flush_chars; + siolx_driver.write_room = siolx_write_room; + siolx_driver.chars_in_buffer = siolx_chars_in_buffer; + siolx_driver.flush_buffer = siolx_flush_buffer; + siolx_driver.ioctl = siolx_ioctl; + siolx_driver.throttle = siolx_throttle; + siolx_driver.unthrottle = siolx_unthrottle; + siolx_driver.set_termios = siolx_set_termios; + siolx_driver.stop = siolx_stop; + siolx_driver.start = siolx_start; + siolx_driver.hangup = siolx_hangup; + + siolx_callout_driver = siolx_driver; + siolx_callout_driver.name = "cuw"; + siolx_callout_driver.major = (siolx_major+1); + siolx_callout_driver.subtype = SIOLX_TYPE_CALLOUT; + + siolx_driver.read_proc = siolx_read_proc; + return 0; +} + + +static void siolx_release_drivers(void) +{ + unsigned int intr_val; + struct siolx_board *bp; + + if(tmp_buf) + { + free_page((unsigned long)tmp_buf); + tmp_buf = NULL; + } + if(siolx_driver_registered) + { + tty_unregister_driver(&siolx_driver); + siolx_driver_registered = 0; + } + if(siolx_callout_driver_registered) + { + tty_unregister_driver(&siolx_callout_driver); + siolx_callout_driver_registered = 0; + } + /* unallocate and turn off ints */ + for(intr_val = 0; intr_val < SIOLX_NUMINTS; ++intr_val) + { + if(SiolxIrqRoot[intr_val] != NULL) + { + for(bp = SiolxIrqRoot[intr_val]; bp != NULL; + bp = bp->next_by_interrupt) + { + SiolxShutdownBoard(bp); /* turn off int; release the plx vaddr space */ + } + free_irq(intr_val, &SiolxIrqRoot[intr_val]); + } + } + +} + +static void siolx_release_memory(void) +{ + register struct siolx_board *bp; + register struct siolx_port *port; + + while(siolx_board_root) + { + bp = siolx_board_root; + siolx_board_root = bp->next_by_global_list; + siolx_release_io_range(bp); /* releases the chip vaddr */ + kfree(bp); + } + while(siolx_port_root) + { + port = siolx_port_root; + if(port->xmit_buf) + { /* should have been done when port shutdown */ + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + siolx_port_root = port->next_by_global_list; + kfree(port); + } + if(siolx_table) + { + kfree(siolx_table); + siolx_table = NULL; + } + if(siolx_termios) + { + kfree(siolx_termios); + siolx_termios = NULL; + } + if(siolx_termios_locked) + { + kfree(siolx_termios_locked); + siolx_termios_locked = NULL; + } + +#ifdef SIOLX_TIMER + del_timer (&missed_irq_timer); +#endif +} + + +static void siolx_cleanup(void) +{ + siolx_release_drivers(); + siolx_release_memory(); +} + +/* + * This routine must be called by kernel at boot time + */ + +static int __init siolx_init(void) +{ + unsigned char bus; + unsigned char devfn; + struct siolx_board *bp; + struct siolx_board *bp2; + unsigned int boardcount; + struct pci_dev *pdev = NULL; + unsigned int ecntl; + unsigned int intr_val; + + printk(KERN_ALERT "aurora interea miseris mortalibus almam extulerat lucem\n"); + printk(KERN_ALERT " referens opera atque labores\n"); + printk(KERN_INFO "siolx: Siolx Aurora Asynchronous Adapter driver v" VERSION ", (c) Telford Tools, Inc.\n"); +#ifdef CONFIG_SIOLX_RTSCTS + printk (KERN_INFO "siolx: DTR/RTS pin is always RTS.\n"); +#else + printk (KERN_INFO "siolx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); +#endif + memset(SiolxIrqRoot, 0, sizeof(SiolxIrqRoot)); + tmp_buf = NULL; + siolx_board_root = NULL; /* clear out the global pointers */ + siolx_board_last = NULL; + siolx_port_root = NULL; + siolx_port_last = NULL; + NumSiolxPorts = 0; + siolx_table = NULL; /* make dynamic */ + siolx_termios = NULL; + siolx_termios_locked = NULL; + siolx_driver_registered = 0; + siolx_callout_driver_registered = 0; + + boardcount = 0; + + if (siolx_init_drivers()) + { + printk(KERN_INFO "siolx: Could not initialize drivers.\n"); + return -EIO; + } + + if (!pci_present()) + { + printk(KERN_INFO "siolx: Could not find PCI bus.\n"); + return -EIO; /* no PCI bus no Aurora cards */ + } + + while(1) + { + pdev = pci_find_device (siolx_vendor_id, siolx_device_id, pdev); + if (!pdev) + { + break; /* found no devices */ + } + + DEBUGPRINT((KERN_ALERT "%s\n", pdev->name)); + DEBUGPRINT((KERN_ALERT "subsystem vendor is %x.\n", + pdev->subsystem_vendor)); + DEBUGPRINT((KERN_ALERT "subsystem device is %x.\n", + pdev->subsystem_device)); + DEBUGPRINT((KERN_ALERT + "BAR0 = %lx\nBAR1 = %lx\nBAR2 = %lx\nBAR3 = %lx\nBAR4 = %lx\nBAR5 = %lx\n", + pci_resource_start(pdev, 0), + pci_resource_start(pdev, 1), + pci_resource_start(pdev, 2), + pci_resource_start(pdev, 3), + pci_resource_start(pdev, 4), + pci_resource_start(pdev, 5))); + DEBUGPRINT((KERN_ALERT + "LAS0 = %lx\nLAS1 = %lx\nLAS2 = %lx\nLAS3 = %lx\nLAS4 = %lx\nLAS5 = %lx\n", + pci_resource_len(pdev, 0), + pci_resource_len(pdev, 1), + pci_resource_len(pdev, 2), + pci_resource_len(pdev, 3), + pci_resource_len(pdev, 4), + pci_resource_len(pdev, 5))); + + if(pdev->subsystem_vendor == siolx_subsystem_vendor) + { + if(pdev->subsystem_device == siolx_subsystem_pci_device) + { + bp = (struct siolx_board*)kmalloc(sizeof(struct siolx_board), GFP_KERNEL); + if(bp == NULL) + { + printk(KERN_ALERT "siolx: Failed to create board structure on board %d.\n", boardcount); + break; /* no memory available */ + } + memset(bp, 0, sizeof(struct siolx_board)); + bp->boardtype = BD_8000P; + } + else if(pdev->subsystem_device == siolx_subsystem_cpci_device) + { + bp = (struct siolx_board*)kmalloc(sizeof(struct siolx_board), GFP_KERNEL); + if(bp == NULL) + { + printk(KERN_ALERT + "siolx: Failed to create board structure on board%p.\n", bp); + break; /* no memory available */ + } + memset(bp, 0, sizeof(struct siolx_board)); + bp->boardtype = BD_8000C; + } + else + { + continue; + } + } + else + { + continue; + } + + DEBUGPRINT((KERN_ALERT "siolx: interrupt is %i.\n", pdev->irq)); + bus = pdev->bus->number; + devfn = pdev->devfn; + DEBUGPRINT((KERN_ALERT "siolx: bus is %x, slot is %x.\n", bus, PCI_SLOT(devfn))); + + if (pci_enable_device(pdev)) + { + kfree(bp); + continue; /* enable failed */ + } + pci_set_master(pdev); + + bp->irq = pdev->irq; + SiolxResetBoard(bp, pdev); /* make sure the board is in a known state */ + if(bp->plx_vaddr == 0) + { + printk(KERN_ALERT "siolx: failed to remap plx address space.\n"); + kfree(bp); + continue; + } + bp->vaddr = (unsigned long) ioremap_nocache(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + if(bp->vaddr) + { + bp->base = (bp->vaddr + MPASYNC_CHIP1_OFFSET); + bp->boardnumber = boardcount; + if (siolx_probe(bp)) /* failure is nonzero */ + { + iounmap((void*)bp->plx_vaddr); + bp->plx_vaddr = 0; + iounmap((void*)bp->vaddr); + bp->vaddr = 0; + kfree(bp); /* something wrong with board */ + continue; + } + intr_val = bp->irq; + if((intr_val < 0) || (intr_val >= SIOLX_NUMINTS)) + { + printk(KERN_ALERT "siolx: bad interrupt %i board %p.\n", intr_val, bp); + iounmap((void*)bp->plx_vaddr); /* but plx space was remapped */ + bp->plx_vaddr = 0; + iounmap((void*)bp->vaddr); /* release chip space */ + bp->vaddr = 0; + kfree(bp); /* release the board structure */ + continue; + } + bp->next_by_interrupt = SiolxIrqRoot[intr_val]; + SiolxIrqRoot[intr_val] = bp; + if(siolx_board_last == NULL) + { + siolx_board_root = bp; + siolx_board_last = bp; + } + else + { + siolx_board_last->next_by_global_list = bp; + siolx_board_last = bp; + } + bp->chipnumber = 0; + bp->intstatus = bp->plx_vaddr + PLX_ICSR; + bp->next_by_chain = bp; /* one item chain */ + ecntl = readl(bp->plx_vaddr + PLX_ECNTL); + boardcount++; /* added a board */ + if(pci_resource_len(pdev, 2) > MPASYNC_CHIP2_OFFSET) + { + ++(bp->boardtype); /* works because how types are defined 8000X --> 16000X*/ + if(bp->boardtype == BD_16000C) + { + if((ecntl & PLX_ECNTLUSERI) == 0) + { + bp->reario = 1; + } + } + bp2 = (struct siolx_board*)kmalloc(sizeof(struct siolx_board), GFP_KERNEL); + if(bp2 == NULL) + { + printk(KERN_ALERT + "siolx: Failed to create second board structure on board %p.\n", bp); + /* fall through because must turn on ints for other chip */ + } + else + { + memset(bp2, 0, sizeof(struct siolx_board)); /* unnecessary */ + *bp2 = *bp; /* note that all guys in chain point to same next_by interrupt */ + bp->next_by_chain = bp2; /* circular list */ + bp2->next_by_chain = bp;/* now chain two elements*/ + ++(bp2->chipnumber); /* chipnumber 1 */ + bp2->base = (bp2->vaddr + MPASYNC_CHIP2_OFFSET); + if(siolx_probe(bp2)) + { + printk(KERN_ALERT "siolx: Failed to probe second board structure on board %p.\n", bp); + kfree(bp2); + /* fall through because must turn on ints for other chip */ + /* don't release pci memory remap -- still works for other chip */ + } + else if(siolx_board_last == NULL) + { + siolx_board_root = bp2; /* this case should not occur */ + siolx_board_last = bp2; + } + else + { + siolx_board_last->next_by_global_list = bp2; + siolx_board_last = bp2; + } + /* don't increment boardnumber */ + } + } + } + else /* could not remap the cd18xx space */ + { + iounmap((void*)bp->plx_vaddr); /* but plx space was remapped */ + bp->plx_vaddr = 0; + kfree(bp); + } + } + if (boardcount == 0) + { + printk(KERN_INFO "siolx: No Aurora Asynchronous Adapter boards detected.\n"); + siolx_cleanup(); /* don't need any allocated memory */ + return -EIO; + } + if (siolx_finish_init_drivers()) + { + printk(KERN_INFO "siolx: Could not finish driver initialization.\n"); + siolx_cleanup(); + return -EIO; + } + + for(intr_val = 0; intr_val < SIOLX_NUMINTS; ++intr_val) /* trying to install as few int handlers as possible */ + { /* one for each group of boards (actually chips) on a given irq */ + if(SiolxIrqRoot[intr_val] != NULL) + { + if (request_irq(intr_val, siolx_interrupt, SA_SHIRQ, "siolx Aurora Asynchronous Adapter", + &SiolxIrqRoot[intr_val]) == 0) + /* interrupts on perboard basis + * cycle through chips and then + * ports */ + /* NOTE PLX INTS ARE OFF -- so turn them on */ + { + for(bp = SiolxIrqRoot[intr_val]; bp != NULL; bp = bp->next_by_interrupt) + { + writel(PLX_ICSRLCLINTPCI | PLX_ICSRPCIINTS, bp->plx_vaddr + PLX_ICSR); /* enable interrupts */ + } + } + else + { + printk(KERN_ALERT "siolx: Unable to get interrupt, board set up not complete %i.\n", intr_val); + /* no interrupts but on all lists */ + } + } + } + return 0; +} + +module_init(siolx_init); +module_exit(siolx_cleanup); +MODULE_DESCRIPTION("multiport Aurora asynchronous driver"); +MODULE_AUTHOR("Joachim Martillo "); +MODULE_LICENSE("GPL"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cd1865/cdsiolx.h linux.21rc1-ac2/drivers/char/cd1865/cdsiolx.h --- linux.21rc1/drivers/char/cd1865/cdsiolx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cd1865/cdsiolx.h 2003-04-22 17:37:24.000000000 +0100 @@ -0,0 +1,136 @@ +/* -*- linux-c -*- */ +/* + * This file was modified from + * linux/drivers/char/siolx_io8.h -- + * Siolx IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) + * Modifications (C) 2002 Telford Tools, Inc. (martillo@telfordtools.com) + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * */ + +#ifndef __LINUX_SIOLX_H +#define __LINUX_SIOLX_H + +#include + +#ifdef __KERNEL__ + +#define SIOLX_NBOARD 8 + +/* eight ports per chip. */ +#define SIOLX_NPORT 8 +#define SIOLX_PORT(line) ((line) & (SIOLX_NPORT - 1)) + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SIOLX_OSCFREQ (33 MHz) +/* oregano is in /1 which mace 66Mhz is in /2 mode */ + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SIOLX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SIOLX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SIOLX_MAGIC 0x0907 + +#define SIOLX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ +#define SIOLX_NUMINTS 32 + +struct siolx_board +{ + unsigned long flags; + unsigned long base; + unsigned char irq; + unsigned char DTR; + unsigned long vaddr; + unsigned long plx_vaddr; + unsigned long intstatus; + struct siolx_board *next_by_chain; /* chains are circular */ + struct siolx_board *next_by_interrupt; /* only chip 0 */ + struct siolx_board *next_by_global_list; /* all boards not circular */ + struct siolx_port *portlist; + struct pci_dev pdev; + unsigned int chipnumber; /* for 8000X this structure really defines the board + * for 16000X the chain corresponds to a board and each + * structure corresponds to a dhip on a single board */ + unsigned int boardnumber; /* same for all boards/chips in a board chain */ + unsigned int boardtype; + unsigned int chiptype; + unsigned int chiprev; + unsigned int reario; + unsigned int rj45; +}; + +#define DRIVER_DEBUG() (siolx_debug) +#define DEBUGPRINT(arg) if(DRIVER_DEBUG()) printk arg + +struct siolx_port +{ + int magic; + int baud_base; + int flags; + struct tty_struct * tty; + int count; + int blocked_open; + int event; + int timeout; + int close_delay; + long session; + long pgrp; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + short wakeup_chars; + short break_length; + unsigned short closing_wait; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef SIOLX_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef SIOLX_REPORT_FIFO + unsigned long hits[10]; +#endif + struct siolx_port *next_by_global_list; + struct siolx_port *next_by_board; + struct siolx_board *board; + unsigned int boardport; /* relative to chain 0-15 for 16000X */ + unsigned int driverport; /* maps to minor device number */ +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SIOLX_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cd1865/Makefile linux.21rc1-ac2/drivers/char/cd1865/Makefile --- linux.21rc1/drivers/char/cd1865/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cd1865/Makefile 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,27 @@ +# Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc. +# +# 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. + +# File: drivers/net/WAN/atiXX50/Makefile +# +# Makefile for the Aurora ESSC based cards +# Specifically the 2520, 4020, 4520, 8520 +# + +all: SILX.o + +O_TARGET := SILX.o + +obj-y := cd1865.o +obj-m := $(O_TARGET) + +EXTRA_CFLAGS += -I. + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s *~ + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cd1865/plx9060.h linux.21rc1-ac2/drivers/char/cd1865/plx9060.h --- linux.21rc1/drivers/char/cd1865/plx9060.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cd1865/plx9060.h 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,97 @@ +#ifndef _PLX9060_H_ +#define _PLX9060_H_ +/* + * Aurora Cirrus CL-CD180/1865 Async Driver (sio16) + * + * This module contains the definitions for the PLX + * 9060SD PCI controller chip. + * + * COPYRIGHT (c) 1996-1998 BY AURORA TECHNOLOGIES, INC., WALTHAM, MA. + * Modifications Copyright (C) 2002 By Telford Tools, Inc., Boston, MA. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * file: plx9060.h + * author: cmw + * created: 11/21/1996 + * info: $Id: plx9060.h,v 1.2 2002/06/11 02:50:02 martillo Exp $ + */ + +/* + * $Log: plx9060.h,v $ + * Revision 1.2 2002/06/11 02:50:02 martillo + * using silx_ and SILX_ instead of sx_ and SX_ + * + * Revision 1.1 2002/05/21 17:30:16 martillo + * first pass for the sio16 driver. + * + * Revision 1.4 1999/02/12 15:38:13 bkd + * Changed PLX_ECNTUSER0 to PLX_ECNTLUSERO and added PLX_ECNTLUSERI. + * + * Revision 1.3 1998/03/23 19:35:42 bkd + * Added definitions for all of the missing PLX9060SD registers. + * + * Revision 1.2 1998/03/13 21:02:16 bkd + * Updated copyright date to include 1998. + * + * Revision 1.1 1996/11/23 01:07:46 bkd + * cmw/bkd (PCI port): + * Initial check-in. + * + */ + +/* + * Register definitions + */ + +#define PLX_LAS0RR 0x00 +#define PLX_LAS0BAR 0x04 +#define PLX_LAR 0x08 +#define PLX_ENDR 0x0c +#define PLX_EROMRR 0x10 +#define PLX_EROMBAR 0x14 +#define PLX_LAS0BRD 0x18 +#define PLX_LAS1RR 0x30 +#define PLX_LAS1BAR 0x34 +#define PLX_LAS1BRD 0x38 + +#define PLX_MBR0 0x40 +#define PLX_MBR1 0x44 +#define PLX_MBR2 0x48 +#define PLX_MBR3 0x4c +#define PLX_PCI2LCLDBR 0x60 +#define PLX_LCL2PCIDBR 0x64 +#define PLX_ICSR 0x68 +#define PLX_ECNTL 0x6c + +/* + * Bit definitions + */ + +#define PLX_ECNTLUSERO 0x00010000 /* turn on user output */ +#define PLX_ECNTLUSERI 0x00020000 /* user input */ +#define PLX_ECNTLLDREG 0x20000000 /* reload configuration registers */ +#define PLX_ECNTLLCLRST 0x40000000 /* local bus reset */ +#define PLX_ECNTLINITSTAT 0x80000000 /* mark board init'ed */ + + +#define PLX_ICSRLSERR_ENA 0x00000001 /* enable local bus LSERR# */ +#define PLX_ICSRLSERRP_ENA 0x00000002 /* enable local bus LSERR# PCI */ +#define PLX_ICSRPCIINTS 0x00000100 /* enable PCI interrupts */ +#define PLX_ICSRLCLINTPCI 0x00000800 +#define PLX_ICSRINTACTIVE 0x00008000 /* RO: local interrupt active */ + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cd1865/siolx.h linux.21rc1-ac2/drivers/char/cd1865/siolx.h --- linux.21rc1/drivers/char/cd1865/siolx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cd1865/siolx.h 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,94 @@ +/* -*- linux-c -*- */ +#ifndef _SIOLX_H_ +#define _SIOLX_H_ + +/* + * Modifications Copyright (C) 2002 By Telford Tools, Inc., Boston, MA. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + */ + +#define AURASUBSYSTEM_VENDOR_ID 0x125c +#define AURASUBSYSTEM_MPASYNCPCI 0x0640 +#define AURASUBSYSTEM_MPASYNCcPCI 0x0641 + +/* + * Aurora Cirrus CL-CD180/1865 Async Driver (sio16) + * + */ + +/* + * Register sets. These must match the order of the registers specified + * in the prom on the board! + */ + +#define MPASYNC_REG_CSR 1 +#define MPASYNC_REG_CD 2 + +#define MPASYNC_CHIP1_OFFSET 0x080 +#define MPASYNC_CHIP2_OFFSET 0x100 + +#define MPASYNC_REG_NO_OBP_CSR 1 +#define MPASYNC_REG_NO_OBP_CD 3 + +#define TX_FIFO 0x8 /* how deep is the chip fifo */ + +/* + * state flags + */ + +/* + * the following defines the model types + */ + +#define OREGANO_MODEL(mod) ((mod) == BD_16000P || (mod) == BD_8000P) +#define MACE_MODEL(mod) ((mod) == BD_16000C || (mod) == BD_8000C) + +/* + * I/O options: + */ + +#define MACE8_STD 0x0 /* 8000CP -- standard I/O */ +#define MACE8_RJ45 0x1 /* 8000CP -- rear RJ45 I/O */ + +#define MACE16_STD 0x0 /* 16000CP -- standard I/O */ +#define MACE16_RJ45 0x1 /* 16000CP -- rear RJ45 I/O */ + +#define SE2_CLK ((unsigned int) 11059200) /* 11.0592 MHz */ +#define SE_CLK ((unsigned int) 14745600) /* 14.7456 MHz */ +#define SE3_CLK ((unsigned int) 33000000) /* 33.3333 MHz */ + +/* divide x by y, rounded */ +#define ROUND_DIV(x, y) (((x) + ((y) >> 1)) / (y)) + +/* Calculate a 16 bit baud rate divisor for the given "encoded" + * (multiplied by two) baud rate. + */ + +/* chip types: */ +#define CT_UNKNOWN 0x0 /* unknown */ +#define CT_CL_CD180 0x1 /* Cirrus Logic CD-180 */ +#define CT_CL_CD1864 0x2 /* Cirrus Logic CD-1864 */ +#define CT_CL_CD1865 0x3 /* Cirrus Logic CD-1864 */ + +/* chip revisions: */ +#define CR_UNKNOWN 0x0 /* unknown */ +#define CR_REVA 0x1 /* revision A */ +#define CR_REVB 0x2 /* revision B */ +#define CR_REVC 0x3 /* revision C */ +/* ...and so on ... */ + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/Config.in linux.21rc1-ac2/drivers/char/Config.in --- linux.21rc1/drivers/char/Config.in 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/Config.in 2003-04-22 16:44:36.000000000 +0100 @@ -35,6 +35,7 @@ fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then + tristate ' Aurora Technology, Inc. asynchronous PCI cards V2' CONFIG_ATI_CD1865 tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate ' Comtrol Rocketport support' CONFIG_ROCKETPORT tristate ' Cyclades async mux support' CONFIG_CYCLADES diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/console.c linux.21rc1-ac2/drivers/char/console.c --- linux.21rc1/drivers/char/console.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/console.c 2003-04-22 16:44:36.000000000 +0100 @@ -2393,7 +2393,7 @@ tty->winsize.ws_row = video_num_lines; tty->winsize.ws_col = video_num_columns; } - if (tty->count == 1) + if (atomic_read(&tty->count) == 1) vcs_make_devfs (currcons, 0); return 0; } @@ -2402,7 +2402,7 @@ { if (!tty) return; - if (tty->count != 1) return; + if (atomic_read(&tty->count) != 1) return; vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1); tty->driver_data = 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/cyclades.c linux.21rc1-ac2/drivers/char/cyclades.c --- linux.21rc1/drivers/char/cyclades.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/cyclades.c 2003-04-22 16:44:36.000000000 +0100 @@ -2822,7 +2822,7 @@ #ifdef CY_DEBUG_OPEN printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/Config.in linux.21rc1-ac2/drivers/char/drm/Config.in --- linux.21rc1/drivers/char/drm/Config.in 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/Config.in 2003-04-22 16:44:36.000000000 +0100 @@ -6,9 +6,9 @@ # tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX -#tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA +tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA tristate ' ATI Rage 128' CONFIG_DRM_R128 -dep_tristate ' ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP +tristate ' ATI Radeon' CONFIG_DRM_RADEON dep_tristate ' Intel I810' CONFIG_DRM_I810 $CONFIG_AGP dep_mbool ' Enabled XFree 4.1 ioctl interface by default' CONFIG_DRM_I810_XFREE_41 $CONFIG_DRM_I810 dep_tristate ' Intel 830M' CONFIG_DRM_I830 $CONFIG_AGP diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_agpsupport.h linux.21rc1-ac2/drivers/char/drm/drm_agpsupport.h --- linux.21rc1/drivers/char/drm/drm_agpsupport.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_agpsupport.h 2003-04-25 16:27:10.000000000 +0100 @@ -283,8 +283,6 @@ break; case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; break; - case VIA_APOLLO_P4X400: head->chipset = "VIA Apollo P4X400"; - break; case SIS_GENERIC: head->chipset = "SiS"; break; case AMD_GENERIC: head->chipset = "AMD"; break; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_bufs.h linux.21rc1-ac2/drivers/char/drm/drm_bufs.h --- linux.21rc1/drivers/char/drm/drm_bufs.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_bufs.h 2003-04-25 16:27:10.000000000 +0100 @@ -136,6 +136,7 @@ } map->offset = (unsigned long)map->handle; if ( map->flags & _DRM_CONTAINS_LOCK ) { + dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */ } break; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_context.h linux.21rc1-ac2/drivers/char/drm/drm_context.h --- linux.21rc1/drivers/char/drm/drm_context.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_context.h 2003-04-25 16:27:10.000000000 +0100 @@ -554,7 +554,7 @@ /* Allocate a new queue */ down(&dev->struct_sem); - queue = gamma_alloc(sizeof(*queue), DRM_MEM_QUEUES); + queue = DRM(alloc)(sizeof(*queue), DRM_MEM_QUEUES); memset(queue, 0, sizeof(*queue)); atomic_set(&queue->use_count, 1); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_dma.h linux.21rc1-ac2/drivers/char/drm/drm_dma.h --- linux.21rc1/drivers/char/drm/drm_dma.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_dma.h 2003-04-25 16:27:10.000000000 +0100 @@ -30,7 +30,7 @@ */ #include "drmP.h" - +#include "drm_os_linux.h" #include /* For task queue support */ #ifndef __HAVE_DMA_WAITQUEUE @@ -537,8 +537,18 @@ dev->tq.data = dev; #endif +#if __HAVE_VBL_IRQ + init_waitqueue_head(&dev->vbl_queue); + + spin_lock_init( &dev->vbl_lock ); + + INIT_LIST_HEAD( &dev->vbl_sigs.head ); + + dev->vbl_pending = 0; +#endif + /* Before installing handler */ - DRIVER_PREINSTALL(); + DRM(driver_irq_preinstall)(dev); /* Install handler */ ret = request_irq( dev->irq, DRM(dma_service), @@ -551,7 +561,7 @@ } /* After installing handler */ - DRIVER_POSTINSTALL(); + DRM(driver_irq_postinstall)(dev); return 0; } @@ -570,7 +580,7 @@ DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq ); - DRIVER_UNINSTALL(); + DRM(driver_irq_uninstall)( dev ); free_irq( irq, dev ); @@ -597,6 +607,142 @@ } } +#if __HAVE_VBL_IRQ + +int DRM(wait_vblank)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_wait_vblank_t vblwait; + struct timeval now; + int ret = 0; + unsigned int flags; + + if (!dev->irq) + return -EINVAL; + + DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data, + sizeof(vblwait) ); + + switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) { + case _DRM_VBLANK_RELATIVE: + vblwait.request.sequence += atomic_read( &dev->vbl_received ); + vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; + case _DRM_VBLANK_ABSOLUTE: + break; + default: + return -EINVAL; + } + + flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + + if ( flags & _DRM_VBLANK_SIGNAL ) { + unsigned long irqflags; + drm_vbl_sig_t *vbl_sig; + + vblwait.reply.sequence = atomic_read( &dev->vbl_received ); + + spin_lock_irqsave( &dev->vbl_lock, irqflags ); + + /* Check if this task has already scheduled the same signal + * for the same vblank sequence number; nothing to be done in + * that case + */ + list_for_each( ( (struct list_head *) vbl_sig ), &dev->vbl_sigs.head ) { + if (vbl_sig->sequence == vblwait.request.sequence + && vbl_sig->info.si_signo == vblwait.request.signal + && vbl_sig->task == current) + { + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + goto done; + } + } + + if ( dev->vbl_pending >= 100 ) { + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + return -EBUSY; + } + + dev->vbl_pending++; + + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + + if ( !( vbl_sig = kmalloc(sizeof(drm_vbl_sig_t), GFP_KERNEL) ) ) + return -ENOMEM; + + + memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); + + vbl_sig->sequence = vblwait.request.sequence; + vbl_sig->info.si_signo = vblwait.request.signal; + vbl_sig->task = current; + + spin_lock_irqsave( &dev->vbl_lock, irqflags ); + + list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); + + spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); + } else { + ret = DRM(vblank_wait)( dev, &vblwait.request.sequence ); + + do_gettimeofday( &now ); + vblwait.reply.tval_sec = now.tv_sec; + vblwait.reply.tval_usec = now.tv_usec; + } + +done: + DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, + sizeof(vblwait) ); + + return ret; +} + +void DRM(vbl_send_signals)( drm_device_t *dev ) +{ + struct list_head *tmp; + drm_vbl_sig_t *vbl_sig; + unsigned int vbl_seq = atomic_read( &dev->vbl_received ); + unsigned long flags; + + spin_lock_irqsave( &dev->vbl_lock, flags ); + + list_for_each_safe( ( (struct list_head *) vbl_sig ), tmp, &dev->vbl_sigs.head ) { + if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { + vbl_sig->info.si_code = vbl_seq; + send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); + + list_del( (struct list_head *) vbl_sig ); + + + kfree( vbl_sig ); + dev->vbl_pending--; + } + } + + spin_unlock_irqrestore( &dev->vbl_lock, flags ); +} + +#endif /* __HAVE_VBL_IRQ */ + +#else + +int DRM(control)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_control_t ctl; + + if ( copy_from_user( &ctl, (drm_control_t *)arg, sizeof(ctl) ) ) + return -EFAULT; + + switch ( ctl.func ) { + case DRM_INST_HANDLER: + case DRM_UNINST_HANDLER: + return 0; + default: + return -EINVAL; + } +} + #endif /* __HAVE_DMA_IRQ */ #endif /* __HAVE_DMA */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_drv.h linux.21rc1-ac2/drivers/char/drm/drm_drv.h --- linux.21rc1/drivers/char/drm/drm_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -115,18 +115,34 @@ #ifndef DRIVER_FOPS #define DRIVER_FOPS \ static struct file_operations DRM(fops) = { \ - owner: THIS_MODULE, \ - open: DRM(open), \ - flush: DRM(flush), \ - release: DRM(release), \ - ioctl: DRM(ioctl), \ - mmap: DRM(mmap), \ - read: DRM(read), \ - fasync: DRM(fasync), \ - poll: DRM(poll), \ + .owner = THIS_MODULE, \ + .open = DRM(open), \ + .flush = DRM(flush), \ + .release = DRM(release), \ + .ioctl = DRM(ioctl), \ + .mmap = DRM(mmap), \ + .read = DRM(read), \ + .fasync = DRM(fasync), \ + .poll = DRM(poll), \ } #endif +#ifndef MODULE +/* DRM(options) is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ +/* Use an additional macro to avoid preprocessor troubles */ +#define DRM_OPTIONS_FUNC DRM(options) +static int __init DRM(options)( char *str ) +{ + DRM(parse_options)( str ); + return 1; +} + +__setup( DRIVER_NAME "=", DRM_OPTIONS_FUNC ); +#undef DRM_OPTIONS_FUNC +#endif /* * The default number of instances (minor numbers) to initialize. @@ -187,10 +203,8 @@ /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */ -#if __HAVE_DMA_IRQ [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { DRM(control), 1, 1 }, #endif -#endif #if __REALLY_HAVE_AGP [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { DRM(agp_acquire), 1, 1 }, @@ -208,6 +222,10 @@ [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { DRM(sg_free), 1, 1 }, #endif +#if __HAVE_VBL_IRQ + [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = { DRM(wait_vblank), 0, 0 }, +#endif + DRIVER_IOCTLS }; @@ -292,7 +310,7 @@ dev->map_count = 0; dev->vmalist = NULL; - dev->lock.hw_lock = NULL; + dev->sigdata.lock = dev->lock.hw_lock = NULL; init_waitqueue_head( &dev->lock.lock_queue ); dev->queue_count = 0; dev->queue_reserved = 0; @@ -477,7 +495,7 @@ DRM(dma_takedown)( dev ); #endif if ( dev->lock.hw_lock ) { - dev->lock.hw_lock = NULL; /* SHM removed */ + dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */ dev->lock.pid = 0; wake_up_interruptible( &dev->lock.lock_queue ); } @@ -705,7 +723,7 @@ int i; for (i = 0; i < DRM(numdevs); i++) { - if (MINOR(inode->i_rdev) == DRM(minor)[i]) { + if (minor(inode->i_rdev) == DRM(minor)[i]) { dev = &(DRM(device)[i]); break; } @@ -747,8 +765,8 @@ * Begin inline drm_release */ - DRM_DEBUG( "pid = %d, device = 0x%x, open_count = %d\n", - current->pid, dev->device, dev->open_count ); + DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n", + current->pid, (long)dev->device, dev->open_count ); if ( dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && @@ -873,8 +891,9 @@ atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] ); ++priv->ioctl_count; - DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%x, auth=%d\n", - current->pid, cmd, nr, dev->device, priv->authenticated ); + DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n", + current->pid, cmd, nr, (long)dev->device, + priv->authenticated ); if ( nr >= DRIVER_IOCTL_COUNT ) { retcode = -EINVAL; @@ -1027,6 +1046,25 @@ atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] ); +#if __HAVE_KERNEL_CTX_SWITCH + /* We no longer really hold it, but if we are the next + * agent to request it then we should just be able to + * take it immediately and not eat the ioctl. + */ + dev->lock.pid = 0; + { + __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; + unsigned int old, new, prev, ctx; + + ctx = lock.context; + do { + old = *plock; + new = ctx; + prev = cmpxchg(plock, old, new); + } while (prev != old); + } + wake_up_interruptible(&dev->lock.lock_queue); +#else DRM(lock_transfer)( dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT ); #if __HAVE_DMA_SCHEDULE @@ -1041,6 +1079,7 @@ DRM_ERROR( "\n" ); } } +#endif /* !__HAVE_KERNEL_CTX_SWITCH */ unblock_all_signals(); return 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_fops.h linux.21rc1-ac2/drivers/char/drm/drm_fops.h --- linux.21rc1/drivers/char/drm/drm_fops.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_fops.h 2003-04-25 16:27:10.000000000 +0100 @@ -37,7 +37,7 @@ int DRM(open_helper)(struct inode *inode, struct file *filp, drm_device_t *dev) { - kdev_t minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); drm_file_t *priv; if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ @@ -94,25 +94,8 @@ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", - current->pid, dev->device, dev->open_count); - if ( dev->lock.hw_lock && - _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && - dev->lock.pid == current->pid ) { - DRM_DEBUG( "Process %d closed fd, freeing lock for context %d\n", - current->pid, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); -#if __HAVE_RELEASE - DRIVER_RELEASE(); -#endif - DRM(lock_free)( dev, &dev->lock.hw_lock->lock, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); - - /* FIXME: may require heavy-handed reset of - hardware at this point, possibly - processed via a callback to the X - server. */ - } + DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", + current->pid, (long)dev->device, dev->open_count); return 0; } @@ -122,7 +105,7 @@ drm_device_t *dev = priv->dev; int retcode; - DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, (long)dev->device); retcode = fasync_helper(fd, filp, on, &dev->buf_async); if (retcode < 0) return retcode; return 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm.h linux.21rc1-ac2/drivers/char/drm/drm.h --- linux.21rc1/drivers/char/drm/drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -38,10 +38,27 @@ #if defined(__linux__) #include #include /* For _IO* macros */ -#define DRM_IOCTL_NR(n) _IOC_NR(n) -#elif defined(__FreeBSD__) +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IOC_VOID _IOC_NONE +#define DRM_IOC_READ _IOC_READ +#define DRM_IOC_WRITE _IOC_WRITE +#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) +#elif defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__FreeBSD__) && defined(XFree86Server) +/* Prevent name collision when including sys/ioccom.h */ +#undef ioctl #include -#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define ioctl(a,b,c) xf86ioctl(a,b,c) +#else +#include +#endif /* __FreeBSD__ && xf86ioctl */ +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define DRM_IOC_VOID IOC_VOID +#define DRM_IOC_READ IOC_OUT +#define DRM_IOC_WRITE IOC_IN +#define DRM_IOC_READWRITE IOC_INOUT +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) #endif #define XFREE86_VERSION(major,minor,patch,snap) \ @@ -84,6 +101,10 @@ /* Warning: If you change this structure, make sure you change * XF86DRIClipRectRec in the server as well */ +/* KW: Actually it's illegal to change either for + * backwards-compatibility reasons. + */ + typedef struct drm_clip_rect { unsigned short x1; unsigned short y1; @@ -332,6 +353,32 @@ int funcnum; } drm_irq_busid_t; +typedef enum { + _DRM_VBLANK_ABSOLUTE = 0x0, /* Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /* Wait for given number of vblanks */ + _DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ +} drm_vblank_seq_type_t; + +#define _DRM_VBLANK_FLAGS_MASK _DRM_VBLANK_SIGNAL + +struct drm_wait_vblank_request { + drm_vblank_seq_type_t type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { + drm_vblank_seq_type_t type; + unsigned int sequence; + long tval_sec; + long tval_usec; +}; + +typedef union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +} drm_wait_vblank_t; + typedef struct drm_agp_mode { unsigned long mode; } drm_agp_mode_t; @@ -371,10 +418,9 @@ #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) -#define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) -#define DRM_IOW(nr,size) _IOW(DRM_IOCTL_BASE,nr,size) -#define DRM_IOWR(nr,size) _IOWR(DRM_IOCTL_BASE,nr,size) - +#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) +#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) +#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) #define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t) #define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t) @@ -427,86 +473,10 @@ #define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, drm_scatter_gather_t) #define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, drm_scatter_gather_t) -/* MGA specific ioctls */ -#define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) -#define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x41, drm_lock_t) -#define DRM_IOCTL_MGA_RESET DRM_IO( 0x42) -#define DRM_IOCTL_MGA_SWAP DRM_IO( 0x43) -#define DRM_IOCTL_MGA_CLEAR DRM_IOW( 0x44, drm_mga_clear_t) -#define DRM_IOCTL_MGA_VERTEX DRM_IOW( 0x45, drm_mga_vertex_t) -#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t) -#define DRM_IOCTL_MGA_ILOAD DRM_IOW( 0x47, drm_mga_iload_t) -#define DRM_IOCTL_MGA_BLIT DRM_IOW( 0x48, drm_mga_blit_t) - -/* i810 specific ioctls */ -#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t) -#define DRM_IOCTL_I810_VERTEX DRM_IOW( 0x41, drm_i810_vertex_t) -#define DRM_IOCTL_I810_CLEAR DRM_IOW( 0x42, drm_i810_clear_t) -#define DRM_IOCTL_I810_FLUSH DRM_IO( 0x43) -#define DRM_IOCTL_I810_GETAGE DRM_IO( 0x44) -#define DRM_IOCTL_I810_GETBUF DRM_IOWR(0x45, drm_i810_dma_t) -#define DRM_IOCTL_I810_SWAP DRM_IO( 0x46) -#define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t) -#define DRM_IOCTL_I810_DOCOPY DRM_IO( 0x48) -#define DRM_IOCTL_I810_OV0INFO DRM_IOR( 0x49, drm_i810_overlay_t) -#define DRM_IOCTL_I810_FSTATUS DRM_IO ( 0x4a) -#define DRM_IOCTL_I810_OV0FLIP DRM_IO ( 0x4b) -#define DRM_IOCTL_I810_MC DRM_IOW( 0x4c, drm_i810_mc_t) -#define DRM_IOCTL_I810_RSTATUS DRM_IO ( 0x4d ) - - -/* Rage 128 specific ioctls */ -#define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t) -#define DRM_IOCTL_R128_CCE_START DRM_IO( 0x41) -#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( 0x42, drm_r128_cce_stop_t) -#define DRM_IOCTL_R128_CCE_RESET DRM_IO( 0x43) -#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( 0x44) -#define DRM_IOCTL_R128_RESET DRM_IO( 0x46) -#define DRM_IOCTL_R128_SWAP DRM_IO( 0x47) -#define DRM_IOCTL_R128_CLEAR DRM_IOW( 0x48, drm_r128_clear_t) -#define DRM_IOCTL_R128_VERTEX DRM_IOW( 0x49, drm_r128_vertex_t) -#define DRM_IOCTL_R128_INDICES DRM_IOW( 0x4a, drm_r128_indices_t) -#define DRM_IOCTL_R128_BLIT DRM_IOW( 0x4b, drm_r128_blit_t) -#define DRM_IOCTL_R128_DEPTH DRM_IOW( 0x4c, drm_r128_depth_t) -#define DRM_IOCTL_R128_STIPPLE DRM_IOW( 0x4d, drm_r128_stipple_t) -#define DRM_IOCTL_R128_INDIRECT DRM_IOWR(0x4f, drm_r128_indirect_t) -#define DRM_IOCTL_R128_FULLSCREEN DRM_IOW( 0x50, drm_r128_fullscreen_t) - -/* Radeon specific ioctls */ -#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( 0x40, drm_radeon_init_t) -#define DRM_IOCTL_RADEON_CP_START DRM_IO( 0x41) -#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( 0x42, drm_radeon_cp_stop_t) -#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( 0x43) -#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( 0x44) -#define DRM_IOCTL_RADEON_RESET DRM_IO( 0x45) -#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( 0x46, drm_radeon_fullscreen_t) -#define DRM_IOCTL_RADEON_SWAP DRM_IO( 0x47) -#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( 0x48, drm_radeon_clear_t) -#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( 0x49, drm_radeon_vertex_t) -#define DRM_IOCTL_RADEON_INDICES DRM_IOW( 0x4a, drm_radeon_indices_t) -#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( 0x4c, drm_radeon_stipple_t) -#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(0x4d, drm_radeon_indirect_t) -#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(0x4e, drm_radeon_texture_t) - -/* SiS specific ioctls */ -#define SIS_IOCTL_FB_ALLOC DRM_IOWR(0x44, drm_sis_mem_t) -#define SIS_IOCTL_FB_FREE DRM_IOW( 0x45, drm_sis_mem_t) -#define SIS_IOCTL_AGP_INIT DRM_IOWR(0x53, drm_sis_agp_t) -#define SIS_IOCTL_AGP_ALLOC DRM_IOWR(0x54, drm_sis_mem_t) -#define SIS_IOCTL_AGP_FREE DRM_IOW( 0x55, drm_sis_mem_t) -#define SIS_IOCTL_FLIP DRM_IOW( 0x48, drm_sis_flip_t) -#define SIS_IOCTL_FLIP_INIT DRM_IO( 0x49) -#define SIS_IOCTL_FLIP_FINAL DRM_IO( 0x50) - -/* I830 specific ioctls */ -#define DRM_IOCTL_I830_INIT DRM_IOW( 0x40, drm_i830_init_t) -#define DRM_IOCTL_I830_VERTEX DRM_IOW( 0x41, drm_i830_vertex_t) -#define DRM_IOCTL_I830_CLEAR DRM_IOW( 0x42, drm_i830_clear_t) -#define DRM_IOCTL_I830_FLUSH DRM_IO ( 0x43) -#define DRM_IOCTL_I830_GETAGE DRM_IO ( 0x44) -#define DRM_IOCTL_I830_GETBUF DRM_IOWR(0x45, drm_i830_dma_t) -#define DRM_IOCTL_I830_SWAP DRM_IO ( 0x46) -#define DRM_IOCTL_I830_COPY DRM_IOW( 0x47, drm_i830_copy_t) -#define DRM_IOCTL_I830_DOCOPY DRM_IO ( 0x48) +#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, drm_wait_vblank_t) + +/* Device specfic ioctls should only be in their respective headers + * The device specific ioctl range is 0x40 to 0x79. */ +#define DRM_COMMAND_BASE 0x40 #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_ioctl.h linux.21rc1-ac2/drivers/char/drm/drm_ioctl.h --- linux.21rc1/drivers/char/drm/drm_ioctl.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_ioctl.h 2003-04-25 16:27:10.000000000 +0100 @@ -111,7 +111,7 @@ do { struct pci_dev *pci_dev; - int b, d, f; + int domain, b, d, f; char *p; for(p = dev->unique; p && *p && *p != ':'; p++); @@ -123,6 +123,27 @@ f = (int)simple_strtoul(p+1, &p, 10); if (*p) break; + domain = b >> 8; + b &= 0xff; + +#ifdef __alpha__ + /* + * Find the hose the device is on (the domain number is the + * hose index) and offset the bus by the root bus of that + * hose. + */ + for(pci_dev = pci_find_device(PCI_ANY_ID,PCI_ANY_ID,NULL); + pci_dev; + pci_dev = pci_find_device(PCI_ANY_ID,PCI_ANY_ID,pci_dev)) { + struct pci_controller *hose = pci_dev->sysdata; + + if (hose->index == domain) { + b += hose->bus->number; + break; + } + } +#endif + pci_dev = pci_find_slot(b, PCI_DEVFN(d,f)); if (pci_dev) { dev->pdev = pci_dev; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_lock.h linux.21rc1-ac2/drivers/char/drm/drm_lock.h --- linux.21rc1/drivers/char/drm/drm_lock.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_lock.h 2003-04-25 16:27:10.000000000 +0100 @@ -236,7 +236,7 @@ /* Allow signal delivery if lock isn't held */ - if (!_DRM_LOCK_IS_HELD(s->lock->lock) + if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock) || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1; /* Otherwise, set flag to force call to diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_memory.h linux.21rc1-ac2/drivers/char/drm/drm_memory.h --- linux.21rc1/drivers/char/drm/drm_memory.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_memory.h 2003-04-25 16:27:10.000000000 +0100 @@ -313,6 +313,29 @@ return pt; } +void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap_nocache(offset, size))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&DRM(mem_lock)); + return pt; +} + void DRM(ioremapfree)(void *pt, unsigned long size) { int alloc_count; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_os_linux.h linux.21rc1-ac2/drivers/char/drm/drm_os_linux.h --- linux.21rc1/drivers/char/drm/drm_os_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_os_linux.h 2003-04-25 16:05:08.000000000 +0100 @@ -0,0 +1,56 @@ +#define __NO_VERSION__ + +#include /* For task queue support */ +#include + + +/* For data going from/to the kernel through the ioctl argument */ +#define DRM_COPY_FROM_USER_IOCTL(arg1, arg2, arg3) \ + if ( copy_from_user(&arg1, arg2, arg3) ) \ + return -EFAULT +#define DRM_COPY_TO_USER_IOCTL(arg1, arg2, arg3) \ + if ( copy_to_user(arg1, &arg2, arg3) ) \ + return -EFAULT + + +#warning the author of this code needs to read up on list_entry +#define DRM_GETSAREA() \ +do { \ + struct list_head *list; \ + list_for_each( list, &dev->maplist->head ) { \ + drm_map_list_t *entry = (drm_map_list_t *)list; \ + if ( entry->map && \ + entry->map->type == _DRM_SHM && \ + (entry->map->flags & _DRM_CONTAINS_LOCK) ) { \ + dev_priv->sarea = entry->map; \ + break; \ + } \ + } \ +} while (0) + +#define DRM_WAIT_ON( ret, queue, timeout, condition ) \ +do { \ + DECLARE_WAITQUEUE(entry, current); \ + unsigned long end = jiffies + (timeout); \ + add_wait_queue(&(queue), &entry); \ + \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if((signed)(end - jiffies) <= 0) { \ + ret = -EBUSY; \ + break; \ + } \ + schedule_timeout((HZ/100 > 1) ? HZ/100 : 1); \ + if (signal_pending(current)) { \ + ret = -EINTR; \ + break; \ + } \ + } \ + set_current_state(TASK_RUNNING); \ + remove_wait_queue(&(queue), &entry); \ +} while (0) + + + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drmP.h linux.21rc1-ac2/drivers/char/drm/drmP.h --- linux.21rc1/drivers/char/drm/drmP.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drmP.h 2003-04-25 16:27:10.000000000 +0100 @@ -53,6 +53,7 @@ #include #include /* For (un)lock_kernel */ #include +#include #if defined(__alpha__) || defined(__powerpc__) #include /* For pte_wrprotect */ #endif @@ -71,10 +72,7 @@ #include #include "drm.h" -/* page_to_bus for earlier kernels, not optimal in all cases */ -#ifndef page_to_bus -#define page_to_bus(page) ((unsigned int)(virt_to_bus(page_address(page)))) -#endif +#include "drm_os_linux.h" /* DRM template customization defaults */ @@ -209,6 +207,7 @@ (unsigned long)(n),sizeof(*(ptr)))) #endif /* i386 & alpha */ #endif +#define __REALLY_HAVE_SG (__HAVE_SG) /* Begin the DRM... */ @@ -251,41 +250,58 @@ #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) -#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) + /* Backward compatibility section */ +#ifndef minor +#define minor(x) MINOR((x)) +#endif -/* Macros to make printk easier */ +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(x) +#endif -#if ( __GNUC__ > 2 ) -#define DRM_ERROR(fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __FUNCTION__, ##arg) -#define DRM_MEM_ERROR(area, fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __FUNCTION__, \ - DRM(mem_stats)[area].name , ##arg) -#define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) +#ifndef pte_offset_map +#define pte_offset_map pte_offset +#define pte_unmap(pte) +#endif -#if DRM_DEBUG_CODE -#define DRM_DEBUG(fmt, arg...) \ - do { \ - if ( DRM(flags) & DRM_FLAG_DEBUG ) \ - printk(KERN_DEBUG \ - "[" DRM_NAME ":%s] " fmt , \ - __FUNCTION__, \ - ##arg); \ - } while (0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +static inline struct page * vmalloc_to_page(void * vmalloc_addr) +{ + unsigned long addr = (unsigned long) vmalloc_addr; + struct page *page = NULL; + pgd_t *pgd = pgd_offset_k(addr); + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, addr); + if (!pmd_none(*pmd)) { + ptep = pte_offset_map(pmd, addr); + pte = *ptep; + if (pte_present(pte)) + page = pte_page(pte); + pte_unmap(ptep); + } + } + return page; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#define DRM_RPR_ARG(vma) #else -#define DRM_DEBUG(fmt, arg...) do { } while (0) +#define DRM_RPR_ARG(vma) vma, #endif -#else /* Gcc 2.x */ -/* Work around a C preprocessor bug */ +#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) /* Macros to make printk easier */ #define DRM_ERROR(fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) + printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __FUNCTION__ , ##arg) #define DRM_MEM_ERROR(area, fmt, arg...) \ - printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \ + printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __FUNCTION__, \ DRM(mem_stats)[area].name , ##arg) #define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) @@ -294,16 +310,13 @@ do { \ if ( DRM(flags) & DRM_FLAG_DEBUG ) \ printk(KERN_DEBUG \ - "[" DRM_NAME ":" __FUNCTION__ "] " fmt , \ - ##arg); \ + "[" DRM_NAME ":%s] " fmt , \ + __FUNCTION__ , ##arg); \ } while (0) #else #define DRM_DEBUG(fmt, arg...) do { } while (0) #endif -#endif /* Gcc 2.x */ - - #define DRM_PROC_LIMIT (PAGE_SIZE-80) #define DRM_PROC_PRINT(fmt, arg...) \ @@ -318,6 +331,9 @@ #define DRM_IOREMAP(map) \ (map)->handle = DRM(ioremap)( (map)->offset, (map)->size ) +#define DRM_IOREMAP_NOCACHE(map) \ + (map)->handle = DRM(ioremap_nocache)((map)->offset, (map)->size) + #define DRM_IOREMAPFREE(map) \ do { \ if ( (map)->handle && (map)->size ) \ @@ -599,6 +615,17 @@ drm_map_t *map; } drm_map_list_t; +#if __HAVE_VBL_IRQ + +typedef struct drm_vbl_sig { + struct list_head head; + unsigned int sequence; + struct siginfo info; + struct task_struct *task; +} drm_vbl_sig_t; + +#endif + typedef struct drm_device { const char *name; /* Simple driver name */ char *unique; /* Unique identifier: e.g., busid */ @@ -658,6 +685,13 @@ int last_context; /* Last current context */ unsigned long last_switch; /* jiffies at last context switch */ struct tq_struct tq; +#if __HAVE_VBL_IRQ + wait_queue_head_t vbl_queue; + atomic_t vbl_received; + spinlock_t vbl_lock; + drm_vbl_sig_t vbl_sigs; + unsigned int vbl_pending; +#endif cycles_t ctx_start; cycles_t lck_start; #if __HAVE_DMA_HISTOGRAM @@ -725,16 +759,16 @@ /* Mapping support (drm_vm.h) */ extern struct page *DRM(vm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused); + int write_access); extern void DRM(vm_open)(struct vm_area_struct *vma); extern void DRM(vm_close)(struct vm_area_struct *vma); extern void DRM(vm_shm_close)(struct vm_area_struct *vma); @@ -756,6 +790,7 @@ extern void DRM(free_pages)(unsigned long address, int order, int area); extern void *DRM(ioremap)(unsigned long offset, unsigned long size); +extern void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size); extern void DRM(ioremapfree)(void *pt, unsigned long size); #if __REALLY_HAVE_AGP @@ -885,6 +920,15 @@ extern int DRM(irq_uninstall)( drm_device_t *dev ); extern void DRM(dma_service)( int irq, void *device, struct pt_regs *regs ); +extern void DRM(driver_irq_preinstall)( drm_device_t *dev ); +extern void DRM(driver_irq_postinstall)( drm_device_t *dev ); +extern void DRM(driver_irq_uninstall)( drm_device_t *dev ); +#if __HAVE_VBL_IRQ +extern int DRM(wait_vblank)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int DRM(vblank_wait)(drm_device_t *dev, unsigned int *vbl_seq); +extern void DRM(vbl_send_signals)( drm_device_t *dev ); +#endif #if __HAVE_DMA_IRQ_BH extern void DRM(dma_immediate_bh)( void *dev ); #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_proc.h linux.21rc1-ac2/drivers/char/drm/drm_proc.h --- linux.21rc1/drivers/char/drm/drm_proc.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_proc.h 2003-04-25 16:27:10.000000000 +0100 @@ -147,10 +147,10 @@ *eof = 0; if (dev->unique) { - DRM_PROC_PRINT("%s 0x%x %s\n", - dev->name, dev->device, dev->unique); + DRM_PROC_PRINT("%s 0x%lx %s\n", + dev->name, (long)dev->device, dev->unique); } else { - DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device); + DRM_PROC_PRINT("%s 0x%lx\n", dev->name, (long)dev->device); } if (len > request + offset) return request; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_sarea.h linux.21rc1-ac2/drivers/char/drm/drm_sarea.h --- linux.21rc1/drivers/char/drm/drm_sarea.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_sarea.h 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,57 @@ +/* sarea.h -- SAREA definitions -*- linux-c -*- + * + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Michel Dänzer + */ + +#ifndef _DRM_SAREA_H_ +#define _DRM_SAREA_H_ + +#define SAREA_MAX_DRAWABLES 256 + +typedef struct _drm_sarea_drawable_t { + unsigned int stamp; + unsigned int flags; +} drm_sarea_drawable_t; + +typedef struct _dri_sarea_frame_t { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int fullscreen; +} drm_sarea_frame_t; + +typedef struct _drm_sarea_t { + /* first thing is always the drm locking structure */ + drm_hw_lock_t lock; + /* NOT_DONE: Use readers/writer lock for drawable_lock */ + drm_hw_lock_t drawable_lock; + drm_sarea_drawable_t drawableTable[SAREA_MAX_DRAWABLES]; + drm_sarea_frame_t frame; + drm_context_t dummy_context; +} drm_sarea_t; + +#endif /* _DRM_SAREA_H_ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_stub.h linux.21rc1-ac2/drivers/char/drm/drm_stub.h --- linux.21rc1/drivers/char/drm/drm_stub.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_stub.h 2003-04-25 16:27:10.000000000 +0100 @@ -48,7 +48,7 @@ static int DRM(stub_open)(struct inode *inode, struct file *filp) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); int err = -ENODEV; struct file_operations *old_fops; @@ -65,8 +65,8 @@ } static struct file_operations DRM(stub_fops) = { - owner: THIS_MODULE, - open: DRM(stub_open) + .owner = THIS_MODULE, + .open = DRM(stub_open) }; static int DRM(stub_getminor)(const char *name, struct file_operations *fops, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/drm_vm.h linux.21rc1-ac2/drivers/char/drm/drm_vm.h --- linux.21rc1/drivers/char/drm/drm_vm.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/drm_vm.h 2003-04-25 16:27:10.000000000 +0100 @@ -57,7 +57,7 @@ struct page *DRM(vm_nopage)(struct vm_area_struct *vma, unsigned long address, - int unused) + int write_access) { #if __REALLY_HAVE_AGP drm_file_t *priv = vma->vm_file->private_data; @@ -70,7 +70,7 @@ * Find the right map */ - if(!dev->agp->cant_use_aperture) goto vm_nopage_error; + if(!dev->agp || !dev->agp->cant_use_aperture) goto vm_nopage_error; list_for_each(list, &dev->maplist->head) { r_list = (drm_map_list_t *)list; @@ -141,9 +141,7 @@ return NOPAGE_OOM; get_page(page); -#if 0 /* XXX page_to_bus is not a portable interface available on all platforms. */ - DRM_DEBUG("0x%08lx => 0x%08llx\n", address, (u64)page_to_bus(page)); -#endif + DRM_DEBUG("shm_nopage 0x%lx\n", address); return page; } @@ -245,10 +243,7 @@ get_page(page); -#if 0 /* XXX page_to_bus is not a portable interface available on all platforms. */ - DRM_DEBUG("0x%08lx (page %lu) => 0x%08llx\n", address, page_nr, - (u64)page_to_bus(page)); -#endif + DRM_DEBUG("dma_nopage 0x%lx (page %lu)\n", address, page_nr); return page; } @@ -449,12 +444,12 @@ } offset = DRIVER_GET_REG_OFS(); #ifdef __sparc__ - if (io_remap_page_range(vma->vm_start, + if (io_remap_page_range(DRM_RPR_ARG(vma) vma->vm_start, VM_OFFSET(vma) + offset, vma->vm_end - vma->vm_start, vma->vm_page_prot, 0)) #else - if (remap_page_range(vma->vm_start, + if (remap_page_range(DRM_RPR_ARG(vma) vma->vm_start, VM_OFFSET(vma) + offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/gamma_dma.c linux.21rc1-ac2/drivers/char/drm/gamma_dma.c --- linux.21rc1/drivers/char/drm/gamma_dma.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/gamma_dma.c 2003-04-22 16:44:36.000000000 +0100 @@ -31,33 +31,32 @@ #include "gamma.h" #include "drmP.h" +#include "drm.h" +#include "gamma_drm.h" #include "gamma_drv.h" #include /* For task queue support */ #include - static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address, unsigned long length) { drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; - - GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address)); - while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4) - ; + (drm_gamma_private_t *)dev->dev_private; + mb(); + while ( GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); + GAMMA_WRITE(GAMMA_DMAADDRESS, address); + while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4) cpu_relax(); GAMMA_WRITE(GAMMA_DMACOUNT, length / 4); } void gamma_dma_quiescent_single(drm_device_t *dev) { drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; + (drm_gamma_private_t *)dev->dev_private; + while (GAMMA_READ(GAMMA_DMACOUNT)) cpu_relax(); - while (GAMMA_READ(GAMMA_DMACOUNT)) - ; - while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) - ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); GAMMA_WRITE(GAMMA_SYNC, 0); @@ -71,56 +70,50 @@ void gamma_dma_quiescent_dual(drm_device_t *dev) { drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; + (drm_gamma_private_t *)dev->dev_private; + while (GAMMA_READ(GAMMA_DMACOUNT)) cpu_relax(); - while (GAMMA_READ(GAMMA_DMACOUNT)) - ; - while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) - ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) cpu_relax(); GAMMA_WRITE(GAMMA_BROADCASTMASK, 3); - GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); GAMMA_WRITE(GAMMA_SYNC, 0); - /* Read from first MX */ + /* Read from first MX */ do { - while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) - ; + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) cpu_relax(); } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); - /* Read from second MX */ + /* Read from second MX */ do { - while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000)) - ; + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000)) cpu_relax(); } while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG); } void gamma_dma_ready(drm_device_t *dev) { drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; - - while (GAMMA_READ(GAMMA_DMACOUNT)) - ; + (drm_gamma_private_t *)dev->dev_private; + while (GAMMA_READ(GAMMA_DMACOUNT)) cpu_relax(); } static inline int gamma_dma_is_ready(drm_device_t *dev) { drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; - - return !GAMMA_READ(GAMMA_DMACOUNT); + (drm_gamma_private_t *)dev->dev_private; + return(!GAMMA_READ(GAMMA_DMACOUNT)); } void gamma_dma_service(int irq, void *device, struct pt_regs *regs) { - drm_device_t *dev = (drm_device_t *)device; - drm_device_dma_t *dma = dev->dma; + drm_device_t *dev = (drm_device_t *)device; + drm_device_dma_t *dma = dev->dma; drm_gamma_private_t *dev_priv = - (drm_gamma_private_t *)dev->dev_private; + (drm_gamma_private_t *)dev->dev_private; atomic_inc(&dev->counts[6]); /* _DRM_STAT_IRQ */ + + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) cpu_relax(); GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */ GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8); GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001); @@ -164,7 +157,9 @@ } buf = dma->next_buffer; - address = (unsigned long)buf->address; + /* WE NOW ARE ON LOGICAL PAGES!! - using page table setup in dma_init */ + /* So we pass the buffer index value into the physical page offset */ + address = buf->idx << 12; length = buf->used; DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", @@ -231,6 +226,9 @@ buf->time_dispatched = get_cycles(); #endif + /* WE NOW ARE ON LOGICAL PAGES!!! - overriding address */ + address = buf->idx << 12; + gamma_dma_dispatch(dev, address, length); gamma_free_buffer(dev, dma->this_buffer); dma->this_buffer = buf; @@ -523,11 +521,11 @@ } } if (retcode) { - DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", + DRM_ERROR("ctx%d w%d p%d c%ld i%d l%d %d/%d\n", d->context, last_buf->waiting, last_buf->pending, - DRM_WAITCOUNT(dev, d->context), + (long)DRM_WAITCOUNT(dev, d->context), last_buf->idx, last_buf->list, last_buf->pid, @@ -581,3 +579,267 @@ return retcode; } + +/* ============================================================= + * DMA initialization, cleanup + */ + +static int gamma_do_init_dma( drm_device_t *dev, drm_gamma_init_t *init ) +{ + drm_gamma_private_t *dev_priv; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + int i; + struct list_head *list; + unsigned long *pgt; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + dev_priv = DRM(alloc)( sizeof(drm_gamma_private_t), + DRM_MEM_DRIVER ); + if ( !dev_priv ) + return -ENOMEM; + + dev->dev_private = (void *)dev_priv; + + memset( dev_priv, 0, sizeof(drm_gamma_private_t) ); + + list_for_each(list, &dev->maplist->head) { + #warning list_entry() is needed here + drm_map_list_t *r_list = (drm_map_list_t *)list; + if( r_list->map && + r_list->map->type == _DRM_SHM && + r_list->map->flags & _DRM_CONTAINS_LOCK ) { + dev_priv->sarea = r_list->map; + break; + } + } + + DRM_FIND_MAP( dev_priv->mmio0, init->mmio0 ); + DRM_FIND_MAP( dev_priv->mmio1, init->mmio1 ); + DRM_FIND_MAP( dev_priv->mmio2, init->mmio2 ); + DRM_FIND_MAP( dev_priv->mmio3, init->mmio3 ); + + dev_priv->sarea_priv = (drm_gamma_sarea_t *) + ((u8 *)dev_priv->sarea->handle + + init->sarea_priv_offset); + + if (init->pcimode) { + buf = dma->buflist[GLINT_DRI_BUF_COUNT]; + pgt = buf->address; + + for (i = 0; i < GLINT_DRI_BUF_COUNT; i++) { + buf = dma->buflist[i]; + *pgt = virt_to_phys((void*)buf->address) | 0x07; + pgt++; + } + + buf = dma->buflist[GLINT_DRI_BUF_COUNT]; + } else { + DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset ); + + DRM_IOREMAP( dev_priv->buffers ); + + buf = dma->buflist[GLINT_DRI_BUF_COUNT]; + pgt = buf->address; + + for (i = 0; i < GLINT_DRI_BUF_COUNT; i++) { + buf = dma->buflist[i]; + *pgt = (unsigned long)buf->address + 0x07; + pgt++; + } + + buf = dma->buflist[GLINT_DRI_BUF_COUNT]; + + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 1) cpu_relax(); + GAMMA_WRITE( GAMMA_GDMACONTROL, 0xe) ; + } + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 2); cpu_relax(); + GAMMA_WRITE( GAMMA_PAGETABLEADDR, virt_to_phys((void*)buf->address) ); + GAMMA_WRITE( GAMMA_PAGETABLELENGTH, 2 ); + + return 0; +} + +int gamma_do_cleanup_dma( drm_device_t *dev ) +{ + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( dev->dev_private ) { + drm_gamma_private_t *dev_priv = dev->dev_private; + + DRM_IOREMAPFREE( dev_priv->buffers ); + + DRM(free)( dev->dev_private, sizeof(drm_gamma_private_t), + DRM_MEM_DRIVER ); + dev->dev_private = NULL; + } + + return 0; +} + +int gamma_dma_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_gamma_init_t init; + + if ( copy_from_user( &init, (drm_gamma_init_t *)arg, sizeof(init) ) ) + return -EFAULT; + + switch ( init.func ) { + case GAMMA_INIT_DMA: + return gamma_do_init_dma( dev, &init ); + case GAMMA_CLEANUP_DMA: + return gamma_do_cleanup_dma( dev ); + } + + return -EINVAL; +} + +static int gamma_do_copy_dma( drm_device_t *dev, drm_gamma_copy_t *copy ) +{ + drm_device_dma_t *dma = dev->dma; + unsigned int *screenbuf; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + /* We've DRM_RESTRICTED this DMA buffer */ + + screenbuf = dma->buflist[ GLINT_DRI_BUF_COUNT + 1 ]->address; + +#if 0 + *buffer++ = 0x180; /* Tag (FilterMode) */ + *buffer++ = 0x200; /* Allow FBColor through */ + *buffer++ = 0x53B; /* Tag */ + *buffer++ = copy->Pitch; + *buffer++ = 0x53A; /* Tag */ + *buffer++ = copy->SrcAddress; + *buffer++ = 0x539; /* Tag */ + *buffer++ = copy->WidthHeight; /* Initiates transfer */ + *buffer++ = 0x53C; /* Tag - DMAOutputAddress */ + *buffer++ = virt_to_phys((void*)screenbuf); + *buffer++ = 0x53D; /* Tag - DMAOutputCount */ + *buffer++ = copy->Count; /* Reads HostOutFifo BLOCKS until ..*/ + + /* Data now sitting in dma->buflist[ GLINT_DRI_BUF_COUNT + 1 ] */ + /* Now put it back to the screen */ + + *buffer++ = 0x180; /* Tag (FilterMode) */ + *buffer++ = 0x400; /* Allow Sync through */ + *buffer++ = 0x538; /* Tag - DMARectangleReadTarget */ + *buffer++ = 0x155; /* FBSourceData | count */ + *buffer++ = 0x537; /* Tag */ + *buffer++ = copy->Pitch; + *buffer++ = 0x536; /* Tag */ + *buffer++ = copy->DstAddress; + *buffer++ = 0x535; /* Tag */ + *buffer++ = copy->WidthHeight; /* Initiates transfer */ + *buffer++ = 0x530; /* Tag - DMAAddr */ + *buffer++ = virt_to_phys((void*)screenbuf); + *buffer++ = 0x531; + *buffer++ = copy->Count; /* initiates DMA transfer of color data */ +#endif + + /* need to dispatch it now */ + + return 0; +} + +int gamma_dma_copy( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_gamma_copy_t copy; + + if ( copy_from_user( ©, (drm_gamma_copy_t *)arg, sizeof(copy) ) ) + return -EFAULT; + + return gamma_do_copy_dma( dev, © ); +} + +/* ============================================================= + * Per Context SAREA Support + */ + +int gamma_getsareactx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_priv_map_t request; + drm_map_t *map; + + if (copy_from_user(&request, + (drm_ctx_priv_map_t *)arg, + sizeof(request))) + return -EFAULT; + + down(&dev->struct_sem); + if ((int)request.ctx_id >= dev->max_context) { + up(&dev->struct_sem); + return -EINVAL; + } + + map = dev->context_sareas[request.ctx_id]; + up(&dev->struct_sem); + + request.handle = map->handle; + if (copy_to_user((drm_ctx_priv_map_t *)arg, &request, sizeof(request))) + return -EFAULT; + return 0; +} + +int gamma_setsareactx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_priv_map_t request; + drm_map_t *map = NULL; + drm_map_list_t *r_list; + struct list_head *list; + + if (copy_from_user(&request, + (drm_ctx_priv_map_t *)arg, + sizeof(request))) + return -EFAULT; + + down(&dev->struct_sem); + r_list = NULL; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *)list; + if(r_list->map && + r_list->map->handle == request.handle) break; + } + if (list == &(dev->maplist->head)) { + up(&dev->struct_sem); + return -EINVAL; + } + map = r_list->map; + up(&dev->struct_sem); + + if (!map) return -EINVAL; + + down(&dev->struct_sem); + if ((int)request.ctx_id >= dev->max_context) { + up(&dev->struct_sem); + return -EINVAL; + } + dev->context_sareas[request.ctx_id] = map; + up(&dev->struct_sem); + return 0; +} + +/* drm_dma.h hooks +*/ +void DRM(driver_irq_preinstall)( drm_device_t *dev ) { +} + +void DRM(driver_irq_postinstall)( drm_device_t *dev ) { +} + +void DRM(driver_irq_uninstall)( drm_device_t *dev ) { +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/gamma_drm.h linux.21rc1-ac2/drivers/char/drm/gamma_drm.h --- linux.21rc1/drivers/char/drm/gamma_drm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/gamma_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,89 @@ +#ifndef _GAMMA_DRM_H_ +#define _GAMMA_DRM_H_ + +typedef struct _drm_gamma_tex_region { + unsigned char next, prev; /* indices to form a circular LRU */ + unsigned char in_use; /* owned by a client, or free? */ + int age; /* tracked by clients to update local LRU's */ +} drm_gamma_tex_region_t; + +typedef struct { + unsigned int GDeltaMode; + unsigned int GDepthMode; + unsigned int GGeometryMode; + unsigned int GTransformMode; +} drm_gamma_context_regs_t; + +typedef struct _drm_gamma_sarea { + drm_gamma_context_regs_t context_state; + + unsigned int dirty; + + + /* Maintain an LRU of contiguous regions of texture space. If + * you think you own a region of texture memory, and it has an + * age different to the one you set, then you are mistaken and + * it has been stolen by another client. If global texAge + * hasn't changed, there is no need to walk the list. + * + * These regions can be used as a proxy for the fine-grained + * texture information of other clients - by maintaining them + * in the same lru which is used to age their own textures, + * clients have an approximate lru for the whole of global + * texture space, and can make informed decisions as to which + * areas to kick out. There is no need to choose whether to + * kick out your own texture or someone else's - simply eject + * them all in LRU order. + */ + +#define GAMMA_NR_TEX_REGIONS 64 + drm_gamma_tex_region_t texList[GAMMA_NR_TEX_REGIONS+1]; + /* Last elt is sentinal */ + int texAge; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int last_quiescent; /* */ + int ctxOwner; /* last context to upload state */ + + int vertex_prim; +} drm_gamma_sarea_t; + +/* WARNING: If you change any of these defines, make sure to wear a bullet + * proof vest because these are part of the stable kernel<->userspace ABI + */ + +/* Gamma specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_GAMMA_INIT DRM_IOW( 0x40, drm_gamma_init_t) +#define DRM_IOCTL_GAMMA_COPY DRM_IOW( 0x41, drm_gamma_copy_t) + +typedef struct drm_gamma_copy { + unsigned int DMAOutputAddress; + unsigned int DMAOutputCount; + unsigned int DMAReadGLINTSource; + unsigned int DMARectangleWriteAddress; + unsigned int DMARectangleWriteLinePitch; + unsigned int DMARectangleWrite; + unsigned int DMARectangleReadAddress; + unsigned int DMARectangleReadLinePitch; + unsigned int DMARectangleRead; + unsigned int DMARectangleReadTarget; +} drm_gamma_copy_t; + +typedef struct drm_gamma_init { + enum { + GAMMA_INIT_DMA = 0x01, + GAMMA_CLEANUP_DMA = 0x02 + } func; + + int sarea_priv_offset; + int pcimode; + unsigned int mmio0; + unsigned int mmio1; + unsigned int mmio2; + unsigned int mmio3; + unsigned int buffers_offset; +} drm_gamma_init_t; + +#endif /* _GAMMA_DRM_H_ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/gamma_drv.c linux.21rc1-ac2/drivers/char/drm/gamma_drv.c --- linux.21rc1/drivers/char/drm/gamma_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/gamma_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -32,57 +32,18 @@ #include #include "gamma.h" #include "drmP.h" +#include "drm.h" +#include "gamma_drm.h" #include "gamma_drv.h" -#define DRIVER_AUTHOR "VA Linux Systems Inc." - -#define DRIVER_NAME "gamma" -#define DRIVER_DESC "3DLabs gamma" -#define DRIVER_DATE "20010216" - -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 0 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { gamma_dma, 1, 0 } - - -#define __HAVE_COUNTERS 5 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_DMA -#define __HAVE_COUNTER8 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER9 _DRM_STAT_SPECIAL -#define __HAVE_COUNTER10 _DRM_STAT_MISSED - - #include "drm_auth.h" +#include "drm_agpsupport.h" #include "drm_bufs.h" #include "drm_context.h" #include "drm_dma.h" #include "drm_drawable.h" #include "drm_drv.h" -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init gamma_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", gamma_options ); -#endif - - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/gamma_drv.h linux.21rc1-ac2/drivers/char/drm/gamma_drv.h --- linux.21rc1/drivers/char/drm/gamma_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/gamma_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -32,8 +32,9 @@ #ifndef _GAMMA_DRV_H_ #define _GAMMA_DRV_H_ - typedef struct drm_gamma_private { + drm_gamma_sarea_t *sarea_priv; + drm_map_t *sarea; drm_map_t *buffers; drm_map_t *mmio0; drm_map_t *mmio1; @@ -51,6 +52,11 @@ } \ } while (0) + /* gamma_dma.c */ +extern int gamma_dma_init( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int gamma_dma_copy( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); extern void gamma_dma_ready(drm_device_t *dev); extern void gamma_dma_quiescent_single(drm_device_t *dev); @@ -63,6 +69,7 @@ extern int gamma_find_devices(void); extern int gamma_found(void); +#define GLINT_DRI_BUF_COUNT 256 #define GAMMA_OFF(reg) \ ((reg < 0x1000) \ @@ -78,7 +85,6 @@ ((reg < 0x10000) ? dev_priv->mmio1->handle : \ ((reg < 0x11000) ? dev_priv->mmio2->handle : \ dev_priv->mmio3->handle)))) - #define GAMMA_ADDR(reg) (GAMMA_BASE(reg) + GAMMA_OFF(reg)) #define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg) #define GAMMA_READ(reg) GAMMA_DEREF(reg) @@ -91,9 +97,11 @@ #define GAMMA_FILTERMODE 0x8c00 #define GAMMA_GCOMMANDINTFLAGS 0x0c50 #define GAMMA_GCOMMANDMODE 0x0c40 +#define GAMMA_QUEUED_DMA_MODE 1<<1 #define GAMMA_GCOMMANDSTATUS 0x0c60 #define GAMMA_GDELAYTIMER 0x0c38 #define GAMMA_GDMACONTROL 0x0060 +#define GAMMA_USE_AGP 1<<1 #define GAMMA_GINTENABLE 0x0808 #define GAMMA_GINTFLAGS 0x0810 #define GAMMA_INFIFOSPACE 0x0018 @@ -101,5 +109,12 @@ #define GAMMA_OUTPUTFIFO 0x2000 #define GAMMA_SYNC 0x8c40 #define GAMMA_SYNC_TAG 0x0188 +#define GAMMA_PAGETABLEADDR 0x0C00 +#define GAMMA_PAGETABLELENGTH 0x0C08 + +#define GAMMA_PASSTHROUGH 0x1FE +#define GAMMA_DMAADDRTAG 0x530 +#define GAMMA_DMACOUNTTAG 0x531 +#define GAMMA_COMMANDINTTAG 0x532 #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/gamma.h linux.21rc1-ac2/drivers/char/drm/gamma.h --- linux.21rc1/drivers/char/drm/gamma.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/gamma.h 2003-04-22 16:44:36.000000000 +0100 @@ -38,9 +38,36 @@ */ #define __HAVE_MTRR 1 +#define DRIVER_AUTHOR "VA Linux Systems Inc." + +#define DRIVER_NAME "gamma" +#define DRIVER_DESC "3DLabs gamma" +#define DRIVER_DATE "20010624" + +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { gamma_dma, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_GAMMA_INIT)] = { gamma_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_GAMMA_COPY)] = { gamma_dma_copy, 1, 1 } + +#define IOCTL_TABLE_NAME DRM(ioctls) +#define IOCTL_FUNC_NAME DRM(ioctl) + +#define __HAVE_COUNTERS 5 +#define __HAVE_COUNTER6 _DRM_STAT_IRQ +#define __HAVE_COUNTER7 _DRM_STAT_DMA +#define __HAVE_COUNTER8 _DRM_STAT_PRIMARY +#define __HAVE_COUNTER9 _DRM_STAT_SPECIAL +#define __HAVE_COUNTER10 _DRM_STAT_MISSED + /* DMA customization: */ #define __HAVE_DMA 1 +#define __HAVE_AGP 1 +#define __MUST_HAVE_AGP 0 #define __HAVE_OLD_DMA 1 #define __HAVE_PCI_DMA 1 @@ -61,33 +88,61 @@ #define __HAVE_DMA_QUIESCENT 1 #define DRIVER_DMA_QUIESCENT() do { \ /* FIXME ! */ \ - gamma_dma_quiescent_dual(dev); \ + gamma_dma_quiescent_single(dev); \ return 0; \ } while (0) #define __HAVE_DMA_IRQ 1 #define __HAVE_DMA_IRQ_BH 1 + +#if 1 #define DRIVER_PREINSTALL() do { \ drm_gamma_private_t *dev_priv = \ (drm_gamma_private_t *)dev->dev_private;\ - GAMMA_WRITE( GAMMA_GCOMMANDMODE, 0x00000000 ); \ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + GAMMA_WRITE( GAMMA_GCOMMANDMODE, 0x00000004 ); \ GAMMA_WRITE( GAMMA_GDMACONTROL, 0x00000000 ); \ } while (0) - #define DRIVER_POSTINSTALL() do { \ drm_gamma_private_t *dev_priv = \ (drm_gamma_private_t *)dev->dev_private;\ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 3) cpu_relax(); \ GAMMA_WRITE( GAMMA_GINTENABLE, 0x00002001 ); \ GAMMA_WRITE( GAMMA_COMMANDINTENABLE, 0x00000008 ); \ GAMMA_WRITE( GAMMA_GDELAYTIMER, 0x00039090 ); \ } while (0) +#else +#define DRIVER_POSTINSTALL() do { \ + drm_gamma_private_t *dev_priv = \ + (drm_gamma_private_t *)dev->dev_private;\ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + GAMMA_WRITE( GAMMA_GINTENABLE, 0x00002000 ); \ + GAMMA_WRITE( GAMMA_COMMANDINTENABLE, 0x00000004 ); \ +} while (0) + +#define DRIVER_PREINSTALL() do { \ + drm_gamma_private_t *dev_priv = \ + (drm_gamma_private_t *)dev->dev_private;\ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + GAMMA_WRITE( GAMMA_GCOMMANDMODE, GAMMA_QUEUED_DMA_MODE );\ + GAMMA_WRITE( GAMMA_GDMACONTROL, 0x00000000 );\ +} while (0) +#endif #define DRIVER_UNINSTALL() do { \ drm_gamma_private_t *dev_priv = \ (drm_gamma_private_t *)dev->dev_private;\ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 2) cpu_relax(); \ + while(GAMMA_READ(GAMMA_INFIFOSPACE) < 3) cpu_relax(); \ GAMMA_WRITE( GAMMA_GDELAYTIMER, 0x00000000 ); \ GAMMA_WRITE( GAMMA_COMMANDINTENABLE, 0x00000000 ); \ GAMMA_WRITE( GAMMA_GINTENABLE, 0x00000000 ); \ } while (0) +#define DRIVER_AGP_BUFFERS_MAP( dev ) \ + ((drm_gamma_private_t *)((dev)->dev_private))->buffers + #endif /* __GAMMA_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i810_dma.c linux.21rc1-ac2/drivers/char/drm/i810_dma.c --- linux.21rc1/drivers/char/drm/i810_dma.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i810_dma.c 2003-04-22 16:44:36.000000000 +0100 @@ -26,21 +26,20 @@ * * Authors: Rickard E. (Rik) Faith * Jeff Hartmann - * Keith Whitwell + * Keith Whitwell * */ #include #include "i810.h" #include "drmP.h" +#include "drm.h" +#include "i810_drm.h" #include "i810_drv.h" #include /* For task queue support */ -#include +#include -/* in case we don't have a 2.3.99-pre6 kernel or later: */ -#ifndef VM_DONTCOPY -#define VM_DONTCOPY 0 -#endif +#define DO_MUNMAP(m, a, l) do_munmap(m, a, l, 1) #define I810_BUF_FREE 2 #define I810_BUF_CLIENT 1 @@ -51,29 +50,27 @@ #define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; -#define BEGIN_LP_RING(n) do { \ - if (I810_VERBOSE) \ - DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ - n, __FUNCTION__); \ - if (dev_priv->ring.space < n*4) \ - i810_wait_ring(dev, n*4); \ - dev_priv->ring.space -= n*4; \ - outring = dev_priv->ring.tail; \ - ringmask = dev_priv->ring.tail_mask; \ - virt = dev_priv->ring.virtual_start; \ +#define BEGIN_LP_RING(n) do { \ + if (0) DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", n, __FUNCTION__); \ + if (dev_priv->ring.space < n*4) \ + i810_wait_ring(dev, n*4); \ + dev_priv->ring.space -= n*4; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ } while (0) -#define ADVANCE_LP_RING() do { \ - if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ - dev_priv->ring.tail = outring; \ - I810_WRITE(LP_RING + RING_TAIL, outring); \ +#define ADVANCE_LP_RING() do { \ + if (0) DRM_DEBUG("ADVANCE_LP_RING\n"); \ + dev_priv->ring.tail = outring; \ + I810_WRITE(LP_RING + RING_TAIL, outring); \ } while(0) -#define OUT_RING(n) do { \ - if (I810_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ - *(volatile unsigned int *)(virt + outring) = n; \ - outring += 4; \ - outring &= ringmask; \ +#define OUT_RING(n) do { \ + if (0) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ } while (0) static inline void i810_print_status_page(drm_device_t *dev) @@ -135,14 +132,14 @@ } static struct file_operations i810_buffer_fops = { - open: DRM(open), - flush: DRM(flush), - release: DRM(release), - ioctl: DRM(ioctl), - mmap: i810_mmap_buffers, - read: DRM(read), - fasync: DRM(fasync), - poll: DRM(poll), + .open = DRM(open), + .flush = DRM(flush), + .release = DRM(release), + .ioctl = DRM(ioctl), + .mmap = i810_mmap_buffers, + .read = DRM(read), + .fasync = DRM(fasync), + .poll = DRM(poll), }; int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) @@ -165,7 +162,7 @@ buf_priv->currently_mapped = I810_BUF_MAPPED; unlock_kernel(); - if (remap_page_range(vma->vm_start, + if (remap_page_range(DRM_RPR_ARG(vma) vma->vm_start, VM_OFFSET(vma), vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; @@ -183,28 +180,31 @@ if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL; - if(VM_DONTCOPY != 0) { - down_write( ¤t->mm->mmap_sem ); - old_fops = filp->f_op; - filp->f_op = &i810_buffer_fops; - dev_priv->mmap_buffer = buf; - buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, - PROT_READ|PROT_WRITE, - MAP_SHARED, - buf->bus_address); - dev_priv->mmap_buffer = NULL; - filp->f_op = old_fops; - if ((unsigned long)buf_priv->virtual > -1024UL) { - /* Real error */ - DRM_DEBUG("mmap error\n"); - retcode = (signed int)buf_priv->virtual; - buf_priv->virtual = 0; - } - up_write( ¤t->mm->mmap_sem ); - } else { - buf_priv->virtual = buf_priv->kernel_virtual; - buf_priv->currently_mapped = I810_BUF_MAPPED; + + + + down_write( ¤t->mm->mmap_sem ); + + old_fops = filp->f_op; + filp->f_op = &i810_buffer_fops; + dev_priv->mmap_buffer = buf; + buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, + PROT_READ|PROT_WRITE, + MAP_SHARED, + buf->bus_address); + dev_priv->mmap_buffer = NULL; + filp->f_op = old_fops; + if ((unsigned long)buf_priv->virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed int)buf_priv->virtual; + buf_priv->virtual = 0; } + + + + up_write( ¤t->mm->mmap_sem ); + return retcode; } @@ -213,15 +213,21 @@ drm_i810_buf_priv_t *buf_priv = buf->dev_private; int retcode = 0; - if(VM_DONTCOPY != 0) { - if(buf_priv->currently_mapped != I810_BUF_MAPPED) - return -EINVAL; - down_write( ¤t->mm->mmap_sem ); - retcode = do_munmap(current->mm, - (unsigned long)buf_priv->virtual, - (size_t) buf->total); - up_write( ¤t->mm->mmap_sem ); - } + if(buf_priv->currently_mapped != I810_BUF_MAPPED) + return -EINVAL; + + + + down_write( ¤t->mm->mmap_sem ); + + retcode = DO_MUNMAP(current->mm, + (unsigned long)buf_priv->virtual, + (size_t) buf->total); + + + + up_write( ¤t->mm->mmap_sem ); + buf_priv->currently_mapped = I810_BUF_UNMAPPED; buf_priv->virtual = 0; @@ -273,8 +279,9 @@ dev_priv->ring.Size); } if(dev_priv->hw_status_page != 0UL) { - pci_free_consistent(dev->pdev, PAGE_SIZE, (void *)dev_priv->hw_status_page, - dev_priv->dma_status_page); + pci_free_consistent(dev->pdev, PAGE_SIZE, + (void *)dev_priv->hw_status_page, + dev_priv->dma_status_page); /* Need to rewrite hardware status page */ I810_WRITE(0x02080, 0x1ffff000); } @@ -301,8 +308,6 @@ end = jiffies + (HZ*3); while (ring->space < n) { - int i; - ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; @@ -311,13 +316,12 @@ end = jiffies + (HZ*3); iters++; - if((signed)(end - jiffies) <= 0) { + if(time_before(end, jiffies)) { DRM_ERROR("space: %d wanted %d\n", ring->space, n); DRM_ERROR("lockup\n"); goto out_wait_ring; } - - for (i = 0 ; i < 2000 ; i++) ; + udelay(1); } out_wait_ring: @@ -405,9 +409,6 @@ ((u8 *)dev_priv->sarea_map->handle + init->sarea_priv_offset); - atomic_set(&dev_priv->flush_done, 0); - init_waitqueue_head(&dev_priv->flush_queue); - dev_priv->ring.Start = init->ring_start; dev_priv->ring.End = init->ring_end; dev_priv->ring.Size = init->ring_size; @@ -440,8 +441,9 @@ dev_priv->zi1 = init->depth_offset | init->pitch_bits; /* Program Hardware Status Page */ - dev_priv->hw_status_page = (unsigned long)pci_alloc_consistent(dev->pdev, PAGE_SIZE, - &dev_priv->dma_status_page); + dev_priv->hw_status_page = + (unsigned long) pci_alloc_consistent(dev->pdev, PAGE_SIZE, + &dev_priv->dma_status_page); if(dev_priv->hw_status_page == 0UL) { dev->dev_private = (void *)dev_priv; i810_dma_cleanup(dev); @@ -451,7 +453,7 @@ memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); - I810_WRITE(0x02080, dev_priv->dma_status_page); + I810_WRITE(0x02080, dev_priv->dma_status_page); DRM_DEBUG("Enabled hardware status page\n"); /* Now we need to init our freelist */ @@ -532,16 +534,12 @@ /* Most efficient way to verify state for the i810 is as it is * emitted. Non-conformant state is silently dropped. - * - * Use 'volatile' & local var tmp to force the emitted values to be - * identical to the verified ones. */ static void i810EmitContextVerified( drm_device_t *dev, - volatile unsigned int *code ) + unsigned int *code ) { drm_i810_private_t *dev_priv = dev->dev_private; int i, j = 0; - unsigned int tmp; RING_LOCALS; BEGIN_LP_RING( I810_CTX_SETUP_SIZE ); @@ -553,14 +551,13 @@ OUT_RING( code[I810_CTXREG_ST1] ); for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) { - tmp = code[i]; - - if ((tmp & (7<<29)) == (3<<29) && - (tmp & (0x1f<<24)) < (0x1d<<24)) + if ((code[i] & (7<<29)) == (3<<29) && + (code[i] & (0x1f<<24)) < (0x1d<<24)) { - OUT_RING( tmp ); + OUT_RING( code[i] ); j++; } + else printk("constext state dropped!!!\n"); } if (j & 1) @@ -574,7 +571,6 @@ { drm_i810_private_t *dev_priv = dev->dev_private; int i, j = 0; - unsigned int tmp; RING_LOCALS; BEGIN_LP_RING( I810_TEX_SETUP_SIZE ); @@ -585,14 +581,14 @@ OUT_RING( code[I810_TEXREG_MI3] ); for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) { - tmp = code[i]; - if ((tmp & (7<<29)) == (3<<29) && - (tmp & (0x1f<<24)) < (0x1d<<24)) + if ((code[i] & (7<<29)) == (3<<29) && + (code[i] & (0x1f<<24)) < (0x1d<<24)) { - OUT_RING( tmp ); + OUT_RING( code[i] ); j++; } + else printk("texture state dropped!!!\n"); } if (j & 1) @@ -617,9 +613,9 @@ if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { OUT_RING( CMD_OP_DESTBUFFER_INFO ); OUT_RING( tmp ); - } else - DRM_DEBUG("bad di1 %x (allow %x or %x)\n", - tmp, dev_priv->front_di1, dev_priv->back_di1); + } + else + printk("buffer state dropped\n"); /* invarient: */ @@ -704,7 +700,6 @@ continue; if ( flags & I810_FRONT ) { - DRM_DEBUG("clear front\n"); BEGIN_LP_RING( 6 ); OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_COLOR_BLT | 0x3 ); @@ -717,7 +712,6 @@ } if ( flags & I810_BACK ) { - DRM_DEBUG("clear back\n"); BEGIN_LP_RING( 6 ); OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_COLOR_BLT | 0x3 ); @@ -730,7 +724,6 @@ } if ( flags & I810_DEPTH ) { - DRM_DEBUG("clear depth\n"); BEGIN_LP_RING( 6 ); OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_COLOR_BLT | 0x3 ); @@ -756,8 +749,6 @@ int i; RING_LOCALS; - DRM_DEBUG("swapbuffers\n"); - i810_kernel_lost_context(dev); if (nbox > I810_NR_SAREA_CLIPRECTS) @@ -776,10 +767,6 @@ pbox->y2 > dev_priv->h) continue; - DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", - pbox[i].x1, pbox[i].y1, - pbox[i].x2, pbox[i].y2); - BEGIN_LP_RING( 6 ); OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 ); OUT_RING( pitch | (0xCC << 16)); @@ -804,7 +791,7 @@ int nbox = sarea_priv->nbox; unsigned long address = (unsigned long)buf->bus_address; unsigned long start = address - dev->agp->base; - int i = 0, u; + int i = 0; RING_LOCALS; i810_kernel_lost_context(dev); @@ -812,33 +799,16 @@ if (nbox > I810_NR_SAREA_CLIPRECTS) nbox = I810_NR_SAREA_CLIPRECTS; - if (discard) { - u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, - I810_BUF_HARDWARE); - if(u != I810_BUF_CLIENT) { - DRM_DEBUG("xxxx 2\n"); - } - } - if (used > 4*1024) used = 0; if (sarea_priv->dirty) i810EmitState( dev ); - DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", - address, used, nbox); - - dev_priv->counter++; - DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); - DRM_DEBUG( "i810_dma_dispatch\n"); - DRM_DEBUG( "start : %lx\n", start); - DRM_DEBUG( "used : %d\n", used); - DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4); - if (buf_priv->currently_mapped == I810_BUF_MAPPED) { - *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | - sarea_priv->vertex_prim | + unsigned int prim = (sarea_priv->vertex_prim & PR_MASK); + + *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | prim | ((used/4)-2)); if (used & 4) { @@ -871,154 +841,62 @@ } while (++i < nbox); } - BEGIN_LP_RING(10); - OUT_RING( CMD_STORE_DWORD_IDX ); - OUT_RING( 20 ); - OUT_RING( dev_priv->counter ); - OUT_RING( 0 ); - if (discard) { + dev_priv->counter++; + + (void) cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_HARDWARE); + + BEGIN_LP_RING(8); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 20 ); + OUT_RING( dev_priv->counter ); OUT_RING( CMD_STORE_DWORD_IDX ); OUT_RING( buf_priv->my_use_idx ); OUT_RING( I810_BUF_FREE ); + OUT_RING( CMD_REPORT_HEAD ); OUT_RING( 0 ); + ADVANCE_LP_RING(); } - - OUT_RING( CMD_REPORT_HEAD ); - OUT_RING( 0 ); - ADVANCE_LP_RING(); -} - - -/* Interrupts are only for flushing */ -void i810_dma_service(int irq, void *device, struct pt_regs *regs) -{ - drm_device_t *dev = (drm_device_t *)device; - drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; - u16 temp; - - atomic_inc(&dev->counts[_DRM_STAT_IRQ]); - temp = I810_READ16(I810REG_INT_IDENTITY_R); - temp = temp & ~(0x6000); - if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, - temp); /* Clear all interrupts */ - else - return; - - queue_task(&dev->tq, &tq_immediate); - mark_bh(IMMEDIATE_BH); } -void i810_dma_immediate_bh(void *device) -{ - drm_device_t *dev = (drm_device_t *) device; - drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; - - atomic_set(&dev_priv->flush_done, 1); - wake_up_interruptible(&dev_priv->flush_queue); -} - -static inline void i810_dma_emit_flush(drm_device_t *dev) -{ - drm_i810_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - i810_kernel_lost_context(dev); - - BEGIN_LP_RING(2); - OUT_RING( CMD_REPORT_HEAD ); - OUT_RING( GFX_OP_USER_INTERRUPT ); - ADVANCE_LP_RING(); - -/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ -/* atomic_set(&dev_priv->flush_done, 1); */ -/* wake_up_interruptible(&dev_priv->flush_queue); */ -} - -static inline void i810_dma_quiescent_emit(drm_device_t *dev) +void i810_dma_quiescent(drm_device_t *dev) { drm_i810_private_t *dev_priv = dev->dev_private; RING_LOCALS; +/* printk("%s\n", __FUNCTION__); */ + i810_kernel_lost_context(dev); BEGIN_LP_RING(4); OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); OUT_RING( CMD_REPORT_HEAD ); OUT_RING( 0 ); - OUT_RING( GFX_OP_USER_INTERRUPT ); + OUT_RING( 0 ); ADVANCE_LP_RING(); -/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ -/* atomic_set(&dev_priv->flush_done, 1); */ -/* wake_up_interruptible(&dev_priv->flush_queue); */ -} - -void i810_dma_quiescent(drm_device_t *dev) -{ - DECLARE_WAITQUEUE(entry, current); - drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; - unsigned long end; - - if(dev_priv == NULL) { - return; - } - atomic_set(&dev_priv->flush_done, 0); - add_wait_queue(&dev_priv->flush_queue, &entry); - end = jiffies + (HZ*3); - - for (;;) { - current->state = TASK_INTERRUPTIBLE; - i810_dma_quiescent_emit(dev); - if (atomic_read(&dev_priv->flush_done) == 1) break; - if((signed)(end - jiffies) <= 0) { - DRM_ERROR("lockup\n"); - break; - } - schedule_timeout(HZ*3); - if (signal_pending(current)) { - break; - } - } - - current->state = TASK_RUNNING; - remove_wait_queue(&dev_priv->flush_queue, &entry); - - return; + i810_wait_ring( dev, dev_priv->ring.Size - 8 ); } static int i810_flush_queue(drm_device_t *dev) { - DECLARE_WAITQUEUE(entry, current); - drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + drm_i810_private_t *dev_priv = dev->dev_private; drm_device_dma_t *dma = dev->dma; - unsigned long end; int i, ret = 0; + RING_LOCALS; + +/* printk("%s\n", __FUNCTION__); */ - if(dev_priv == NULL) { - return 0; - } - atomic_set(&dev_priv->flush_done, 0); - add_wait_queue(&dev_priv->flush_queue, &entry); - end = jiffies + (HZ*3); - for (;;) { - current->state = TASK_INTERRUPTIBLE; - i810_dma_emit_flush(dev); - if (atomic_read(&dev_priv->flush_done) == 1) break; - if((signed)(end - jiffies) <= 0) { - DRM_ERROR("lockup\n"); - break; - } - schedule_timeout(HZ*3); - if (signal_pending(current)) { - ret = -EINTR; /* Can't restart */ - break; - } - } + i810_kernel_lost_context(dev); - current->state = TASK_RUNNING; - remove_wait_queue(&dev_priv->flush_queue, &entry); + BEGIN_LP_RING(2); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + i810_wait_ring( dev, dev_priv->ring.Size - 8 ); for (i = 0; i < dma->buf_count; i++) { drm_buf_t *buf = dma->buflist[ i ]; @@ -1030,7 +908,7 @@ if (used == I810_BUF_HARDWARE) DRM_DEBUG("reclaimed from HARDWARE\n"); if (used == I810_BUF_CLIENT) - DRM_DEBUG("still on client HARDWARE\n"); + DRM_DEBUG("still on client\n"); } return ret; @@ -1070,7 +948,6 @@ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - DRM_DEBUG("i810_flush_ioctl\n"); if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { DRM_ERROR("i810_flush_ioctl called without lock held\n"); return -EINVAL; @@ -1101,9 +978,6 @@ return -EINVAL; } - DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n", - vertex.idx, vertex.used, vertex.discard); - if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL; i810_dma_dispatch_vertex( dev, @@ -1152,8 +1026,6 @@ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - DRM_DEBUG("i810_swap_bufs\n"); - if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { DRM_ERROR("i810_swap_buf called without lock held\n"); return -EINVAL; @@ -1189,7 +1061,6 @@ drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) dev_priv->sarea_priv; - DRM_DEBUG("getbuf\n"); if (copy_from_user(&d, (drm_i810_dma_t *)arg, sizeof(d))) return -EFAULT; @@ -1202,9 +1073,6 @@ retcode = i810_dma_get_buffer(dev, &d, filp); - DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n", - current->pid, retcode, d.granted); - if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d))) return -EFAULT; sarea_priv->last_dispatch = (int) hw_status[5]; @@ -1212,47 +1080,19 @@ return retcode; } -int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) +int i810_copybuf(struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg) { - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - drm_i810_copy_t d; - drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; - u32 *hw_status = (u32 *)dev_priv->hw_status_page; - drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) - dev_priv->sarea_priv; - drm_buf_t *buf; - drm_i810_buf_priv_t *buf_priv; - drm_device_dma_t *dma = dev->dma; - - if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { - DRM_ERROR("i810_dma called without lock held\n"); - return -EINVAL; - } - - if (copy_from_user(&d, (drm_i810_copy_t *)arg, sizeof(d))) - return -EFAULT; - - if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL; - buf = dma->buflist[ d.idx ]; - buf_priv = buf->dev_private; - if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM; - - if(d.used < 0 || d.used > buf->total) return -EINVAL; - - if (copy_from_user(buf_priv->virtual, d.address, d.used)) - return -EFAULT; - - sarea_priv->last_dispatch = (int) hw_status[5]; - + /* Never copy - 2.4.x doesn't need it */ return 0; } int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { - if(VM_DONTCOPY == 0) return 1; + /* Never copy - 2.4.x doesn't need it */ return 0; } @@ -1371,7 +1211,8 @@ data.offset = dev_priv->overlay_offset; data.physical = dev_priv->overlay_physical; - copy_to_user((drm_i810_overlay_t *)arg,&data,sizeof(data)); + if (copy_to_user((drm_i810_overlay_t *)arg,&data,sizeof(data))) + return -EFAULT; return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i810_drm.h linux.21rc1-ac2/drivers/char/drm/i810_drm.h --- linux.21rc1/drivers/char/drm/i810_drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i810_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -88,6 +88,8 @@ #define I810_TEXREG_MCS 7 /* GFX_OP_MAP_COORD_SETS ??? */ #define I810_TEX_SETUP_SIZE 8 +/* Flags for clear ioctl + */ #define I810_FRONT 0x1 #define I810_BACK 0x2 #define I810_DEPTH 0x4 @@ -166,14 +168,34 @@ } drm_i810_sarea_t; +/* WARNING: If you change any of these defines, make sure to wear a bullet + * proof vest since these are part of the stable kernel<->userspace ABI + */ + +/* i810 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t) +#define DRM_IOCTL_I810_VERTEX DRM_IOW( 0x41, drm_i810_vertex_t) +#define DRM_IOCTL_I810_CLEAR DRM_IOW( 0x42, drm_i810_clear_t) +#define DRM_IOCTL_I810_FLUSH DRM_IO( 0x43) +#define DRM_IOCTL_I810_GETAGE DRM_IO( 0x44) +#define DRM_IOCTL_I810_GETBUF DRM_IOWR(0x45, drm_i810_dma_t) +#define DRM_IOCTL_I810_SWAP DRM_IO( 0x46) +#define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t) +#define DRM_IOCTL_I810_DOCOPY DRM_IO( 0x48) +#define DRM_IOCTL_I810_OV0INFO DRM_IOR( 0x49, drm_i810_overlay_t) +#define DRM_IOCTL_I810_FSTATUS DRM_IO ( 0x4a) +#define DRM_IOCTL_I810_OV0FLIP DRM_IO ( 0x4b) +#define DRM_IOCTL_I810_MC DRM_IOW( 0x4c, drm_i810_mc_t) +#define DRM_IOCTL_I810_RSTATUS DRM_IO ( 0x4d ) + typedef struct _drm_i810_clear { int clear_color; int clear_depth; int flags; } drm_i810_clear_t; - - /* These may be placeholders if we have more cliprects than * I810_NR_SAREA_CLIPRECTS. In that case, the client sets discard to * false, indicating that the buffer will be dispatched again with a @@ -191,6 +213,17 @@ void *address; /* Address to copy from */ } drm_i810_copy_t; +#define PR_TRIANGLES (0x0<<18) +#define PR_TRISTRIP_0 (0x1<<18) +#define PR_TRISTRIP_1 (0x2<<18) +#define PR_TRIFAN (0x3<<18) +#define PR_POLYGON (0x4<<18) +#define PR_LINES (0x5<<18) +#define PR_LINESTRIP (0x6<<18) +#define PR_RECTS (0x7<<18) +#define PR_MASK (0x7<<18) + + typedef struct drm_i810_dma { void *virtual; int request_idx; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i810_drv.c linux.21rc1-ac2/drivers/char/drm/i810_drv.c --- linux.21rc1/drivers/char/drm/i810_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i810_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -33,42 +33,10 @@ #include #include "i810.h" #include "drmP.h" +#include "drm.h" +#include "i810_drm.h" #include "i810_drv.h" -#define DRIVER_AUTHOR "VA Linux Systems Inc." - -#define DRIVER_NAME "i810" -#define DRIVER_DESC "Intel i810" -#define DRIVER_DATE "20010920" - -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 0 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)] = { i810_clear_bufs, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)] = { i810_flush_ioctl, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0INFO)] = { i810_ov0_info, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_FSTATUS)] = { i810_fstatus, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0FLIP)] = { i810_ov0_flip, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_MC)] = { i810_dma_mc, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I810_RSTATUS)] = { i810_rstatus, 1, 0 } - - -#define __HAVE_COUNTERS 4 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY -#define __HAVE_COUNTER9 _DRM_STAT_DMA - - #include "drm_agpsupport.h" #include "drm_auth.h" #include "drm_bufs.h" @@ -77,25 +45,6 @@ #include "drm_drawable.h" #include "drm_drv.h" -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init i810_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", i810_options ); -#endif - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i810_drv.h linux.21rc1-ac2/drivers/char/drm/i810_drv.h --- linux.21rc1/drivers/char/drm/i810_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i810_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -63,10 +63,9 @@ unsigned long hw_status_page; unsigned long counter; - dma_addr_t dma_status_page; - atomic_t flush_done; - wait_queue_head_t flush_queue; /* Processes waiting until flush */ + dma_addr_t dma_status_page; + drm_buf_t *mmap_buffer; @@ -78,6 +77,7 @@ int overlay_physical; int w, h; int pitch; + } drm_i810_private_t; /* i810_dma.c */ @@ -92,8 +92,13 @@ extern int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma); + +/* Obsolete: + */ extern int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +/* Obsolete: + */ extern int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); @@ -111,9 +116,6 @@ extern void i810_dma_quiescent(drm_device_t *dev); -#define I810_VERBOSE 0 - - int i810_dma_vertex(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); @@ -196,6 +198,7 @@ #define CMD_OP_Z_BUFFER_INFO ((0x0<<29)|(0x16<<23)) #define CMD_OP_DESTBUFFER_INFO ((0x0<<29)|(0x15<<23)) +#define CMD_OP_FRONTBUFFER_INFO ((0x0<<29)|(0x14<<23)) #define BR00_BITBLT_CLIENT 0x40000000 #define BR00_OP_COLOR_BLT 0x10000000 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i810.h linux.21rc1-ac2/drivers/char/drm/i810.h --- linux.21rc1/drivers/char/drm/i810.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i810.h 2003-04-22 16:44:36.000000000 +0100 @@ -41,6 +41,47 @@ #define __HAVE_MTRR 1 #define __HAVE_CTX_BITMAP 1 +#define DRIVER_AUTHOR "VA Linux Systems Inc." + +#define DRIVER_NAME "i810" +#define DRIVER_DESC "Intel i810" +#define DRIVER_DATE "20020211" + +/* Interface history + * + * 1.1 - XFree86 4.1 + * 1.2 - XvMC interfaces + * - XFree86 4.2 + * 1.2.1 - Disable copying code (leave stub ioctls for backwards compatibility) + * - Remove requirement for interrupt (leave stubs again) + */ +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 1 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)] = { i810_clear_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)] = { i810_flush_ioctl, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0INFO)] = { i810_ov0_info, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_FSTATUS)] = { i810_fstatus, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_OV0FLIP)] = { i810_ov0_flip, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_MC)] = { i810_dma_mc, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I810_RSTATUS)] = { i810_rstatus, 1, 0 } + + +#define __HAVE_COUNTERS 4 +#define __HAVE_COUNTER6 _DRM_STAT_IRQ +#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY +#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY +#define __HAVE_COUNTER9 _DRM_STAT_DMA + /* Driver customization: */ #define __HAVE_RELEASE 1 @@ -60,50 +101,10 @@ i810_dma_quiescent( dev ); \ } while (0) -#define __HAVE_DMA_IRQ 1 -#define __HAVE_DMA_IRQ_BH 1 -#define __HAVE_SHARED_IRQ 1 -#define DRIVER_PREINSTALL() do { \ - drm_i810_private_t *dev_priv = \ - (drm_i810_private_t *)dev->dev_private; \ - u16 tmp; \ - tmp = I810_READ16( I810REG_HWSTAM ); \ - tmp = tmp & 0x6000; \ - I810_WRITE16( I810REG_HWSTAM, tmp ); \ - \ - tmp = I810_READ16( I810REG_INT_MASK_R ); \ - tmp = tmp & 0x6000; /* Unmask interrupts */ \ - I810_WRITE16( I810REG_INT_MASK_R, tmp ); \ - tmp = I810_READ16( I810REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; /* Disable all interrupts */ \ - I810_WRITE16( I810REG_INT_ENABLE_R, tmp ); \ -} while (0) - -#define DRIVER_POSTINSTALL() do { \ - drm_i810_private_t *dev_priv = \ - (drm_i810_private_t *)dev->dev_private; \ - u16 tmp; \ - tmp = I810_READ16( I810REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; \ - tmp = tmp | 0x0003; /* Enable bp & user interrupts */ \ - I810_WRITE16( I810REG_INT_ENABLE_R, tmp ); \ -} while (0) - -#define DRIVER_UNINSTALL() do { \ - drm_i810_private_t *dev_priv = \ - (drm_i810_private_t *)dev->dev_private; \ - u16 tmp; \ - if ( dev_priv ) { \ - tmp = I810_READ16( I810REG_INT_IDENTITY_R ); \ - tmp = tmp & ~(0x6000); /* Clear all interrupts */ \ - if ( tmp != 0 ) \ - I810_WRITE16( I810REG_INT_IDENTITY_R, tmp ); \ - \ - tmp = I810_READ16( I810REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; /* Disable all interrupts */ \ - I810_WRITE16( I810REG_INT_ENABLE_R, tmp ); \ - } \ -} while (0) +/* Don't need an irq any more. The template code will make sure that + * a noop stub is generated for compatibility. + */ +#define __HAVE_DMA_IRQ 0 /* Buffer customization: */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830_dma.c linux.21rc1-ac2/drivers/char/drm/i830_dma.c --- linux.21rc1/drivers/char/drm/i830_dma.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830_dma.c 2003-04-22 16:44:36.000000000 +0100 @@ -26,20 +26,22 @@ * * Authors: Rickard E. (Rik) Faith * Jeff Hartmann - * Keith Whitwell - * Abraham vd Merwe + * Keith Whitwell + * Abraham vd Merwe * */ + #include "i830.h" #include "drmP.h" +#include "drm.h" +#include "i830_drm.h" #include "i830_drv.h" #include /* For task queue support */ +#include /* For FASTCALL on unlock_page() */ #include -/* in case we don't have a 2.3.99-pre6 kernel or later: */ -#ifndef VM_DONTCOPY -#define VM_DONTCOPY 0 -#endif + +#define DO_MUNMAP(m, a, l) do_munmap(m, a, l, 1) #define I830_BUF_FREE 2 #define I830_BUF_CLIENT 1 @@ -48,54 +50,24 @@ #define I830_BUF_UNMAPPED 0 #define I830_BUF_MAPPED 1 -#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; -#define DO_IDLE_WORKAROUND() \ -do { \ - int _head; \ - int _tail; \ - do { \ - _head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; \ - _tail = I830_READ(LP_RING + RING_TAIL) & TAIL_ADDR; \ - udelay(1); \ - } while(_head != _tail); \ -} while(0) - -#define I830_SYNC_WORKAROUND 0 - -#define BEGIN_LP_RING(n) do { \ - if (I830_VERBOSE) \ - DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ - n, __FUNCTION__); \ - if (I830_SYNC_WORKAROUND) \ - DO_IDLE_WORKAROUND(); \ - if (dev_priv->ring.space < n*4) \ - i830_wait_ring(dev, n*4); \ - dev_priv->ring.space -= n*4; \ - outring = dev_priv->ring.tail; \ - ringmask = dev_priv->ring.tail_mask; \ - virt = dev_priv->ring.virtual_start; \ -} while (0) - -#define ADVANCE_LP_RING() do { \ - if (I830_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ - dev_priv->ring.tail = outring; \ - I830_WRITE(LP_RING + RING_TAIL, outring); \ -} while(0) - -#define OUT_RING(n) do { \ - if (I830_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ - *(volatile unsigned int *)(virt + outring) = n; \ - outring += 4; \ - outring &= ringmask; \ -} while (0); + + + + + + + + + + static inline void i830_print_status_page(drm_device_t *dev) { drm_device_dma_t *dma = dev->dma; drm_i830_private_t *dev_priv = dev->dev_private; - u8 *temp = dev_priv->hw_status_page; + u32 *temp = (u32 *)dev_priv->hw_status_page; int i; DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]); @@ -149,14 +121,14 @@ } static struct file_operations i830_buffer_fops = { - open: DRM(open), - flush: DRM(flush), - release: DRM(release), - ioctl: DRM(ioctl), - mmap: i830_mmap_buffers, - read: DRM(read), - fasync: DRM(fasync), - poll: DRM(poll), + .open = DRM(open), + .flush = DRM(flush), + .release = DRM(release), + .ioctl = DRM(ioctl), + .mmap = i830_mmap_buffers, + .read = DRM(read), + .fasync = DRM(fasync), + .poll = DRM(poll), }; int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) @@ -179,7 +151,7 @@ buf_priv->currently_mapped = I830_BUF_MAPPED; unlock_kernel(); - if (remap_page_range(vma->vm_start, + if (remap_page_range(DRM_RPR_ARG(vma) vma->vm_start, VM_OFFSET(vma), vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; @@ -197,28 +169,24 @@ if(buf_priv->currently_mapped == I830_BUF_MAPPED) return -EINVAL; - if(VM_DONTCOPY != 0) { - down_write( ¤t->mm->mmap_sem ); - old_fops = filp->f_op; - filp->f_op = &i830_buffer_fops; - dev_priv->mmap_buffer = buf; - buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, - PROT_READ|PROT_WRITE, - MAP_SHARED, - buf->bus_address); - dev_priv->mmap_buffer = NULL; - filp->f_op = old_fops; - if ((unsigned long)buf_priv->virtual > -1024UL) { - /* Real error */ - DRM_DEBUG("mmap error\n"); - retcode = (signed int)buf_priv->virtual; - buf_priv->virtual = 0; - } - up_write( ¤t->mm->mmap_sem ); - } else { - buf_priv->virtual = buf_priv->kernel_virtual; - buf_priv->currently_mapped = I830_BUF_MAPPED; + down_write( ¤t->mm->mmap_sem ); + old_fops = filp->f_op; + filp->f_op = &i830_buffer_fops; + dev_priv->mmap_buffer = buf; + buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, + PROT_READ|PROT_WRITE, + MAP_SHARED, + buf->bus_address); + dev_priv->mmap_buffer = NULL; + filp->f_op = old_fops; + if ((unsigned long)buf_priv->virtual > -1024UL) { + /* Real error */ + DRM_ERROR("mmap error\n"); + retcode = (signed int)buf_priv->virtual; + buf_priv->virtual = 0; } + up_write( ¤t->mm->mmap_sem ); + return retcode; } @@ -227,15 +195,15 @@ drm_i830_buf_priv_t *buf_priv = buf->dev_private; int retcode = 0; - if(VM_DONTCOPY != 0) { - if(buf_priv->currently_mapped != I830_BUF_MAPPED) - return -EINVAL; - down_write( ¤t->mm->mmap_sem ); - retcode = do_munmap(current->mm, - (unsigned long)buf_priv->virtual, - (size_t) buf->total); - up_write( ¤t->mm->mmap_sem ); - } + if(buf_priv->currently_mapped != I830_BUF_MAPPED) + return -EINVAL; + + down_write(¤t->mm->mmap_sem); + retcode = DO_MUNMAP(current->mm, + (unsigned long)buf_priv->virtual, + (size_t) buf->total); + up_write(¤t->mm->mmap_sem); + buf_priv->currently_mapped = I830_BUF_UNMAPPED; buf_priv->virtual = 0; @@ -260,7 +228,7 @@ retcode = i830_map_buffer(buf, filp); if(retcode) { i830_freelist_put(dev, buf); - DRM_DEBUG("mapbuf failed, retcode %d\n", retcode); + DRM_ERROR("mapbuf failed, retcode %d\n", retcode); return retcode; } buf->pid = priv->pid; @@ -286,12 +254,22 @@ DRM(ioremapfree)((void *) dev_priv->ring.virtual_start, dev_priv->ring.Size); } - if(dev_priv->hw_status_page != NULL) { - pci_free_consistent(dev->pdev, PAGE_SIZE, - dev_priv->hw_status_page, dev_priv->dma_status_page); + if(dev_priv->hw_status_page != 0UL) { + pci_free_consistent(dev->pdev, PAGE_SIZE, + (void *)dev_priv->hw_status_page, + dev_priv->dma_status_page); /* Need to rewrite hardware status page */ I830_WRITE(0x02080, 0x1ffff000); } + + /* Disable interrupts here because after dev_private + * is freed, it's too late. + */ + if (dev->irq) { + I830_WRITE16( I830REG_INT_MASK_R, 0xffff ); + I830_WRITE16( I830REG_INT_ENABLE_R, 0x0 ); + } + DRM(free)(dev->dev_private, sizeof(drm_i830_private_t), DRM_MEM_DRIVER); dev->dev_private = NULL; @@ -305,7 +283,7 @@ return 0; } -static int i830_wait_ring(drm_device_t *dev, int n) +int i830_wait_ring(drm_device_t *dev, int n, const char *caller) { drm_i830_private_t *dev_priv = dev->dev_private; drm_i830_ring_buffer_t *ring = &(dev_priv->ring); @@ -314,7 +292,7 @@ unsigned int last_head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; end = jiffies + (HZ*3); - while (ring->space < n) { + while (ring->space < n) { ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; @@ -325,13 +303,13 @@ } iters++; - if(time_before(end,jiffies)) { + if(time_before(end, jiffies)) { DRM_ERROR("space: %d wanted %d\n", ring->space, n); DRM_ERROR("lockup\n"); goto out_wait_ring; } - - udelay(1); + udelay(1); + dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT; } out_wait_ring: @@ -344,9 +322,12 @@ drm_i830_ring_buffer_t *ring = &(dev_priv->ring); ring->head = I830_READ(LP_RING + RING_HEAD) & HEAD_ADDR; - ring->tail = I830_READ(LP_RING + RING_TAIL); + ring->tail = I830_READ(LP_RING + RING_TAIL) & TAIL_ADDR; ring->space = ring->head - (ring->tail+8); if (ring->space < 0) ring->space += ring->Size; + + if (ring->head == ring->tail) + dev_priv->sarea_priv->perf_boxes |= I830_BOX_RING_EMPTY; } static int i830_freelist_init(drm_device_t *dev, drm_i830_private_t *dev_priv) @@ -420,9 +401,6 @@ ((u8 *)dev_priv->sarea_map->handle + init->sarea_priv_offset); - atomic_set(&dev_priv->flush_done, 0); - init_waitqueue_head(&dev_priv->flush_queue); - dev_priv->ring.Start = init->ring_start; dev_priv->ring.End = init->ring_end; dev_priv->ring.Size = init->ring_size; @@ -446,11 +424,17 @@ dev_priv->pitch = init->pitch; dev_priv->back_offset = init->back_offset; dev_priv->depth_offset = init->depth_offset; + dev_priv->front_offset = init->front_offset; dev_priv->front_di1 = init->front_offset | init->pitch_bits; dev_priv->back_di1 = init->back_offset | init->pitch_bits; dev_priv->zi1 = init->depth_offset | init->pitch_bits; + DRM_DEBUG("front_di1 %x\n", dev_priv->front_di1); + DRM_DEBUG("back_offset %x\n", dev_priv->back_offset); + DRM_DEBUG("back_di1 %x\n", dev_priv->back_di1); + DRM_DEBUG("pitch_bits %x\n", init->pitch_bits); + dev_priv->cpp = init->cpp; /* We are using seperate values as placeholders for mechanisms for * private backbuffer/depthbuffer usage. @@ -458,20 +442,23 @@ dev_priv->back_pitch = init->back_pitch; dev_priv->depth_pitch = init->depth_pitch; + dev_priv->do_boxes = 0; + dev_priv->use_mi_batchbuffer_start = 0; /* Program Hardware Status Page */ - dev_priv->hw_status_page = pci_alloc_consistent(dev->pdev, PAGE_SIZE, + dev_priv->hw_status_page = + (unsigned long) pci_alloc_consistent(dev->pdev, PAGE_SIZE, &dev_priv->dma_status_page); - if(dev_priv->hw_status_page == NULL) { + if(dev_priv->hw_status_page == 0UL) { dev->dev_private = (void *)dev_priv; i830_dma_cleanup(dev); DRM_ERROR("Can not allocate hardware status page\n"); return -ENOMEM; } - memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); + memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); + DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); - I830_WRITE(0x02080, dev_priv->dma_status_page); + I830_WRITE(0x02080, dev_priv->dma_status_page); DRM_DEBUG("Enabled hardware status page\n"); /* Now we need to init our freelist */ @@ -517,83 +504,107 @@ return retcode; } +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define ST1_ENABLE (1<<16) +#define ST1_MASK (0xffff) + /* Most efficient way to verify state for the i830 is as it is * emitted. Non-conformant state is silently dropped. - * - * Use 'volatile' & local var tmp to force the emitted values to be - * identical to the verified ones. */ -static void i830EmitContextVerified( drm_device_t *dev, - volatile unsigned int *code ) +static void i830EmitContextVerified( drm_device_t *dev, + unsigned int *code ) { drm_i830_private_t *dev_priv = dev->dev_private; int i, j = 0; unsigned int tmp; RING_LOCALS; - BEGIN_LP_RING( I830_CTX_SETUP_SIZE ); - for ( i = 0 ; i < I830_CTX_SETUP_SIZE ; i++ ) { + BEGIN_LP_RING( I830_CTX_SETUP_SIZE + 4 ); + + for ( i = 0 ; i < I830_CTXREG_BLENDCOLR0 ; i++ ) { tmp = code[i]; + if ((tmp & (7<<29)) == CMD_3D && + (tmp & (0x1f<<24)) < (0x1d<<24)) { + OUT_RING( tmp ); + j++; + } else { + DRM_ERROR("Skipping %d\n", i); + } + } -#if 0 - if ((tmp & (7<<29)) == (3<<29) && + OUT_RING( STATE3D_CONST_BLEND_COLOR_CMD ); + OUT_RING( code[I830_CTXREG_BLENDCOLR] ); + j += 2; + + for ( i = I830_CTXREG_VF ; i < I830_CTXREG_MCSB0 ; i++ ) { + tmp = code[i]; + if ((tmp & (7<<29)) == CMD_3D && (tmp & (0x1f<<24)) < (0x1d<<24)) { OUT_RING( tmp ); j++; } else { - printk("Skipping %d\n", i); + DRM_ERROR("Skipping %d\n", i); } -#else - OUT_RING( tmp ); - j++; -#endif } + OUT_RING( STATE3D_MAP_COORD_SETBIND_CMD ); + OUT_RING( code[I830_CTXREG_MCSB1] ); + j += 2; + if (j & 1) OUT_RING( 0 ); ADVANCE_LP_RING(); } -static void i830EmitTexVerified( drm_device_t *dev, - volatile unsigned int *code ) +static void i830EmitTexVerified( drm_device_t *dev, unsigned int *code ) { drm_i830_private_t *dev_priv = dev->dev_private; int i, j = 0; unsigned int tmp; RING_LOCALS; - BEGIN_LP_RING( I830_TEX_SETUP_SIZE ); - - OUT_RING( GFX_OP_MAP_INFO ); - OUT_RING( code[I830_TEXREG_MI1] ); - OUT_RING( code[I830_TEXREG_MI2] ); - OUT_RING( code[I830_TEXREG_MI3] ); - OUT_RING( code[I830_TEXREG_MI4] ); - OUT_RING( code[I830_TEXREG_MI5] ); - - for ( i = 6 ; i < I830_TEX_SETUP_SIZE ; i++ ) { - tmp = code[i]; - OUT_RING( tmp ); - j++; - } + if (code[I830_TEXREG_MI0] == GFX_OP_MAP_INFO || + (code[I830_TEXREG_MI0] & ~(0xf*LOAD_TEXTURE_MAP0)) == + (STATE3D_LOAD_STATE_IMMEDIATE_2|4)) { + + BEGIN_LP_RING( I830_TEX_SETUP_SIZE ); + + OUT_RING( code[I830_TEXREG_MI0] ); /* TM0LI */ + OUT_RING( code[I830_TEXREG_MI1] ); /* TM0S0 */ + OUT_RING( code[I830_TEXREG_MI2] ); /* TM0S1 */ + OUT_RING( code[I830_TEXREG_MI3] ); /* TM0S2 */ + OUT_RING( code[I830_TEXREG_MI4] ); /* TM0S3 */ + OUT_RING( code[I830_TEXREG_MI5] ); /* TM0S4 */ + + for ( i = 6 ; i < I830_TEX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + OUT_RING( tmp ); + j++; + } - if (j & 1) - OUT_RING( 0 ); + if (j & 1) + OUT_RING( 0 ); - ADVANCE_LP_RING(); + ADVANCE_LP_RING(); + } + else + printk("rejected packet %x\n", code[0]); } static void i830EmitTexBlendVerified( drm_device_t *dev, - volatile unsigned int *code, - volatile unsigned int num) + unsigned int *code, + unsigned int num) { drm_i830_private_t *dev_priv = dev->dev_private; int i, j = 0; unsigned int tmp; RING_LOCALS; - BEGIN_LP_RING( num ); + if (!num) + return; + + BEGIN_LP_RING( num + 1 ); for ( i = 0 ; i < num ; i++ ) { tmp = code[i]; @@ -616,6 +627,8 @@ int i; RING_LOCALS; + return; /* Is this right ? -- Arjan */ + BEGIN_LP_RING( 258 ); if(is_shared == 1) { @@ -629,44 +642,43 @@ OUT_RING(palette[i]); } OUT_RING(0); + /* KW: WHERE IS THE ADVANCE_LP_RING? This is effectively a noop! + */ } /* Need to do some additional checking when setting the dest buffer. */ static void i830EmitDestVerified( drm_device_t *dev, - volatile unsigned int *code ) + unsigned int *code ) { drm_i830_private_t *dev_priv = dev->dev_private; unsigned int tmp; RING_LOCALS; - BEGIN_LP_RING( I830_DEST_SETUP_SIZE + 6 ); + BEGIN_LP_RING( I830_DEST_SETUP_SIZE + 10 ); + tmp = code[I830_DESTREG_CBUFADDR]; - if (tmp == dev_priv->front_di1) { - /* Don't use fence when front buffer rendering */ - OUT_RING( CMD_OP_DESTBUFFER_INFO ); - OUT_RING( BUF_3D_ID_COLOR_BACK | - BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) ); - OUT_RING( tmp ); + if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { + if (((int)outring) & 8) { + OUT_RING(0); + OUT_RING(0); + } OUT_RING( CMD_OP_DESTBUFFER_INFO ); - OUT_RING( BUF_3D_ID_DEPTH | - BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp)); - OUT_RING( dev_priv->zi1 ); - } else if(tmp == dev_priv->back_di1) { - OUT_RING( CMD_OP_DESTBUFFER_INFO ); OUT_RING( BUF_3D_ID_COLOR_BACK | BUF_3D_PITCH(dev_priv->back_pitch * dev_priv->cpp) | BUF_3D_USE_FENCE); OUT_RING( tmp ); + OUT_RING( 0 ); OUT_RING( CMD_OP_DESTBUFFER_INFO ); OUT_RING( BUF_3D_ID_DEPTH | BUF_3D_USE_FENCE | BUF_3D_PITCH(dev_priv->depth_pitch * dev_priv->cpp)); OUT_RING( dev_priv->zi1 ); + OUT_RING( 0 ); } else { - DRM_DEBUG("bad di1 %x (allow %x or %x)\n", + DRM_ERROR("bad di1 %x (allow %x or %x)\n", tmp, dev_priv->front_di1, dev_priv->back_di1); } @@ -688,25 +700,39 @@ if((tmp & ~0x3) == GFX_OP_SCISSOR_ENABLE) { OUT_RING( tmp ); } else { - DRM_DEBUG("bad scissor enable\n"); + DRM_ERROR("bad scissor enable\n"); OUT_RING( 0 ); } - OUT_RING( code[I830_DESTREG_SENABLE] ); - OUT_RING( GFX_OP_SCISSOR_RECT ); OUT_RING( code[I830_DESTREG_SR1] ); OUT_RING( code[I830_DESTREG_SR2] ); + OUT_RING( 0 ); ADVANCE_LP_RING(); } +static void i830EmitStippleVerified( drm_device_t *dev, + unsigned int *code ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + BEGIN_LP_RING( 2 ); + OUT_RING( GFX_OP_STIPPLE ); + OUT_RING( code[1] ); + ADVANCE_LP_RING(); +} + + static void i830EmitState( drm_device_t *dev ) { drm_i830_private_t *dev_priv = dev->dev_private; drm_i830_sarea_t *sarea_priv = dev_priv->sarea_priv; unsigned int dirty = sarea_priv->dirty; + DRM_DEBUG("%s %x\n", __FUNCTION__, dirty); + if (dirty & I830_UPLOAD_BUFFERS) { i830EmitDestVerified( dev, sarea_priv->BufferState ); sarea_priv->dirty &= ~I830_UPLOAD_BUFFERS; @@ -740,17 +766,154 @@ } if (dirty & I830_UPLOAD_TEX_PALETTE_SHARED) { - i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1); + i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 1); + } else { + if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) { + i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0); + } + if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) { + i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1); + } + + /* 1.3: + */ +#if 0 + if (dirty & I830_UPLOAD_TEX_PALETTE_N(2)) { + i830EmitTexPalette(dev, sarea_priv->Palette2[0], 0, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2); + } + if (dirty & I830_UPLOAD_TEX_PALETTE_N(3)) { + i830EmitTexPalette(dev, sarea_priv->Palette2[1], 1, 0); + sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(2); + } +#endif + } + + /* 1.3: + */ + if (dirty & I830_UPLOAD_STIPPLE) { + i830EmitStippleVerified( dev, + sarea_priv->StippleState); + sarea_priv->dirty &= ~I830_UPLOAD_STIPPLE; + } + + if (dirty & I830_UPLOAD_TEX2) { + i830EmitTexVerified( dev, sarea_priv->TexState2 ); + sarea_priv->dirty &= ~I830_UPLOAD_TEX2; + } + + if (dirty & I830_UPLOAD_TEX3) { + i830EmitTexVerified( dev, sarea_priv->TexState3 ); + sarea_priv->dirty &= ~I830_UPLOAD_TEX3; + } + + + if (dirty & I830_UPLOAD_TEXBLEND2) { + i830EmitTexBlendVerified( + dev, + sarea_priv->TexBlendState2, + sarea_priv->TexBlendStateWordsUsed2); + + sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND2; + } + + if (dirty & I830_UPLOAD_TEXBLEND3) { + i830EmitTexBlendVerified( + dev, + sarea_priv->TexBlendState3, + sarea_priv->TexBlendStateWordsUsed3); + sarea_priv->dirty &= ~I830_UPLOAD_TEXBLEND3; + } +} + +/* ================================================================ + * Performance monitoring functions + */ + +static void i830_fill_box( drm_device_t *dev, + int x, int y, int w, int h, + int r, int g, int b ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + u32 color; + unsigned int BR13, CMD; + RING_LOCALS; + + BR13 = (0xF0 << 16) | (dev_priv->pitch * dev_priv->cpp) | (1<<24); + CMD = XY_COLOR_BLT_CMD; + x += dev_priv->sarea_priv->boxes[0].x1; + y += dev_priv->sarea_priv->boxes[0].y1; + + if (dev_priv->cpp == 4) { + BR13 |= (1<<25); + CMD |= (XY_COLOR_BLT_WRITE_ALPHA | XY_COLOR_BLT_WRITE_RGB); + color = (((0xff) << 24) | (r << 16) | (g << 8) | b); } else { - if (dirty & I830_UPLOAD_TEX_PALETTE_N(0)) { - i830EmitTexPalette(dev, sarea_priv->Palette[0], 0, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(0); - } - if (dirty & I830_UPLOAD_TEX_PALETTE_N(1)) { - i830EmitTexPalette(dev, sarea_priv->Palette[1], 1, 0); - sarea_priv->dirty &= ~I830_UPLOAD_TEX_PALETTE_N(1); - } + color = (((r & 0xf8) << 8) | + ((g & 0xfc) << 3) | + ((b & 0xf8) >> 3)); + } + + BEGIN_LP_RING( 6 ); + OUT_RING( CMD ); + OUT_RING( BR13 ); + OUT_RING( (y << 16) | x ); + OUT_RING( ((y+h) << 16) | (x+w) ); + + if ( dev_priv->current_page == 1 ) { + OUT_RING( dev_priv->front_offset ); + } else { + OUT_RING( dev_priv->back_offset ); + } + + OUT_RING( color ); + ADVANCE_LP_RING(); +} + +static void i830_cp_performance_boxes( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + + /* Purple box for page flipping + */ + if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_FLIP ) + i830_fill_box( dev, 4, 4, 8, 8, 255, 0, 255 ); + + /* Red box if we have to wait for idle at any point + */ + if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_WAIT ) + i830_fill_box( dev, 16, 4, 8, 8, 255, 0, 0 ); + + /* Blue box: lost context? + */ + if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_LOST_CONTEXT ) + i830_fill_box( dev, 28, 4, 8, 8, 0, 0, 255 ); + + /* Yellow box for texture swaps + */ + if ( dev_priv->sarea_priv->perf_boxes & I830_BOX_TEXTURE_LOAD ) + i830_fill_box( dev, 40, 4, 8, 8, 255, 255, 0 ); + + /* Green box if hardware never idles (as far as we can tell) + */ + if ( !(dev_priv->sarea_priv->perf_boxes & I830_BOX_RING_EMPTY) ) + i830_fill_box( dev, 64, 4, 8, 8, 0, 255, 0 ); + + + /* Draw bars indicating number of buffers allocated + * (not a great measure, easily confused) + */ + if (dev_priv->dma_used) { + int bar = dev_priv->dma_used / 10240; + if (bar > 100) bar = 100; + if (bar < 1) bar = 1; + i830_fill_box( dev, 4, 16, bar, 4, 196, 128, 128 ); + dev_priv->dma_used = 0; } + + dev_priv->sarea_priv->perf_boxes = 0; } static void i830_dma_dispatch_clear( drm_device_t *dev, int flags, @@ -768,6 +931,15 @@ unsigned int BR13, CMD, D_CMD; RING_LOCALS; + + if ( dev_priv->current_page == 1 ) { + unsigned int tmp = flags; + + flags &= ~(I830_FRONT | I830_BACK); + if ( tmp & I830_FRONT ) flags |= I830_BACK; + if ( tmp & I830_BACK ) flags |= I830_FRONT; + } + i830_kernel_lost_context(dev); switch(cpp) { @@ -808,7 +980,7 @@ OUT_RING( BR13 ); OUT_RING( (pbox->y1 << 16) | pbox->x1 ); OUT_RING( (pbox->y2 << 16) | pbox->x2 ); - OUT_RING( 0 ); + OUT_RING( dev_priv->front_offset ); OUT_RING( clear_color ); ADVANCE_LP_RING(); } @@ -847,13 +1019,17 @@ drm_clip_rect_t *pbox = sarea_priv->boxes; int pitch = dev_priv->pitch; int cpp = dev_priv->cpp; - int ofs = dev_priv->back_offset; int i; unsigned int CMD, BR13; RING_LOCALS; DRM_DEBUG("swapbuffers\n"); + i830_kernel_lost_context(dev); + + if (dev_priv->do_boxes) + i830_cp_performance_boxes( dev ); + switch(cpp) { case 2: BR13 = (pitch * cpp) | (0xCC << 16) | (1<<24); @@ -870,7 +1046,6 @@ break; } - i830_kernel_lost_context(dev); if (nbox > I830_NR_SAREA_CLIPRECTS) nbox = I830_NR_SAREA_CLIPRECTS; @@ -890,23 +1065,72 @@ BEGIN_LP_RING( 8 ); OUT_RING( CMD ); OUT_RING( BR13 ); + OUT_RING( (pbox->y1 << 16) | pbox->x1 ); + OUT_RING( (pbox->y2 << 16) | pbox->x2 ); - OUT_RING( (pbox->y1 << 16) | - pbox->x1 ); - OUT_RING( (pbox->y2 << 16) | - pbox->x2 ); - - OUT_RING( 0 /* front ofs always zero */ ); - OUT_RING( (pbox->y1 << 16) | - pbox->x1 ); + if (dev_priv->current_page == 0) + OUT_RING( dev_priv->front_offset ); + else + OUT_RING( dev_priv->back_offset ); + OUT_RING( (pbox->y1 << 16) | pbox->x1 ); OUT_RING( BR13 & 0xffff ); - OUT_RING( ofs ); + + if (dev_priv->current_page == 0) + OUT_RING( dev_priv->back_offset ); + else + OUT_RING( dev_priv->front_offset ); ADVANCE_LP_RING(); } } +static void i830_dma_dispatch_flip( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n", + __FUNCTION__, + dev_priv->current_page, + dev_priv->sarea_priv->pf_current_page); + + i830_kernel_lost_context(dev); + + if (dev_priv->do_boxes) { + dev_priv->sarea_priv->perf_boxes |= I830_BOX_FLIP; + i830_cp_performance_boxes( dev ); + } + + + BEGIN_LP_RING( 2 ); + OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + BEGIN_LP_RING( 6 ); + OUT_RING( CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP ); + OUT_RING( 0 ); + if ( dev_priv->current_page == 0 ) { + OUT_RING( dev_priv->back_offset ); + dev_priv->current_page = 1; + } else { + OUT_RING( dev_priv->front_offset ); + dev_priv->current_page = 0; + } + OUT_RING(0); + ADVANCE_LP_RING(); + + + BEGIN_LP_RING( 2 ); + OUT_RING( MI_WAIT_FOR_EVENT | + MI_WAIT_FOR_PLANE_A_FLIP ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; +} static void i830_dma_dispatch_vertex(drm_device_t *dev, drm_buf_t *buf, @@ -936,7 +1160,7 @@ } } - if (used > 4*1024) + if (used > 4*1023) used = 0; if (sarea_priv->dirty) @@ -953,12 +1177,19 @@ DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4); if (buf_priv->currently_mapped == I830_BUF_MAPPED) { - *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | - sarea_priv->vertex_prim | - ((used/4)-2)); + u32 *vp = buf_priv->virtual; + + vp[0] = (GFX_OP_PRIMITIVE | + sarea_priv->vertex_prim | + ((used/4)-2)); + + if (dev_priv->use_mi_batchbuffer_start) { + vp[used/4] = MI_BATCH_BUFFER_END; + used += 4; + } if (used & 4) { - *(u32 *)((u32)buf_priv->virtual + used) = 0; + vp[used/4] = 0; used += 4; } @@ -978,80 +1209,45 @@ ADVANCE_LP_RING(); } - BEGIN_LP_RING(4); + if (dev_priv->use_mi_batchbuffer_start) { + BEGIN_LP_RING(2); + OUT_RING( MI_BATCH_BUFFER_START | (2<<6) ); + OUT_RING( start | MI_BATCH_NON_SECURE ); + ADVANCE_LP_RING(); + } + else { + BEGIN_LP_RING(4); + OUT_RING( MI_BATCH_BUFFER ); + OUT_RING( start | MI_BATCH_NON_SECURE ); + OUT_RING( start + used - 4 ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } - OUT_RING( MI_BATCH_BUFFER ); - OUT_RING( start | MI_BATCH_NON_SECURE ); - OUT_RING( start + used - 4 ); - OUT_RING( 0 ); - ADVANCE_LP_RING(); - } while (++i < nbox); } - BEGIN_LP_RING(10); - OUT_RING( CMD_STORE_DWORD_IDX ); - OUT_RING( 20 ); - OUT_RING( dev_priv->counter ); - OUT_RING( 0 ); - if (discard) { + dev_priv->counter++; + + (void) cmpxchg(buf_priv->in_use, I830_BUF_CLIENT, + I830_BUF_HARDWARE); + + BEGIN_LP_RING(8); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 20 ); + OUT_RING( dev_priv->counter ); OUT_RING( CMD_STORE_DWORD_IDX ); OUT_RING( buf_priv->my_use_idx ); OUT_RING( I830_BUF_FREE ); + OUT_RING( CMD_REPORT_HEAD ); OUT_RING( 0 ); + ADVANCE_LP_RING(); } - - OUT_RING( CMD_REPORT_HEAD ); - OUT_RING( 0 ); - ADVANCE_LP_RING(); -} - -/* Interrupts are only for flushing */ -void i830_dma_service(int irq, void *device, struct pt_regs *regs) -{ - drm_device_t *dev = (drm_device_t *)device; - drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; - u16 temp; - - temp = I830_READ16(I830REG_INT_IDENTITY_R); - temp = temp & ~(0x6000); - if(temp != 0) I830_WRITE16(I830REG_INT_IDENTITY_R, - temp); /* Clear all interrupts */ - else - return; - - queue_task(&dev->tq, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -void DRM(dma_immediate_bh)(void *device) -{ - drm_device_t *dev = (drm_device_t *) device; - drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; - - atomic_set(&dev_priv->flush_done, 1); - wake_up_interruptible(&dev_priv->flush_queue); } -static inline void i830_dma_emit_flush(drm_device_t *dev) -{ - drm_i830_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - - i830_kernel_lost_context(dev); - BEGIN_LP_RING(2); - OUT_RING( CMD_REPORT_HEAD ); - OUT_RING( GFX_OP_USER_INTERRUPT ); - ADVANCE_LP_RING(); - - i830_wait_ring( dev, dev_priv->ring.Size - 8 ); - atomic_set(&dev_priv->flush_done, 1); - wake_up_interruptible(&dev_priv->flush_queue); -} - -static inline void i830_dma_quiescent_emit(drm_device_t *dev) +void i830_dma_quiescent(drm_device_t *dev) { drm_i830_private_t *dev_priv = dev->dev_private; RING_LOCALS; @@ -1062,79 +1258,27 @@ OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); OUT_RING( CMD_REPORT_HEAD ); OUT_RING( 0 ); - OUT_RING( GFX_OP_USER_INTERRUPT ); + OUT_RING( 0 ); ADVANCE_LP_RING(); - i830_wait_ring( dev, dev_priv->ring.Size - 8 ); - atomic_set(&dev_priv->flush_done, 1); - wake_up_interruptible(&dev_priv->flush_queue); -} - -void i830_dma_quiescent(drm_device_t *dev) -{ - DECLARE_WAITQUEUE(entry, current); - drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; - unsigned long end; - - if(dev_priv == NULL) { - return; - } - atomic_set(&dev_priv->flush_done, 0); - add_wait_queue(&dev_priv->flush_queue, &entry); - end = jiffies + (HZ*3); - - for (;;) { - current->state = TASK_INTERRUPTIBLE; - i830_dma_quiescent_emit(dev); - if (atomic_read(&dev_priv->flush_done) == 1) break; - if(time_before(end, jiffies)) { - DRM_ERROR("lockup\n"); - break; - } - schedule_timeout(HZ*3); - if (signal_pending(current)) { - break; - } - } - - current->state = TASK_RUNNING; - remove_wait_queue(&dev_priv->flush_queue, &entry); - - return; + i830_wait_ring( dev, dev_priv->ring.Size - 8, __FUNCTION__ ); } static int i830_flush_queue(drm_device_t *dev) { - DECLARE_WAITQUEUE(entry, current); - drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + drm_i830_private_t *dev_priv = dev->dev_private; drm_device_dma_t *dma = dev->dma; - unsigned long end; - int i, ret = 0; + int i, ret = 0; + RING_LOCALS; + + i830_kernel_lost_context(dev); - if(dev_priv == NULL) { - return 0; - } - atomic_set(&dev_priv->flush_done, 0); - add_wait_queue(&dev_priv->flush_queue, &entry); - end = jiffies + (HZ*3); - for (;;) { - current->state = TASK_INTERRUPTIBLE; - i830_dma_emit_flush(dev); - if (atomic_read(&dev_priv->flush_done) == 1) break; - if(time_before(end, jiffies)) { - DRM_ERROR("lockup\n"); - break; - } - schedule_timeout(HZ*3); - if (signal_pending(current)) { - ret = -EINTR; /* Can't restart */ - break; - } - } - - current->state = TASK_RUNNING; - remove_wait_queue(&dev_priv->flush_queue, &entry); + BEGIN_LP_RING(2); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + i830_wait_ring( dev, dev_priv->ring.Size - 8, __FUNCTION__ ); for (i = 0; i < dma->buf_count; i++) { drm_buf_t *buf = dma->buflist[ i ]; @@ -1146,7 +1290,7 @@ if (used == I830_BUF_HARDWARE) DRM_DEBUG("reclaimed from HARDWARE\n"); if (used == I830_BUF_CLIENT) - DRM_DEBUG("still on client HARDWARE\n"); + DRM_DEBUG("still on client\n"); } return ret; @@ -1185,8 +1329,7 @@ { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - - DRM_DEBUG("i830_flush_ioctl\n"); + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { DRM_ERROR("i830_flush_ioctl called without lock held\n"); return -EINVAL; @@ -1275,6 +1418,53 @@ return 0; } + + +/* Not sure why this isn't set all the time: + */ +static void i830_do_init_pageflip( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + dev_priv->page_flipping = 1; + dev_priv->current_page = 0; + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; +} + +int i830_do_cleanup_pageflip( drm_device_t *dev ) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + if (dev_priv->current_page != 0) + i830_dma_dispatch_flip( dev ); + + dev_priv->page_flipping = 0; + return 0; +} + +int i830_flip_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_flip_buf called without lock held\n"); + return -EINVAL; + } + + if (!dev_priv->page_flipping) + i830_do_init_pageflip( dev ); + + i830_dma_dispatch_flip( dev ); + return 0; +} + int i830_getage(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { @@ -1324,46 +1514,80 @@ return retcode; } -int i830_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, +int i830_copybuf(struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + /* Never copy - 2.4.x doesn't need it */ + return 0; +} + +int i830_docopy(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { + return 0; +} + + + +int i830_getparam( struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg ) +{ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; - drm_i830_copy_t d; - drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; - u32 *hw_status = (u32 *)dev_priv->hw_status_page; - drm_i830_sarea_t *sarea_priv = (drm_i830_sarea_t *) - dev_priv->sarea_priv; - drm_buf_t *buf; - drm_i830_buf_priv_t *buf_priv; - drm_device_dma_t *dma = dev->dma; + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_getparam_t param; + int value; - if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { - DRM_ERROR("i830_dma called without lock held\n"); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } - - if (copy_from_user(&d, (drm_i830_copy_t *)arg, sizeof(d))) - return -EFAULT; - - if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL; - buf = dma->buflist[ d.idx ]; - buf_priv = buf->dev_private; - if (buf_priv->currently_mapped != I830_BUF_MAPPED) return -EPERM; - - if(d.used < 0 || d.used > buf->total) return -EINVAL; - if (copy_from_user(buf_priv->virtual, d.address, d.used)) + if (copy_from_user(¶m, (drm_i830_getparam_t *)arg, sizeof(param) )) return -EFAULT; - sarea_priv->last_dispatch = (int) hw_status[5]; + switch( param.param ) { + case I830_PARAM_IRQ_ACTIVE: + value = dev->irq ? 1 : 0; + break; + default: + return -EINVAL; + } + if ( copy_to_user( param.value, &value, sizeof(int) ) ) { + DRM_ERROR( "copy_to_user\n" ); + return -EFAULT; + } + return 0; } -int i830_docopy(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) + +int i830_setparam( struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg ) { - if(VM_DONTCOPY == 0) return 1; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_setparam_t param; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + if (copy_from_user(¶m, (drm_i830_setparam_t *)arg, sizeof(param) )) + return -EFAULT; + + switch( param.param ) { + case I830_SETPARAM_USE_MI_BATCHBUFFER_START: + dev_priv->use_mi_batchbuffer_start = param.value; + break; + default: + return -EINVAL; + } + return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830_drm.h linux.21rc1-ac2/drivers/char/drm/i830_drm.h --- linux.21rc1/drivers/char/drm/i830_drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -3,6 +3,9 @@ /* WARNING: These defines must be the same as what the Xserver uses. * if you change them, you must change the defines in the Xserver. + * + * KW: Actually, you can't ever change them because doing so would + * break backwards compatibility. */ #ifndef _I830_DEFINES_ @@ -18,14 +21,12 @@ #define I830_NR_TEX_REGIONS 64 #define I830_LOG_MIN_TEX_REGION_SIZE 16 -/* if defining I830_ENABLE_4_TEXTURES, do it in i830_3d_reg.h, too */ -#if !defined(I830_ENABLE_4_TEXTURES) +/* KW: These aren't correct but someone set them to two and then + * released the module. Now we can't change them as doing so would + * break backwards compatibility. + */ #define I830_TEXTURE_COUNT 2 -#define I830_TEXBLEND_COUNT 2 /* always same as TEXTURE_COUNT? */ -#else /* defined(I830_ENABLE_4_TEXTURES) */ -#define I830_TEXTURE_COUNT 4 -#define I830_TEXBLEND_COUNT 4 /* always same as TEXTURE_COUNT? */ -#endif /* I830_ENABLE_4_TEXTURES */ +#define I830_TEXBLEND_COUNT I830_TEXTURE_COUNT #define I830_TEXBLEND_SIZE 12 /* (4 args + op) * 2 + COLOR_FACTOR */ @@ -57,6 +58,7 @@ #define I830_UPLOAD_TEXBLEND_MASK 0xf00000 #define I830_UPLOAD_TEX_PALETTE_N(n) (0x1000000 << (n)) #define I830_UPLOAD_TEX_PALETTE_SHARED 0x4000000 +#define I830_UPLOAD_STIPPLE 0x8000000 /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -73,7 +75,6 @@ */ #define I830_DESTREG_CBUFADDR 0 -/* Invarient */ #define I830_DESTREG_DBUFADDR 1 #define I830_DESTREG_DV0 2 #define I830_DESTREG_DV1 3 @@ -109,6 +110,13 @@ #define I830_CTXREG_MCSB1 16 #define I830_CTX_SETUP_SIZE 17 +/* 1.3: Stipple state + */ +#define I830_STPREG_ST0 0 +#define I830_STPREG_ST1 1 +#define I830_STP_SETUP_SIZE 2 + + /* Texture state (per tex unit) */ @@ -124,6 +132,18 @@ #define I830_TEXREG_MCS 9 /* GFX_OP_MAP_COORD_SETS */ #define I830_TEX_SETUP_SIZE 10 +#define I830_TEXREG_TM0LI 0 /* load immediate 2 texture map n */ +#define I830_TEXREG_TM0S0 1 +#define I830_TEXREG_TM0S1 2 +#define I830_TEXREG_TM0S2 3 +#define I830_TEXREG_TM0S3 4 +#define I830_TEXREG_TM0S4 5 +#define I830_TEXREG_NOP0 6 /* noop */ +#define I830_TEXREG_NOP1 7 /* noop */ +#define I830_TEXREG_NOP2 8 /* noop */ +#define __I830_TEXREG_MCS 9 /* GFX_OP_MAP_COORD_SETS -- shared */ +#define __I830_TEX_SETUP_SIZE 10 + #define I830_FRONT 0x1 #define I830_BACK 0x2 #define I830_DEPTH 0x4 @@ -199,8 +219,53 @@ int ctxOwner; /* last context to upload state */ int vertex_prim; + + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + + int perf_boxes; /* performance boxes to be displayed */ + + /* Here's the state for texunits 2,3: + */ + unsigned int TexState2[I830_TEX_SETUP_SIZE]; + unsigned int TexBlendState2[I830_TEXBLEND_SIZE]; + unsigned int TexBlendStateWordsUsed2; + + unsigned int TexState3[I830_TEX_SETUP_SIZE]; + unsigned int TexBlendState3[I830_TEXBLEND_SIZE]; + unsigned int TexBlendStateWordsUsed3; + + unsigned int StippleState[I830_STP_SETUP_SIZE]; } drm_i830_sarea_t; +/* Flags for perf_boxes + */ +#define I830_BOX_RING_EMPTY 0x1 /* populated by kernel */ +#define I830_BOX_FLIP 0x2 /* populated by kernel */ +#define I830_BOX_WAIT 0x4 /* populated by kernel & client */ +#define I830_BOX_TEXTURE_LOAD 0x8 /* populated by kernel */ +#define I830_BOX_LOST_CONTEXT 0x10 /* populated by client */ + + +/* I830 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_I830_INIT DRM_IOW( 0x40, drm_i830_init_t) +#define DRM_IOCTL_I830_VERTEX DRM_IOW( 0x41, drm_i830_vertex_t) +#define DRM_IOCTL_I830_CLEAR DRM_IOW( 0x42, drm_i830_clear_t) +#define DRM_IOCTL_I830_FLUSH DRM_IO ( 0x43) +#define DRM_IOCTL_I830_GETAGE DRM_IO ( 0x44) +#define DRM_IOCTL_I830_GETBUF DRM_IOWR(0x45, drm_i830_dma_t) +#define DRM_IOCTL_I830_SWAP DRM_IO ( 0x46) +#define DRM_IOCTL_I830_COPY DRM_IOW( 0x47, drm_i830_copy_t) +#define DRM_IOCTL_I830_DOCOPY DRM_IO ( 0x48) +#define DRM_IOCTL_I830_FLIP DRM_IO ( 0x49) +#define DRM_IOCTL_I830_IRQ_EMIT DRM_IOWR(0x4a, drm_i830_irq_emit_t) +#define DRM_IOCTL_I830_IRQ_WAIT DRM_IOW( 0x4b, drm_i830_irq_wait_t) +#define DRM_IOCTL_I830_GETPARAM DRM_IOWR(0x4c, drm_i830_getparam_t) +#define DRM_IOCTL_I830_SETPARAM DRM_IOWR(0x4d, drm_i830_setparam_t) + typedef struct _drm_i830_clear { int clear_color; int clear_depth; @@ -235,4 +300,36 @@ int granted; } drm_i830_dma_t; + +/* 1.3: Userspace can request & wait on irq's: + */ +typedef struct drm_i830_irq_emit { + int *irq_seq; +} drm_i830_irq_emit_t; + +typedef struct drm_i830_irq_wait { + int irq_seq; +} drm_i830_irq_wait_t; + + +/* 1.3: New ioctl to query kernel params: + */ +#define I830_PARAM_IRQ_ACTIVE 1 + +typedef struct drm_i830_getparam { + int param; + int *value; +} drm_i830_getparam_t; + + +/* 1.3: New ioctl to set kernel params: + */ +#define I830_SETPARAM_USE_MI_BATCHBUFFER_START 1 + +typedef struct drm_i830_setparam { + int param; + int value; +} drm_i830_setparam_t; + + #endif /* _I830_DRM_H_ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830_drv.c linux.21rc1-ac2/drivers/char/drm/i830_drv.c --- linux.21rc1/drivers/char/drm/i830_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -29,41 +29,16 @@ * Jeff Hartmann * Gareth Hughes * Abraham vd Merwe + * Keith Whitwell */ #include #include "i830.h" #include "drmP.h" +#include "drm.h" +#include "i830_drm.h" #include "i830_drv.h" -#define DRIVER_AUTHOR "VA Linux Systems Inc." - -#define DRIVER_NAME "i830" -#define DRIVER_DESC "Intel 830M" -#define DRIVER_DATE "20011004" - -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 0 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_INIT)] = { i830_dma_init, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_VERTEX)] = { i830_dma_vertex, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_CLEAR)] = { i830_clear_bufs, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_FLUSH)] = { i830_flush_ioctl, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_GETAGE)] = { i830_getage, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_GETBUF)] = { i830_getbuf, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_SWAP)] = { i830_swap_bufs, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_COPY)] = { i830_copybuf, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_I830_DOCOPY)] = { i830_docopy, 1, 0 }, - -#define __HAVE_COUNTERS 4 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY -#define __HAVE_COUNTER9 _DRM_STAT_DMA - - #include "drm_agpsupport.h" #include "drm_auth.h" #include "drm_bufs.h" @@ -72,25 +47,6 @@ #include "drm_drawable.h" #include "drm_drv.h" -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init i830_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", i830_options ); -#endif - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830_drv.h linux.21rc1-ac2/drivers/char/drm/i830_drv.h --- linux.21rc1/drivers/char/drm/i830_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -61,24 +61,36 @@ drm_i830_sarea_t *sarea_priv; drm_i830_ring_buffer_t ring; - u8 *hw_status_page; + unsigned long hw_status_page; unsigned long counter; - - dma_addr_t dma_status_page; - atomic_t flush_done; - wait_queue_head_t flush_queue; /* Processes waiting until flush */ + dma_addr_t dma_status_page; + drm_buf_t *mmap_buffer; u32 front_di1, back_di1, zi1; int back_offset; int depth_offset; + int front_offset; int w, h; int pitch; int back_pitch; int depth_pitch; unsigned int cpp; + + int do_boxes; + int dma_used; + + int current_page; + int page_flipping; + + wait_queue_head_t irq_queue; + atomic_t irq_received; + atomic_t irq_emitted; + + int use_mi_batchbuffer_start; + } drm_i830_private_t; /* i830_dma.c */ @@ -109,24 +121,81 @@ extern int i830_clear_bufs(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -#define I830_VERBOSE 0 +extern int i830_flip_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i830_getparam( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + +extern int i830_setparam( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); + +/* i830_irq.c */ +extern int i830_irq_emit( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int i830_irq_wait( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ); +extern int i830_wait_irq(drm_device_t *dev, int irq_nr); +extern int i830_emit_irq(drm_device_t *dev); + #define I830_BASE(reg) ((unsigned long) \ dev_priv->mmio_map->handle) #define I830_ADDR(reg) (I830_BASE(reg) + reg) -#define I830_DEREF(reg) *(__volatile__ int *)I830_ADDR(reg) -#define I830_READ(reg) I830_DEREF(reg) -#define I830_WRITE(reg,val) do { I830_DEREF(reg) = val; } while (0) +#define I830_DEREF(reg) *(__volatile__ unsigned int *)I830_ADDR(reg) +#define I830_READ(reg) readl((volatile u32 *)I830_ADDR(reg)) +#define I830_WRITE(reg,val) writel(val, (volatile u32 *)I830_ADDR(reg)) #define I830_DEREF16(reg) *(__volatile__ u16 *)I830_ADDR(reg) #define I830_READ16(reg) I830_DEREF16(reg) #define I830_WRITE16(reg,val) do { I830_DEREF16(reg) = val; } while (0) + + +#define I830_VERBOSE 0 + +#define RING_LOCALS unsigned int outring, ringmask, outcount; \ + volatile char *virt; + +#define BEGIN_LP_RING(n) do { \ + if (I830_VERBOSE) \ + printk("BEGIN_LP_RING(%d) in %s\n", \ + n, __FUNCTION__); \ + if (dev_priv->ring.space < n*4) \ + i830_wait_ring(dev, n*4, __FUNCTION__); \ + outcount = 0; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ +} while (0) + + +#define OUT_RING(n) do { \ + if (I830_VERBOSE) printk(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outcount++; \ + outring += 4; \ + outring &= ringmask; \ +} while (0) + +#define ADVANCE_LP_RING() do { \ + if (I830_VERBOSE) printk("ADVANCE_LP_RING %x\n", outring); \ + dev_priv->ring.tail = outring; \ + dev_priv->ring.space -= outcount * 4; \ + I830_WRITE(LP_RING + RING_TAIL, outring); \ +} while(0) + +extern int i830_wait_ring(drm_device_t *dev, int n, const char *caller); + + #define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) #define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) #define CMD_REPORT_HEAD (7<<23) #define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) #define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) +#define STATE3D_LOAD_STATE_IMMEDIATE_2 ((0x3<<29)|(0x1d<<24)|(0x03<<16)) +#define LOAD_TEXTURE_MAP0 (1<<11) + #define INST_PARSER_CLIENT 0x00000000 #define INST_OP_FLUSH 0x02000000 #define INST_FLUSH_MAP_CACHE 0x00000001 @@ -142,18 +211,21 @@ #define I830REG_INT_MASK_R 0x020a8 #define I830REG_INT_ENABLE_R 0x020a0 +#define I830_IRQ_RESERVED ((1<<13)|(3<<2)) + + #define LP_RING 0x2030 #define HP_RING 0x2040 #define RING_TAIL 0x00 -#define TAIL_ADDR 0x000FFFF8 +#define TAIL_ADDR 0x001FFFF8 #define RING_HEAD 0x04 #define HEAD_WRAP_COUNT 0xFFE00000 #define HEAD_WRAP_ONE 0x00200000 #define HEAD_ADDR 0x001FFFFC #define RING_START 0x08 -#define START_ADDR 0x00FFFFF8 +#define START_ADDR 0x0xFFFFF000 #define RING_LEN 0x0C -#define RING_NR_PAGES 0x000FF000 +#define RING_NR_PAGES 0x001FF000 #define RING_REPORT_MASK 0x00000006 #define RING_REPORT_64K 0x00000002 #define RING_REPORT_128K 0x00000004 @@ -184,6 +256,12 @@ #define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) +#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2) +#define ASYNC_FLIP (1<<22) + +#define CMD_3D (0x3<<29) +#define STATE3D_CONST_BLEND_COLOR_CMD (CMD_3D|(0x1d<<24)|(0x88<<16)) +#define STATE3D_MAP_COORD_SETBIND_CMD (CMD_3D|(0x1d<<24)|(0x02<<16)) #define BR00_BITBLT_CLIENT 0x40000000 #define BR00_OP_COLOR_BLT 0x10000000 @@ -208,8 +286,15 @@ #define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) #define MI_BATCH_BUFFER ((0x30<<23)|1) +#define MI_BATCH_BUFFER_START (0x31<<23) +#define MI_BATCH_BUFFER_END (0xA<<23) #define MI_BATCH_NON_SECURE (1) +#define MI_WAIT_FOR_EVENT ((0x3<<23)) +#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2) +#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1) + +#define MI_LOAD_SCAN_LINES_INCL ((0x12<<23)) #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830.h linux.21rc1-ac2/drivers/char/drm/i830.h --- linux.21rc1/drivers/char/drm/i830.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830.h 2003-04-22 16:44:36.000000000 +0100 @@ -41,6 +41,48 @@ #define __HAVE_MTRR 1 #define __HAVE_CTX_BITMAP 1 +#define DRIVER_AUTHOR "VA Linux Systems Inc." + +#define DRIVER_NAME "i830" +#define DRIVER_DESC "Intel 830M" +#define DRIVER_DATE "20021108" + +/* Interface history: + * + * 1.1: Original. + * 1.2: ? + * 1.3: New irq emit/wait ioctls. + * New pageflip ioctl. + * New getparam ioctl. + * State for texunits 3&4 in sarea. + * New (alternative) layout for texture state. + */ +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 3 +#define DRIVER_PATCHLEVEL 2 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_INIT)] = { i830_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_VERTEX)] = { i830_dma_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_CLEAR)] = { i830_clear_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_FLUSH)] = { i830_flush_ioctl, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_GETAGE)] = { i830_getage, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_GETBUF)] = { i830_getbuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_SWAP)] = { i830_swap_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_COPY)] = { i830_copybuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_DOCOPY)] = { i830_docopy, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_FLIP)] = { i830_flip_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_IRQ_EMIT)] = { i830_irq_emit, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_IRQ_WAIT)] = { i830_irq_wait, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_GETPARAM)] = { i830_getparam, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I830_SETPARAM)] = { i830_setparam, 1, 0 } + +#define __HAVE_COUNTERS 4 +#define __HAVE_COUNTER6 _DRM_STAT_IRQ +#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY +#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY +#define __HAVE_COUNTER9 _DRM_STAT_DMA + /* Driver customization: */ #define __HAVE_RELEASE 1 @@ -60,51 +102,50 @@ i830_dma_quiescent( dev ); \ } while (0) + +/* Driver will work either way: IRQ's save cpu time when waiting for + * the card, but are subject to subtle interactions between bios, + * hardware and the driver. + */ +#define USE_IRQS 0 + + +#if USE_IRQS #define __HAVE_DMA_IRQ 1 -#define __HAVE_DMA_IRQ_BH 1 -#define __HAVE_SHARED_IRQ 1 -#define DRIVER_PREINSTALL() do { \ - drm_i830_private_t *dev_priv = \ - (drm_i830_private_t *)dev->dev_private; \ - u16 tmp; \ - tmp = I830_READ16( I830REG_HWSTAM ); \ - tmp = tmp & 0x6000; \ - I830_WRITE16( I830REG_HWSTAM, tmp ); \ - \ - tmp = I830_READ16( I830REG_INT_MASK_R ); \ - tmp = tmp & 0x6000; /* Unmask interrupts */ \ - I830_WRITE16( I830REG_INT_MASK_R, tmp ); \ - tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; /* Disable all interrupts */ \ - I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ -} while (0) +#define __HAVE_SHARED_IRQ 1 -#define DRIVER_POSTINSTALL() do { \ - drm_i830_private_t *dev_priv = \ +#define DRIVER_PREINSTALL() do { \ + drm_i830_private_t *dev_priv = \ (drm_i830_private_t *)dev->dev_private; \ - u16 tmp; \ - tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; \ - tmp = tmp | 0x0003; /* Enable bp & user interrupts */ \ - I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ + \ + I830_WRITE16( I830REG_HWSTAM, 0xffff ); \ + I830_WRITE16( I830REG_INT_MASK_R, 0x0 ); \ + I830_WRITE16( I830REG_INT_ENABLE_R, 0x0 ); \ } while (0) -#define DRIVER_UNINSTALL() do { \ - drm_i830_private_t *dev_priv = \ - (drm_i830_private_t *)dev->dev_private; \ - u16 tmp; \ - if ( dev_priv ) { \ - tmp = I830_READ16( I830REG_INT_IDENTITY_R ); \ - tmp = tmp & ~(0x6000); /* Clear all interrupts */ \ - if ( tmp != 0 ) \ - I830_WRITE16( I830REG_INT_IDENTITY_R, tmp ); \ - \ - tmp = I830_READ16( I830REG_INT_ENABLE_R ); \ - tmp = tmp & 0x6000; /* Disable all interrupts */ \ - I830_WRITE16( I830REG_INT_ENABLE_R, tmp ); \ - } \ + +#define DRIVER_POSTINSTALL() do { \ + drm_i830_private_t *dev_priv = \ + (drm_i830_private_t *)dev->dev_private; \ + I830_WRITE16( I830REG_INT_ENABLE_R, 0x2 ); \ + atomic_set(&dev_priv->irq_received, 0); \ + atomic_set(&dev_priv->irq_emitted, 0); \ + init_waitqueue_head(&dev_priv->irq_queue); \ } while (0) + +/* This gets called too late to be useful: dev_priv has already been + * freed. + */ +#define DRIVER_UNINSTALL() do { \ +} while (0) + +#else +#define __HAVE_DMA_IRQ 0 +#endif + + + /* Buffer customization: */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/i830_irq.c linux.21rc1-ac2/drivers/char/drm/i830_irq.c --- linux.21rc1/drivers/char/drm/i830_irq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/i830_irq.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,178 @@ +/* i830_dma.c -- DMA support for the I830 -*- linux-c -*- + * + * Copyright 2002 Tungsten Graphics, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Keith Whitwell + * + */ + + +#include "i830.h" +#include "drmP.h" +#include "drm.h" +#include "i830_drm.h" +#include "i830_drv.h" +#include /* For task queue support */ +#include + + +void DRM(dma_service)(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_i830_private_t *dev_priv = (drm_i830_private_t *)dev->dev_private; + u16 temp; + + temp = I830_READ16(I830REG_INT_IDENTITY_R); + printk("%s: %x\n", __FUNCTION__, temp); + + if(temp == 0) + return; + + I830_WRITE16(I830REG_INT_IDENTITY_R, temp); + + if (temp & 2) { + atomic_inc(&dev_priv->irq_received); + wake_up_interruptible(&dev_priv->irq_queue); + } +} + + +int i830_emit_irq(drm_device_t *dev) +{ + drm_i830_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG("%s\n", __FUNCTION__); + + atomic_inc(&dev_priv->irq_emitted); + + BEGIN_LP_RING(2); + OUT_RING( 0 ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + + return atomic_read(&dev_priv->irq_emitted); +} + + +int i830_wait_irq(drm_device_t *dev, int irq_nr) +{ + drm_i830_private_t *dev_priv = + (drm_i830_private_t *)dev->dev_private; + DECLARE_WAITQUEUE(entry, current); + unsigned long end = jiffies + HZ*3; + int ret = 0; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (atomic_read(&dev_priv->irq_received) >= irq_nr) + return 0; + + dev_priv->sarea_priv->perf_boxes |= I830_BOX_WAIT; + + add_wait_queue(&dev_priv->irq_queue, &entry); + + for (;;) { + current->state = TASK_INTERRUPTIBLE; + if (atomic_read(&dev_priv->irq_received) >= irq_nr) + break; + if (time_after(jiffies, end)) { + DRM_ERROR("timeout iir %x imr %x ier %x hwstam %x\n", + I830_READ16( I830REG_INT_IDENTITY_R ), + I830_READ16( I830REG_INT_MASK_R ), + I830_READ16( I830REG_INT_ENABLE_R ), + I830_READ16( I830REG_HWSTAM )); + + ret = -EBUSY; /* Lockup? Missed irq? */ + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + ret = -EINTR; + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->irq_queue, &entry); + return ret; +} + + +/* Needs the lock as it touches the ring. + */ +int i830_irq_emit( struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_irq_emit_t emit; + int result; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i830_irq_emit called without lock held\n"); + return -EINVAL; + } + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + if (copy_from_user( &emit, (drm_i830_irq_emit_t *)arg, sizeof(emit) )) + return -EFAULT; + + result = i830_emit_irq( dev ); + + if ( copy_to_user( emit.irq_seq, &result, sizeof(int) ) ) { + DRM_ERROR( "copy_to_user\n" ); + return -EFAULT; + } + + return 0; +} + + +/* Doesn't need the hardware lock. + */ +int i830_irq_wait( struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i830_private_t *dev_priv = dev->dev_private; + drm_i830_irq_wait_t irqwait; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + if (copy_from_user( &irqwait, (drm_i830_irq_wait_t *)arg, + sizeof(irqwait) )) + return -EFAULT; + + return i830_wait_irq( dev, irqwait.irq_seq ); +} + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/Makefile linux.21rc1-ac2/drivers/char/drm/Makefile --- linux.21rc1/drivers/char/drm/Makefile 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/Makefile 2003-04-22 16:44:36.000000000 +0100 @@ -10,9 +10,9 @@ r128-objs := r128_drv.o r128_cce.o r128_state.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o i810-objs := i810_drv.o i810_dma.o -i830-objs := i830_drv.o i830_dma.o +i830-objs := i830_drv.o i830_dma.o i830_irq.o -radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o +radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o ffb-objs := ffb_drv.o ffb_context.o sis-objs := sis_drv.o sis_ds.o sis_mm.o diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_dma.c linux.21rc1-ac2/drivers/char/drm/mga_dma.c --- linux.21rc1/drivers/char/drm/mga_dma.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_dma.c 2003-04-22 16:44:36.000000000 +0100 @@ -35,10 +35,11 @@ #include "mga.h" #include "drmP.h" +#include "drm.h" +#include "mga_drm.h" #include "mga_drv.h" - -#include /* For task queue support */ -#include +#include +#include "drm_os_linux.h" #define MGA_DEFAULT_USEC_TIMEOUT 10000 #define MGA_FREELIST_DEBUG 0 @@ -52,7 +53,7 @@ { u32 status = 0; int i; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; @@ -74,7 +75,7 @@ { u32 status = 0; int i; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK; @@ -93,7 +94,7 @@ drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_mga_primary_buffer_t *primary = &dev_priv->prim; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); /* The primary DMA stream should look like new right about now. */ @@ -114,7 +115,7 @@ int mga_do_engine_reset( drm_mga_private_t *dev_priv ) { - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); /* Okay, so we've completely screwed up and locked the engine. * How about we clean up after ourselves? @@ -160,8 +161,8 @@ u32 head, tail; u32 status = 0; int i; - DMA_LOCALS; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DMA_LOCALS; + DRM_DEBUG( "\n" ); /* We need to wait so that we can do an safe flush */ for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { @@ -207,7 +208,7 @@ mga_flush_write_combine(); MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); - DRM_DEBUG( "%s: done.\n", __FUNCTION__ ); + DRM_DEBUG( "done.\n" ); } void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) @@ -215,7 +216,7 @@ drm_mga_primary_buffer_t *primary = &dev_priv->prim; u32 head, tail; DMA_LOCALS; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); BEGIN_DMA_WRAP(); @@ -250,7 +251,7 @@ MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); set_bit( 0, &primary->wrapped ); - DRM_DEBUG( "%s: done.\n", __FUNCTION__ ); + DRM_DEBUG( "done.\n" ); } void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) @@ -258,7 +259,7 @@ drm_mga_primary_buffer_t *primary = &dev_priv->prim; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 head = dev_priv->primary->offset; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); sarea_priv->last_wrap++; DRM_DEBUG( " wrap = %d\n", sarea_priv->last_wrap ); @@ -267,7 +268,7 @@ MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); clear_bit( 0, &primary->wrapped ); - DRM_DEBUG( "%s: done.\n", __FUNCTION__ ); + DRM_DEBUG( "done.\n" ); } @@ -307,8 +308,7 @@ drm_mga_buf_priv_t *buf_priv; drm_mga_freelist_t *entry; int i; - DRM_DEBUG( "%s: count=%d\n", - __FUNCTION__, dma->buf_count ); + DRM_DEBUG( "count=%d\n", dma->buf_count ); dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); @@ -354,7 +354,7 @@ drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *entry; drm_mga_freelist_t *next; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); entry = dev_priv->head; while ( entry ) { @@ -392,7 +392,7 @@ drm_mga_freelist_t *prev; drm_mga_freelist_t *tail = dev_priv->tail; u32 head, wrap; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); head = MGA_READ( MGA_PRIMADDRESS ); wrap = dev_priv->sarea_priv->last_wrap; @@ -424,8 +424,7 @@ drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_freelist_t *head, *entry, *prev; - DRM_DEBUG( "%s: age=0x%06lx wrap=%d\n", - __FUNCTION__, + DRM_DEBUG( "age=0x%06lx wrap=%d\n", buf_priv->list_entry->age.head - dev_priv->primary->offset, buf_priv->list_entry->age.wrap ); @@ -458,9 +457,8 @@ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) { drm_mga_private_t *dev_priv; - struct list_head *list; int ret; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER ); if ( !dev_priv ) @@ -494,15 +492,8 @@ dev_priv->texture_offset = init->texture_offset[0]; dev_priv->texture_size = init->texture_size[0]; - list_for_each( list, &dev->maplist->head ) { - drm_map_list_t *entry = (drm_map_list_t *)list; - if ( entry->map && - entry->map->type == _DRM_SHM && - (entry->map->flags & _DRM_CONTAINS_LOCK) ) { - dev_priv->sarea = entry->map; - break; - } - } + DRM_GETSAREA(); + if(!dev_priv->sarea) { DRM_ERROR( "failed to find sarea!\n" ); /* Assign dev_private so we can do cleanup. */ @@ -626,8 +617,6 @@ dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE; - spin_lock_init( &dev_priv->prim.list_lock ); - dev_priv->prim.status[0] = dev_priv->primary->offset; dev_priv->prim.status[1] = 0; @@ -650,7 +639,7 @@ int mga_do_cleanup_dma( drm_device_t *dev ) { - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); if ( dev->dev_private ) { drm_mga_private_t *dev_priv = dev->dev_private; @@ -725,7 +714,7 @@ #if MGA_DMA_DEBUG int ret = mga_do_wait_for_idle( dev_priv ); if ( ret < 0 ) - DRM_INFO( __FUNCTION__": -EBUSY\n" ); + DRM_INFO( "%s: -EBUSY\n", __FUNCTION__ ); return ret; #else return mga_do_wait_for_idle( dev_priv ); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_drm.h linux.21rc1-ac2/drivers/char/drm/mga_drm.h --- linux.21rc1/drivers/char/drm/mga_drm.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -225,6 +225,20 @@ /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmMga.h) */ + +/* MGA specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x41, drm_lock_t) +#define DRM_IOCTL_MGA_RESET DRM_IO( 0x42) +#define DRM_IOCTL_MGA_SWAP DRM_IO( 0x43) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( 0x44, drm_mga_clear_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( 0x45, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( 0x47, drm_mga_iload_t) +#define DRM_IOCTL_MGA_BLIT DRM_IOW( 0x48, drm_mga_blit_t) + typedef struct _drm_mga_warp_index { int installed; unsigned long phys_addr; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_drv.c linux.21rc1-ac2/drivers/char/drm/mga_drv.c --- linux.21rc1/drivers/char/drm/mga_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -32,37 +32,9 @@ #include #include "mga.h" #include "drmP.h" +#include "drm.h" +#include "mga_drm.h" #include "mga_drv.h" - -#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." - -#define DRIVER_NAME "mga" -#define DRIVER_DESC "Matrox G200/G400" -#define DRIVER_DATE "20010321" - -#define DRIVER_MAJOR 3 -#define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 2 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma_buffers, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)] = { mga_dma_init, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)] = { mga_dma_flush, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_RESET)] = { mga_dma_reset, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)] = { mga_dma_swap, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)] = { mga_dma_clear, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)] = { mga_dma_vertex, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_dma_indices, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)] = { mga_dma_iload, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_MGA_BLIT)] = { mga_dma_blit, 1, 0 }, - - -#define __HAVE_COUNTERS 3 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY - - #include "drm_agpsupport.h" #include "drm_auth.h" #include "drm_bufs.h" @@ -70,27 +42,6 @@ #include "drm_dma.h" #include "drm_drawable.h" #include "drm_drv.h" - -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init mga_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", mga_options ); -#endif - - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_drv.h linux.21rc1-ac2/drivers/char/drm/mga_drv.h --- linux.21rc1/drivers/char/drm/mga_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -46,8 +46,6 @@ u32 last_wrap; u32 high_mark; - - spinlock_t list_lock; } drm_mga_primary_buffer_t; typedef struct drm_mga_freelist { @@ -257,7 +255,7 @@ #define BEGIN_DMA_WRAP() \ do { \ if ( MGA_VERBOSE ) { \ - DRM_INFO( "BEGIN_DMA() in %s\n", __FUNCTION__ ); \ + DRM_INFO( "BEGIN_DMA() in %s\n", __FUNCTION__ ); \ DRM_INFO( " space=0x%x\n", dev_priv->prim.space ); \ } \ prim = dev_priv->prim.start; \ @@ -276,7 +274,7 @@ #define FLUSH_DMA() \ do { \ if ( 0 ) { \ - DRM_INFO( "%s:\n" , __FUNCTION__); \ + DRM_INFO( "%s:\n", __FUNCTION__ ); \ DRM_INFO( " tail=0x%06x head=0x%06lx\n", \ dev_priv->prim.tail, \ MGA_READ( MGA_PRIMADDRESS ) - \ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga.h linux.21rc1-ac2/drivers/char/drm/mga.h --- linux.21rc1/drivers/char/drm/mga.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga.h 2003-04-22 16:44:36.000000000 +0100 @@ -41,6 +41,33 @@ #define __HAVE_MTRR 1 #define __HAVE_CTX_BITMAP 1 +#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." + +#define DRIVER_NAME "mga" +#define DRIVER_DESC "Matrox G200/G400" +#define DRIVER_DATE "20010321" + +#define DRIVER_MAJOR 3 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 2 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma_buffers, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)] = { mga_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)] = { mga_dma_flush, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_RESET)] = { mga_dma_reset, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)] = { mga_dma_swap, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)] = { mga_dma_clear, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)] = { mga_dma_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_dma_indices, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)] = { mga_dma_iload, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_MGA_BLIT)] = { mga_dma_blit, 1, 0 }, + +#define __HAVE_COUNTERS 3 +#define __HAVE_COUNTER6 _DRM_STAT_IRQ +#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY +#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY + /* Driver customization: */ #define DRIVER_PRETAKEDOWN() do { \ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_state.c linux.21rc1-ac2/drivers/char/drm/mga_state.c --- linux.21rc1/drivers/char/drm/mga_state.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_state.c 2003-04-22 16:44:36.000000000 +0100 @@ -34,8 +34,9 @@ #include "mga.h" #include "drmP.h" -#include "mga_drv.h" #include "drm.h" +#include "mga_drm.h" +#include "mga_drv.h" /* ================================================================ @@ -512,7 +513,7 @@ int nbox = sarea_priv->nbox; int i; DMA_LOCALS; - DRM_DEBUG("%s:\n" , __FUNCTION__); + DRM_DEBUG( "\n" ); BEGIN_DMA( 1 ); @@ -606,7 +607,7 @@ int nbox = sarea_priv->nbox; int i; DMA_LOCALS; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); sarea_priv->last_frame.head = dev_priv->prim.tail; sarea_priv->last_frame.wrap = dev_priv->prim.last_wrap; @@ -760,8 +761,7 @@ u32 srcorg = buf->bus_address | MGA_SRCACC_AGP | MGA_SRCMAP_SYSMEM; u32 y2; DMA_LOCALS; - DRM_DEBUG( "%s: buf=%d used=%d\n", - __FUNCTION__, buf->idx, buf->used ); + DRM_DEBUG( "buf=%d used=%d\n", buf->idx, buf->used ); y2 = length / 64; @@ -815,7 +815,7 @@ int nbox = sarea_priv->nbox; u32 scandir = 0, i; DMA_LOCALS; - DRM_DEBUG( "%s:\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); BEGIN_DMA( 4 + nbox ); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/mga_warp.c linux.21rc1-ac2/drivers/char/drm/mga_warp.c --- linux.21rc1/drivers/char/drm/mga_warp.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/mga_warp.c 2003-04-22 16:44:36.000000000 +0100 @@ -27,8 +27,11 @@ * Gareth Hughes */ + #include "mga.h" #include "drmP.h" +#include "drm.h" +#include "mga_drm.h" #include "mga_drv.h" #include "mga_ucode.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128_cce.c linux.21rc1-ac2/drivers/char/drm/r128_cce.c --- linux.21rc1/drivers/char/drm/r128_cce.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128_cce.c 2003-04-22 16:44:36.000000000 +0100 @@ -30,14 +30,14 @@ #include "r128.h" #include "drmP.h" +#include "drm.h" +#include "r128_drm.h" #include "r128_drv.h" - -#include /* For task queue support */ -#include +#include "drm_os_linux.h" +#include #define R128_FIFO_DEBUG 0 - /* CCE microcode (from ATI) */ static u32 r128_cce_microcode[] = { 0, 276838400, 0, 268449792, 2, 142, 2, 145, 0, 1076765731, 0, @@ -83,6 +83,7 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +int r128_do_wait_for_idle( drm_r128_private_t *dev_priv ); int R128_READ_PLL(drm_device_t *dev, int addr) { @@ -131,7 +132,7 @@ } #if R128_FIFO_DEBUG - DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + DRM_ERROR( "failed!\n" ); #endif return -EBUSY; } @@ -147,7 +148,7 @@ } #if R128_FIFO_DEBUG - DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + DRM_ERROR( "failed!\n" ); #endif return -EBUSY; } @@ -157,7 +158,7 @@ int i, ret; ret = r128_do_wait_for_fifo( dev_priv, 64 ); - if ( ret < 0 ) return ret; + if ( ret ) return ret; for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { if ( !(R128_READ( R128_GUI_STAT ) & R128_GUI_ACTIVE) ) { @@ -168,7 +169,7 @@ } #if R128_FIFO_DEBUG - DRM_ERROR( "%s failed!\n", __FUNCTION__ ); + DRM_ERROR( "failed!\n" ); #endif return -EBUSY; } @@ -183,7 +184,7 @@ { int i; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); r128_do_wait_for_idle( dev_priv ); @@ -319,7 +320,7 @@ u32 ring_start; u32 tmp; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); /* The manual (p. 2) says this address is in "VM space". This * means it's an offset from the start of AGP space. @@ -351,8 +352,8 @@ R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR, entry->busaddr[page_ofs]); - DRM_DEBUG( "ring rptr: offset=0x%08llx handle=0x%08lx\n", - (u64)entry->busaddr[page_ofs], + DRM_DEBUG( "ring rptr: offset=0x%08x handle=0x%08lx\n", + entry->busaddr[page_ofs], entry->handle + tmp_ofs ); } @@ -374,9 +375,8 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) { drm_r128_private_t *dev_priv; - struct list_head *list; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); dev_priv = DRM(alloc)( sizeof(drm_r128_private_t), DRM_MEM_DRIVER ); if ( dev_priv == NULL ) @@ -481,15 +481,8 @@ dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) | (dev_priv->span_offset >> 5)); - list_for_each(list, &dev->maplist->head) { - drm_map_list_t *r_list = (drm_map_list_t *)list; - if( r_list->map && - r_list->map->type == _DRM_SHM && - r_list->map->flags & _DRM_CONTAINS_LOCK ) { - dev_priv->sarea = r_list->map; - break; - } - } + DRM_GETSAREA(); + if(!dev_priv->sarea) { DRM_ERROR("could not find sarea!\n"); dev->dev_private = (void *)dev_priv; @@ -622,16 +615,20 @@ if ( dev->dev_private ) { drm_r128_private_t *dev_priv = dev->dev_private; +#if __REALLY_HAVE_SG if ( !dev_priv->is_pci ) { +#endif DRM_IOREMAPFREE( dev_priv->cce_ring ); DRM_IOREMAPFREE( dev_priv->ring_rptr ); DRM_IOREMAPFREE( dev_priv->buffers ); +#if __REALLY_HAVE_SG } else { if (!DRM(ati_pcigart_cleanup)( dev, dev_priv->phys_pci_gart, dev_priv->bus_pci_gart )) DRM_ERROR( "failed to cleanup PCI GART!\n" ); } +#endif DRM(free)( dev->dev_private, sizeof(drm_r128_private_t), DRM_MEM_DRIVER ); @@ -713,7 +710,7 @@ */ if ( stop.idle ) { ret = r128_do_cce_idle( dev_priv ); - if ( ret < 0 ) return ret; + if ( ret ) return ret; } /* Finally, we can turn off the CCE. If the engine isn't idle, @@ -790,7 +787,7 @@ static int r128_do_init_pageflip( drm_device_t *dev ) { drm_r128_private_t *dev_priv = dev->dev_private; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); dev_priv->crtc_offset = R128_READ( R128_CRTC_OFFSET ); dev_priv->crtc_offset_cntl = R128_READ( R128_CRTC_OFFSET_CNTL ); @@ -808,7 +805,7 @@ int r128_do_cleanup_pageflip( drm_device_t *dev ) { drm_r128_private_t *dev_priv = dev->dev_private; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); R128_WRITE( R128_CRTC_OFFSET, dev_priv->crtc_offset ); R128_WRITE( R128_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl ); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128_drm.h linux.21rc1-ac2/drivers/char/drm/r128_drm.h --- linux.21rc1/drivers/char/drm/r128_drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -170,6 +170,27 @@ /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmR128.h) */ + +/* Rage 128 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t) +#define DRM_IOCTL_R128_CCE_START DRM_IO( 0x41) +#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( 0x42, drm_r128_cce_stop_t) +#define DRM_IOCTL_R128_CCE_RESET DRM_IO( 0x43) +#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( 0x44) +#define DRM_IOCTL_R128_RESET DRM_IO( 0x46) +#define DRM_IOCTL_R128_SWAP DRM_IO( 0x47) +#define DRM_IOCTL_R128_CLEAR DRM_IOW( 0x48, drm_r128_clear_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( 0x49, drm_r128_vertex_t) +#define DRM_IOCTL_R128_INDICES DRM_IOW( 0x4a, drm_r128_indices_t) +#define DRM_IOCTL_R128_BLIT DRM_IOW( 0x4b, drm_r128_blit_t) +#define DRM_IOCTL_R128_DEPTH DRM_IOW( 0x4c, drm_r128_depth_t) +#define DRM_IOCTL_R128_STIPPLE DRM_IOW( 0x4d, drm_r128_stipple_t) +#define DRM_IOCTL_R128_INDIRECT DRM_IOWR(0x4f, drm_r128_indirect_t) +#define DRM_IOCTL_R128_FULLSCREEN DRM_IOW( 0x50, drm_r128_fullscreen_t) +#define DRM_IOCTL_R128_CLEAR2 DRM_IOW( 0x51, drm_r128_clear2_t) + typedef struct drm_r128_init { enum { R128_INIT_CCE = 0x01, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128_drv.c linux.21rc1-ac2/drivers/char/drm/r128_drv.c --- linux.21rc1/drivers/char/drm/r128_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -32,48 +32,11 @@ #include #include "r128.h" #include "drmP.h" +#include "drm.h" +#include "r128_drm.h" #include "r128_drv.h" #include "ati_pcigart.h" -#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." - -#define DRIVER_NAME "r128" -#define DRIVER_DESC "ATI Rage 128" -#define DRIVER_DATE "20010917" - -#define DRIVER_MAJOR 2 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 0 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_cce_init, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_START)] = { r128_cce_start, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_STOP)] = { r128_cce_stop, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_RESET)] = { r128_cce_reset, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_IDLE)] = { r128_cce_idle, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)] = { r128_engine_reset, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_FULLSCREEN)] = { r128_fullscreen, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_SWAP)] = { r128_cce_swap, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_CLEAR)] = { r128_cce_clear, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_cce_vertex, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_INDICES)] = { r128_cce_indices, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_BLIT)] = { r128_cce_blit, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_DEPTH)] = { r128_cce_depth, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_STIPPLE)] = { r128_cce_stipple, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_R128_INDIRECT)] = { r128_cce_indirect, 1, 1 }, - - -#if 0 -/* GH: Count data sent to card via ring or vertex/indirect buffers. - */ -#define __HAVE_COUNTERS 3 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY -#endif - - #include "drm_agpsupport.h" #include "drm_auth.h" #include "drm_bufs.h" @@ -81,26 +44,6 @@ #include "drm_dma.h" #include "drm_drawable.h" #include "drm_drv.h" - -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init r128_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", r128_options ); -#endif - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128_drv.h linux.21rc1-ac2/drivers/char/drm/r128_drv.h --- linux.21rc1/drivers/char/drm/r128_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -33,9 +33,10 @@ #ifndef __R128_DRV_H__ #define __R128_DRV_H__ +#include -#define GET_RING_HEAD( ring ) le32_to_cpu( *(ring)->head ) -#define SET_RING_HEAD( ring, val ) *(ring)->head = cpu_to_le32( val ) +#define GET_RING_HEAD(ring) readl( (volatile u32 *) (ring)->head ) +#define SET_RING_HEAD(ring,val) writel( (val), (volatile u32 *) (ring)->head ) typedef struct drm_r128_freelist { unsigned int age; @@ -384,44 +385,11 @@ #define R128_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) #define R128_ADDR(reg) (R128_BASE( reg ) + reg) -#define R128_DEREF(reg) *(volatile u32 *)R128_ADDR( reg ) -#ifdef __alpha__ -#define R128_READ(reg) (_R128_READ((u32 *)R128_ADDR(reg))) -static inline u32 _R128_READ(u32 *addr) -{ - mb(); - return *(volatile u32 *)addr; -} -#define R128_WRITE(reg,val) \ -do { \ - wmb(); \ - R128_DEREF(reg) = val; \ -} while (0) -#else -#define R128_READ(reg) le32_to_cpu( R128_DEREF( reg ) ) -#define R128_WRITE(reg,val) \ -do { \ - R128_DEREF( reg ) = cpu_to_le32( val ); \ -} while (0) -#endif +#define R128_READ(reg) readl( (volatile u32 *) R128_ADDR(reg) ) +#define R128_WRITE(reg,val) writel( (val) , (volatile u32 *) R128_ADDR(reg)) -#define R128_DEREF8(reg) *(volatile u8 *)R128_ADDR( reg ) -#ifdef __alpha__ -#define R128_READ8(reg) _R128_READ8((u8 *)R128_ADDR(reg)) -static inline u8 _R128_READ8(u8 *addr) -{ - mb(); - return *(volatile u8 *)addr; -} -#define R128_WRITE8(reg,val) \ -do { \ - wmb(); \ - R128_DEREF8(reg) = val; \ -} while (0) -#else -#define R128_READ8(reg) R128_DEREF8( reg ) -#define R128_WRITE8(reg,val) do { R128_DEREF8( reg ) = val; } while (0) -#endif +#define R128_READ8(reg) readb( (volatile u8 *) R128_ADDR(reg) ) +#define R128_WRITE8(reg,val) writeb( (val), (volatile u8 *) R128_ADDR(reg) ) #define R128_WRITE_PLL(addr,val) \ do { \ @@ -470,6 +438,7 @@ return -EBUSY; \ } \ __ring_space_done: ; \ + break; \ } while (0) #define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ @@ -493,7 +462,11 @@ * Ring control */ +#if defined(__powerpc__) +#define r128_flush_write_combine() (void) GET_RING_HEAD( &dev_priv->ring ) +#else #define r128_flush_write_combine() mb() +#endif #define R128_VERBOSE 0 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128.h linux.21rc1-ac2/drivers/char/drm/r128.h --- linux.21rc1/drivers/char/drm/r128.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128.h 2003-04-22 16:44:36.000000000 +0100 @@ -43,6 +43,35 @@ #define __HAVE_SG 1 #define __HAVE_PCI_DMA 1 +#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." + +#define DRIVER_NAME "r128" +#define DRIVER_DESC "ATI Rage 128" +#define DRIVER_DATE "20010917" + +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 2 +#define DRIVER_PATCHLEVEL 0 + + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_cce_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_START)] = { r128_cce_start, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_STOP)] = { r128_cce_stop, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_RESET)] = { r128_cce_reset, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_IDLE)] = { r128_cce_idle, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)] = { r128_engine_reset, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_FULLSCREEN)] = { r128_fullscreen, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_SWAP)] = { r128_cce_swap, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_CLEAR)] = { r128_cce_clear, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_cce_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_INDICES)] = { r128_cce_indices, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_BLIT)] = { r128_cce_blit, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_DEPTH)] = { r128_cce_depth, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_STIPPLE)] = { r128_cce_stipple, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_R128_INDIRECT)] = { r128_cce_indirect, 1, 1 }, + /* Driver customization: */ #define DRIVER_PRERELEASE() do { \ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/r128_state.c linux.21rc1-ac2/drivers/char/drm/r128_state.c --- linux.21rc1/drivers/char/drm/r128_state.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/r128_state.c 2003-04-22 16:44:36.000000000 +0100 @@ -29,9 +29,9 @@ #include "r128.h" #include "drmP.h" -#include "r128_drv.h" #include "drm.h" -#include +#include "r128_drm.h" +#include "r128_drv.h" /* ================================================================ @@ -528,7 +528,7 @@ { drm_r128_private_t *dev_priv = dev->dev_private; RING_LOCALS; - DRM_DEBUG( "%s: page=%d\n", __FUNCTION__, dev_priv->current_page ); + DRM_DEBUG( "page=%d\n", dev_priv->current_page ); #if R128_PERFORMANCE_BOXES /* Do some trivial performance monitoring... @@ -577,8 +577,7 @@ int prim = buf_priv->prim; int i = 0; RING_LOCALS; - DRM_DEBUG( "%s: buf=%d nbox=%d\n", - __FUNCTION__, buf->idx, sarea_priv->nbox ); + DRM_DEBUG( "buf=%d nbox=%d\n", buf->idx, sarea_priv->nbox ); if ( 0 ) r128_print_dirty( "dispatch_vertex", sarea_priv->dirty ); @@ -789,7 +788,7 @@ u32 *data; int dword_shift, dwords; RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); /* The compiler won't optimize away a division by a variable, * even if the only legal values are powers of two. Thus, we'll diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_cp.c linux.21rc1-ac2/drivers/char/drm/radeon_cp.c --- linux.21rc1/drivers/char/drm/radeon_cp.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_cp.c 2003-04-22 16:44:36.000000000 +0100 @@ -30,21 +30,278 @@ #include "radeon.h" #include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" #include "radeon_drv.h" +#include "drm_os_linux.h" #include /* For task queue support */ #include - #define RADEON_FIFO_DEBUG 0 -#if defined(__alpha__) -# define PCIGART_ENABLED -#else -# undef PCIGART_ENABLED -#endif /* CP microcode (from ATI) */ +static u32 R200_cp_microcode[][2] = { + { 0x21007000, 0000000000 }, + { 0x20007000, 0000000000 }, + { 0x000000ab, 0x00000004 }, + { 0x000000af, 0x00000004 }, + { 0x66544a49, 0000000000 }, + { 0x49494174, 0000000000 }, + { 0x54517d83, 0000000000 }, + { 0x498d8b64, 0000000000 }, + { 0x49494949, 0000000000 }, + { 0x49da493c, 0000000000 }, + { 0x49989898, 0000000000 }, + { 0xd34949d5, 0000000000 }, + { 0x9dc90e11, 0000000000 }, + { 0xce9b9b9b, 0000000000 }, + { 0x000f0000, 0x00000016 }, + { 0x352e232c, 0000000000 }, + { 0x00000013, 0x00000004 }, + { 0x000f0000, 0x00000016 }, + { 0x352e272c, 0000000000 }, + { 0x000f0001, 0x00000016 }, + { 0x3239362f, 0000000000 }, + { 0x000077ef, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00061000, 0x00000002 }, + { 0x00000020, 0x0000001a }, + { 0x00004000, 0x0000001e }, + { 0x00000016, 0x00000004 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000002 }, + { 0x00000016, 0x00000004 }, + { 0x000077e0, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x000037e1, 0x00000002 }, + { 0x040067e1, 0x00000006 }, + { 0x000077e0, 0x00000002 }, + { 0x000077e1, 0x00000002 }, + { 0x000077e1, 0x00000006 }, + { 0xffffffff, 0000000000 }, + { 0x10000000, 0000000000 }, + { 0x0003802a, 0x00000002 }, + { 0x040067e0, 0x00000006 }, + { 0x00007675, 0x00000002 }, + { 0x00007676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0003802b, 0x00000002 }, + { 0x04002676, 0x00000002 }, + { 0x00007677, 0x00000002 }, + { 0x00007678, 0x00000006 }, + { 0x0000002e, 0x00000018 }, + { 0x0000002e, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x0000002f, 0x00000018 }, + { 0x0000002f, 0x00000018 }, + { 0000000000, 0x00000006 }, + { 0x01605000, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00098000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x64c0603d, 0x00000004 }, + { 0x00080000, 0x00000016 }, + { 0000000000, 0000000000 }, + { 0x0400251d, 0x00000002 }, + { 0x00007580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x04002580, 0x00000002 }, + { 0x00067581, 0x00000002 }, + { 0x00000046, 0x00000004 }, + { 0x00005000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x00019000, 0x00000002 }, + { 0x00011055, 0x00000014 }, + { 0x00000055, 0x00000012 }, + { 0x0400250f, 0x00000002 }, + { 0x0000504a, 0x00000004 }, + { 0x00007565, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000051, 0x00000004 }, + { 0x01e655b4, 0x00000002 }, + { 0x4401b0dc, 0x00000002 }, + { 0x01c110dc, 0x00000002 }, + { 0x2666705d, 0x00000018 }, + { 0x040c2565, 0x00000002 }, + { 0x0000005d, 0x00000018 }, + { 0x04002564, 0x00000002 }, + { 0x00007566, 0x00000002 }, + { 0x00000054, 0x00000004 }, + { 0x00401060, 0x00000008 }, + { 0x00101000, 0x00000002 }, + { 0x000d80ff, 0x00000002 }, + { 0x00800063, 0x00000008 }, + { 0x000f9000, 0x00000002 }, + { 0x000e00ff, 0x00000002 }, + { 0000000000, 0x00000006 }, + { 0x00000080, 0x00000018 }, + { 0x00000054, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00065000, 0x00000002 }, + { 0x00009000, 0x00000002 }, + { 0x00041000, 0x00000002 }, + { 0x0c00350e, 0x00000002 }, + { 0x00049000, 0x00000002 }, + { 0x00051000, 0x00000002 }, + { 0x01e785f8, 0x00000002 }, + { 0x00200000, 0x00000002 }, + { 0x00600073, 0x0000000c }, + { 0x00007563, 0x00000002 }, + { 0x006075f0, 0x00000021 }, + { 0x20007068, 0x00000004 }, + { 0x00005068, 0x00000004 }, + { 0x00007576, 0x00000002 }, + { 0x00007577, 0x00000002 }, + { 0x0000750e, 0x00000002 }, + { 0x0000750f, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00600076, 0x0000000c }, + { 0x006075f0, 0x00000021 }, + { 0x000075f8, 0x00000002 }, + { 0x00000076, 0x00000004 }, + { 0x000a750e, 0x00000002 }, + { 0x0020750f, 0x00000002 }, + { 0x00600079, 0x00000004 }, + { 0x00007570, 0x00000002 }, + { 0x00007571, 0x00000002 }, + { 0x00007572, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00007568, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x00000084, 0x0000000c }, + { 0x00058000, 0x00000002 }, + { 0x0c607562, 0x00000002 }, + { 0x00000086, 0x00000004 }, + { 0x00600085, 0x00000004 }, + { 0x400070dd, 0000000000 }, + { 0x000380dd, 0x00000002 }, + { 0x00000093, 0x0000001c }, + { 0x00065095, 0x00000018 }, + { 0x040025bb, 0x00000002 }, + { 0x00061096, 0x00000018 }, + { 0x040075bc, 0000000000 }, + { 0x000075bb, 0x00000002 }, + { 0x000075bc, 0000000000 }, + { 0x00090000, 0x00000006 }, + { 0x00090000, 0x00000002 }, + { 0x000d8002, 0x00000006 }, + { 0x00005000, 0x00000002 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x00007821, 0x00000002 }, + { 0x00007800, 0000000000 }, + { 0x01665000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x000671cc, 0x00000002 }, + { 0x0286f1cd, 0x00000002 }, + { 0x000000a3, 0x00000010 }, + { 0x21007000, 0000000000 }, + { 0x000000aa, 0x0000001c }, + { 0x00065000, 0x00000002 }, + { 0x000a0000, 0x00000002 }, + { 0x00061000, 0x00000002 }, + { 0x000b0000, 0x00000002 }, + { 0x38067000, 0x00000002 }, + { 0x000a00a6, 0x00000004 }, + { 0x20007000, 0000000000 }, + { 0x01200000, 0x00000002 }, + { 0x20077000, 0x00000002 }, + { 0x01200000, 0x00000002 }, + { 0x20007000, 0000000000 }, + { 0x00061000, 0x00000002 }, + { 0x0120751b, 0x00000002 }, + { 0x8040750a, 0x00000002 }, + { 0x8040750b, 0x00000002 }, + { 0x00110000, 0x00000002 }, + { 0x000380dd, 0x00000002 }, + { 0x000000bd, 0x0000001c }, + { 0x00061096, 0x00000018 }, + { 0x844075bd, 0x00000002 }, + { 0x00061095, 0x00000018 }, + { 0x840075bb, 0x00000002 }, + { 0x00061096, 0x00000018 }, + { 0x844075bc, 0x00000002 }, + { 0x000000c0, 0x00000004 }, + { 0x804075bd, 0x00000002 }, + { 0x800075bb, 0x00000002 }, + { 0x804075bc, 0x00000002 }, + { 0x00108000, 0x00000002 }, + { 0x01400000, 0x00000002 }, + { 0x006000c4, 0x0000000c }, + { 0x20c07000, 0x00000020 }, + { 0x000000c6, 0x00000012 }, + { 0x00800000, 0x00000006 }, + { 0x0080751d, 0x00000006 }, + { 0x000025bb, 0x00000002 }, + { 0x000040c0, 0x00000004 }, + { 0x0000775c, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460275d, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x00007999, 0x00000002 }, + { 0x00a05000, 0x00000002 }, + { 0x00661000, 0x00000002 }, + { 0x0460299b, 0x00000020 }, + { 0x00004000, 0000000000 }, + { 0x01e00830, 0x00000002 }, + { 0x21007000, 0000000000 }, + { 0x00005000, 0x00000002 }, + { 0x00038042, 0x00000002 }, + { 0x040025e0, 0x00000002 }, + { 0x000075e1, 0000000000 }, + { 0x00000001, 0000000000 }, + { 0x000380d9, 0x00000002 }, + { 0x04007394, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, + { 0000000000, 0000000000 }, +}; + + static u32 radeon_cp_microcode[][2] = { { 0x21007000, 0000000000 }, { 0x20007000, 0000000000 }, @@ -346,6 +603,8 @@ u32 tmp; int i; + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + tmp = RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT ); tmp |= RADEON_RB2D_DC_FLUSH_ALL; RADEON_WRITE( RADEON_RB2D_DSTCACHE_CTLSTAT, tmp ); @@ -370,6 +629,8 @@ { int i; + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { int slots = ( RADEON_READ( RADEON_RBBM_STATUS ) & RADEON_RBBM_FIFOCNT_MASK ); @@ -388,8 +649,10 @@ { int i, ret; + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + ret = radeon_do_wait_for_fifo( dev_priv, 64 ); - if ( ret < 0 ) return ret; + if ( ret ) return ret; for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { if ( !(RADEON_READ( RADEON_RBBM_STATUS ) @@ -416,16 +679,31 @@ static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv ) { int i; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); radeon_do_wait_for_idle( dev_priv ); RADEON_WRITE( RADEON_CP_ME_RAM_ADDR, 0 ); - for ( i = 0 ; i < 256 ; i++ ) { - RADEON_WRITE( RADEON_CP_ME_RAM_DATAH, - radeon_cp_microcode[i][1] ); - RADEON_WRITE( RADEON_CP_ME_RAM_DATAL, - radeon_cp_microcode[i][0] ); + + if (dev_priv->is_r200) + { + DRM_INFO("Loading R200 Microcode\n"); + for ( i = 0 ; i < 256 ; i++ ) + { + RADEON_WRITE( RADEON_CP_ME_RAM_DATAH, + R200_cp_microcode[i][1] ); + RADEON_WRITE( RADEON_CP_ME_RAM_DATAL, + R200_cp_microcode[i][0] ); + } + } + else + { + for ( i = 0 ; i < 256 ; i++ ) { + RADEON_WRITE( RADEON_CP_ME_RAM_DATAH, + radeon_cp_microcode[i][1] ); + RADEON_WRITE( RADEON_CP_ME_RAM_DATAL, + radeon_cp_microcode[i][0] ); + } } } @@ -435,7 +713,7 @@ */ static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv ) { - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); #if 0 u32 tmp; @@ -449,7 +727,7 @@ int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ) { RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); BEGIN_RING( 6 ); @@ -458,6 +736,7 @@ RADEON_WAIT_UNTIL_IDLE(); ADVANCE_RING(); + COMMIT_RING(); return radeon_do_wait_for_idle( dev_priv ); } @@ -467,7 +746,7 @@ static void radeon_do_cp_start( drm_radeon_private_t *dev_priv ) { RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); radeon_do_wait_for_idle( dev_priv ); @@ -482,6 +761,7 @@ RADEON_WAIT_UNTIL_IDLE(); ADVANCE_RING(); + COMMIT_RING(); } /* Reset the Command Processor. This will not flush any pending @@ -491,7 +771,7 @@ static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv ) { u32 cur_read_ptr; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR ); RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr ); @@ -505,7 +785,7 @@ */ static void radeon_do_cp_stop( drm_radeon_private_t *dev_priv ) { - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); RADEON_WRITE( RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS ); @@ -518,7 +798,7 @@ { drm_radeon_private_t *dev_priv = dev->dev_private; u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); radeon_do_pixcache_flush( dev_priv ); @@ -603,6 +883,7 @@ /* Set the write pointer delay */ RADEON_WRITE( RADEON_CP_RB_WPTR_DELAY, 0 ); + RADEON_READ( RADEON_CP_RB_WPTR_DELAY ); /* read back to propagate */ /* Initialize the ring buffer's read and write pointers */ cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR ); @@ -622,13 +903,63 @@ RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, entry->busaddr[page_ofs]); - DRM_DEBUG( "ring rptr: offset=0x%08llx handle=0x%08lx\n", - (u64)entry->busaddr[page_ofs], + DRM_DEBUG( "ring rptr: offset=0x%08x handle=0x%08lx\n", + entry->busaddr[page_ofs], entry->handle + tmp_ofs ); } + /* Initialize the scratch register pointer. This will cause + * the scratch register values to be written out to memory + * whenever they are updated. + * + * We simply put this behind the ring read pointer, this works + * with PCI GART as well as (whatever kind of) AGP GART + */ + RADEON_WRITE( RADEON_SCRATCH_ADDR, RADEON_READ( RADEON_CP_RB_RPTR_ADDR ) + + RADEON_SCRATCH_REG_OFFSET ); + + dev_priv->scratch = ((__volatile__ u32 *) + dev_priv->ring.head + + (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); + + RADEON_WRITE( RADEON_SCRATCH_UMSK, 0x7 ); + + /* Writeback doesn't seem to work everywhere, test it first */ + writel(0, &dev_priv->scratch[1]); + RADEON_WRITE( RADEON_SCRATCH_REG1, 0xdeadbeef ); + + for ( tmp = 0 ; tmp < dev_priv->usec_timeout ; tmp++ ) { + if ( readl( &dev_priv->scratch[1] ) == 0xdeadbeef ) + break; + udelay(1); + } + + if ( tmp < dev_priv->usec_timeout ) { + dev_priv->writeback_works = 1; + DRM_DEBUG( "writeback test succeeded, tmp=%d\n", tmp ); + } else { + dev_priv->writeback_works = 0; + DRM_DEBUG( "writeback test failed\n" ); + } + + dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0; + RADEON_WRITE( RADEON_LAST_FRAME_REG, + dev_priv->sarea_priv->last_frame ); + + dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0; + RADEON_WRITE( RADEON_LAST_DISPATCH_REG, + dev_priv->sarea_priv->last_dispatch ); + + dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0; + RADEON_WRITE( RADEON_LAST_CLEAR_REG, + dev_priv->sarea_priv->last_clear ); + /* Set ring buffer size */ +#ifdef __BIG_ENDIAN + RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT ); +#else RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw ); +#endif radeon_do_wait_for_idle( dev_priv ); @@ -647,9 +978,8 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) { drm_radeon_private_t *dev_priv; - struct list_head *list; u32 tmp; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); dev_priv = DRM(alloc)( sizeof(drm_radeon_private_t), DRM_MEM_DRIVER ); if ( dev_priv == NULL ) @@ -659,17 +989,6 @@ dev_priv->is_pci = init->is_pci; -#if !defined(PCIGART_ENABLED) - /* PCI support is not 100% working, so we disable it here. - */ - if ( dev_priv->is_pci ) { - DRM_ERROR( "PCI GART not yet supported for Radeon!\n" ); - dev->dev_private = (void *)dev_priv; - radeon_do_cleanup_cp(dev); - return -EINVAL; - } -#endif - if ( dev_priv->is_pci && !dev->sg ) { DRM_ERROR( "PCI GART memory not allocated!\n" ); dev->dev_private = (void *)dev_priv; @@ -686,12 +1005,10 @@ return -EINVAL; } + dev_priv->is_r200 = (init->func == RADEON_INIT_R200_CP); + dev_priv->do_boxes = 0; dev_priv->cp_mode = init->cp_mode; - /* Simple idle check. - */ - atomic_set( &dev_priv->idle_count, 0 ); - /* We don't support anything other than bus-mastering ring mode, * but the ring can be in either AGP or PCI space for the ring * read pointer. @@ -743,17 +1060,17 @@ * and screwing with the clear operation. */ dev_priv->depth_clear.rb3d_cntl = (RADEON_PLANE_MASK_ENABLE | - RADEON_Z_ENABLE | (dev_priv->color_fmt << 10) | - RADEON_ZBLOCK16); + (1<<15)); - dev_priv->depth_clear.rb3d_zstencilcntl = (dev_priv->depth_fmt | - RADEON_Z_TEST_ALWAYS | - RADEON_STENCIL_TEST_ALWAYS | - RADEON_STENCIL_S_FAIL_KEEP | - RADEON_STENCIL_ZPASS_KEEP | - RADEON_STENCIL_ZFAIL_KEEP | - RADEON_Z_WRITE_ENABLE); + dev_priv->depth_clear.rb3d_zstencilcntl = + (dev_priv->depth_fmt | + RADEON_Z_TEST_ALWAYS | + RADEON_STENCIL_TEST_ALWAYS | + RADEON_STENCIL_S_FAIL_REPLACE | + RADEON_STENCIL_ZPASS_REPLACE | + RADEON_STENCIL_ZFAIL_REPLACE | + RADEON_Z_WRITE_ENABLE); dev_priv->depth_clear.se_cntl = (RADEON_FFACE_CULL_CW | RADEON_BFACE_SOLID | @@ -767,15 +1084,8 @@ RADEON_ROUND_MODE_TRUNC | RADEON_ROUND_PREC_8TH_PIX); - list_for_each(list, &dev->maplist->head) { - drm_map_list_t *r_list = (drm_map_list_t *)list; - if( r_list->map && - r_list->map->type == _DRM_SHM && - r_list->map->flags & _DRM_CONTAINS_LOCK ) { - dev_priv->sarea = r_list->map; - break; - } - } + DRM_GETSAREA(); + if(!dev_priv->sarea) { DRM_ERROR("could not find sarea!\n"); dev->dev_private = (void *)dev_priv; @@ -896,34 +1206,7 @@ dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK; -#if 0 - /* Initialize the scratch register pointer. This will cause - * the scratch register values to be written out to memory - * whenever they are updated. - * FIXME: This doesn't quite work yet, so we're disabling it - * for the release. - */ - RADEON_WRITE( RADEON_SCRATCH_ADDR, (dev_priv->ring_rptr->offset + - RADEON_SCRATCH_REG_OFFSET) ); - RADEON_WRITE( RADEON_SCRATCH_UMSK, 0x7 ); -#endif - - dev_priv->scratch = ((__volatile__ u32 *) - dev_priv->ring_rptr->handle + - (RADEON_SCRATCH_REG_OFFSET / sizeof(u32))); - - dev_priv->sarea_priv->last_frame = 0; - RADEON_WRITE( RADEON_LAST_FRAME_REG, - dev_priv->sarea_priv->last_frame ); - - dev_priv->sarea_priv->last_dispatch = 0; - RADEON_WRITE( RADEON_LAST_DISPATCH_REG, - dev_priv->sarea_priv->last_dispatch ); - - dev_priv->sarea_priv->last_clear = 0; - RADEON_WRITE( RADEON_LAST_CLEAR_REG, - dev_priv->sarea_priv->last_clear ); - +#if __REALLY_HAVE_SG if ( dev_priv->is_pci ) { if (!DRM(ati_pcigart_init)( dev, &dev_priv->phys_pci_gart, &dev_priv->bus_pci_gart)) { @@ -953,19 +1236,20 @@ RADEON_WRITE( RADEON_MC_AGP_LOCATION, 0xffffffc0 ); /* ?? */ RADEON_WRITE( RADEON_AGP_COMMAND, 0 ); /* clear AGP_COMMAND */ } else { +#endif /* __REALLY_HAVE_SG */ /* Turn off PCI GART */ tmp = RADEON_READ( RADEON_AIC_CNTL ) & ~RADEON_PCIGART_TRANSLATE_EN; RADEON_WRITE( RADEON_AIC_CNTL, tmp ); +#if __REALLY_HAVE_SG } +#endif /* __REALLY_HAVE_SG */ radeon_cp_load_microcode( dev_priv ); radeon_cp_init_ring_buffer( dev, dev_priv ); -#if ROTATE_BUFS dev_priv->last_buf = 0; -#endif dev->dev_private = (void *)dev_priv; @@ -976,7 +1260,7 @@ int radeon_do_cleanup_cp( drm_device_t *dev ) { - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); if ( dev->dev_private ) { drm_radeon_private_t *dev_priv = dev->dev_private; @@ -986,10 +1270,12 @@ DRM_IOREMAPFREE( dev_priv->ring_rptr ); DRM_IOREMAPFREE( dev_priv->buffers ); } else { +#if __REALLY_HAVE_SG if (!DRM(ati_pcigart_cleanup)( dev, dev_priv->phys_pci_gart, dev_priv->bus_pci_gart )) DRM_ERROR( "failed to cleanup PCI GART!\n" ); +#endif /* __REALLY_HAVE_SG */ } DRM(free)( dev->dev_private, sizeof(drm_radeon_private_t), @@ -1012,6 +1298,7 @@ switch ( init.func ) { case RADEON_INIT_CP: + case RADEON_INIT_R200_CP: return radeon_do_init_cp( dev, &init ); case RADEON_CLEANUP_CP: return radeon_do_cleanup_cp( dev ); @@ -1075,7 +1362,7 @@ */ if ( stop.idle ) { ret = radeon_do_cp_idle( dev_priv ); - if ( ret < 0 ) return ret; + if ( ret ) return ret; } /* Finally, we can turn off the CP. If the engine isn't idle, @@ -1145,117 +1432,74 @@ * Fullscreen mode */ -static int radeon_do_init_pageflip( drm_device_t *dev ) -{ - drm_radeon_private_t *dev_priv = dev->dev_private; - DRM_DEBUG( "%s\n", __FUNCTION__ ); - - dev_priv->crtc_offset = RADEON_READ( RADEON_CRTC_OFFSET ); - dev_priv->crtc_offset_cntl = RADEON_READ( RADEON_CRTC_OFFSET_CNTL ); - - RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->front_offset ); - RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, - dev_priv->crtc_offset_cntl | - RADEON_CRTC_OFFSET_FLIP_CNTL ); - - dev_priv->page_flipping = 1; - dev_priv->current_page = 0; - - return 0; -} - -int radeon_do_cleanup_pageflip( drm_device_t *dev ) +/* KW: Deprecated to say the least: + */ +int radeon_fullscreen(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) { - drm_radeon_private_t *dev_priv = dev->dev_private; - DRM_DEBUG( "%s\n", __FUNCTION__ ); - - RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->crtc_offset ); - RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl ); - - dev_priv->page_flipping = 0; - dev_priv->current_page = 0; - return 0; } -int radeon_fullscreen( struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg ) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - drm_radeon_fullscreen_t fs; - - LOCK_TEST_WITH_RETURN( dev ); - - if ( copy_from_user( &fs, (drm_radeon_fullscreen_t *)arg, - sizeof(fs) ) ) - return -EFAULT; - - switch ( fs.func ) { - case RADEON_INIT_FULLSCREEN: - return radeon_do_init_pageflip( dev ); - case RADEON_CLEANUP_FULLSCREEN: - return radeon_do_cleanup_pageflip( dev ); - } - - return -EINVAL; -} - /* ================================================================ * Freelist management */ -#define RADEON_BUFFER_USED 0xffffffff -#define RADEON_BUFFER_FREE 0 -#if 0 -static int radeon_freelist_init( drm_device_t *dev ) +/* Original comment: FIXME: ROTATE_BUFS is a hack to cycle through + * bufs until freelist code is used. Note this hides a problem with + * the scratch register * (used to keep track of last buffer + * completed) being written to before * the last buffer has actually + * completed rendering. + * + * KW: It's also a good way to find free buffers quickly. + * + * KW: Ideally this loop wouldn't exist, and freelist_get wouldn't + * sleep. However, bugs in older versions of radeon_accel.c mean that + * we essentially have to do this, else old clients will break. + * + * However, it does leave open a potential deadlock where all the + * buffers are held by other clients, which can't release them because + * they can't get the lock. + */ + +drm_buf_t *radeon_freelist_get( drm_device_t *dev ) { drm_device_dma_t *dma = dev->dma; drm_radeon_private_t *dev_priv = dev->dev_private; - drm_buf_t *buf; drm_radeon_buf_priv_t *buf_priv; - drm_radeon_freelist_t *entry; - int i; - - dev_priv->head = DRM(alloc)( sizeof(drm_radeon_freelist_t), - DRM_MEM_DRIVER ); - if ( dev_priv->head == NULL ) - return -ENOMEM; - - memset( dev_priv->head, 0, sizeof(drm_radeon_freelist_t) ); - dev_priv->head->age = RADEON_BUFFER_USED; + drm_buf_t *buf; + int i, t; + int start; - for ( i = 0 ; i < dma->buf_count ; i++ ) { - buf = dma->buflist[i]; - buf_priv = buf->dev_private; + if ( ++dev_priv->last_buf >= dma->buf_count ) + dev_priv->last_buf = 0; - entry = DRM(alloc)( sizeof(drm_radeon_freelist_t), - DRM_MEM_DRIVER ); - if ( !entry ) return -ENOMEM; - - entry->age = RADEON_BUFFER_FREE; - entry->buf = buf; - entry->prev = dev_priv->head; - entry->next = dev_priv->head->next; - if ( !entry->next ) - dev_priv->tail = entry; - - buf_priv->discard = 0; - buf_priv->dispatched = 0; - buf_priv->list_entry = entry; + start = dev_priv->last_buf; - dev_priv->head->next = entry; + for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) { + u32 done_age = GET_SCRATCH( 1 ); + DRM_DEBUG("done_age = %d\n",done_age); + for ( i = start ; i < dma->buf_count ; i++ ) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if ( buf->pid == 0 || (buf->pending && + buf_priv->age <= done_age) ) { + dev_priv->stats.requested_bufs++; + buf->pending = 0; + return buf; + } + start = 0; + } - if ( dev_priv->head->next ) - dev_priv->head->next->prev = entry; + if (t) { + udelay(1); + dev_priv->stats.freelist_loops++; + } } - return 0; - + DRM_DEBUG( "returning NULL!\n" ); + return NULL; } -#endif - +#if 0 drm_buf_t *radeon_freelist_get( drm_device_t *dev ) { drm_device_dma_t *dma = dev->dma; @@ -1263,76 +1507,40 @@ drm_radeon_buf_priv_t *buf_priv; drm_buf_t *buf; int i, t; -#if ROTATE_BUFS int start; -#endif - - /* FIXME: Optimize -- use freelist code */ + u32 done_age = readl(&dev_priv->scratch[1]); - for ( i = 0 ; i < dma->buf_count ; i++ ) { - buf = dma->buflist[i]; - buf_priv = buf->dev_private; - if ( buf->pid == 0 ) { - DRM_DEBUG( " ret buf=%d last=%d pid=0\n", - buf->idx, dev_priv->last_buf ); - return buf; - } - DRM_DEBUG( " skipping buf=%d pid=%d\n", - buf->idx, buf->pid ); - } - -#if ROTATE_BUFS if ( ++dev_priv->last_buf >= dma->buf_count ) dev_priv->last_buf = 0; + start = dev_priv->last_buf; -#endif - for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) { -#if 0 - /* FIXME: Disable this for now */ - u32 done_age = dev_priv->scratch[RADEON_LAST_DISPATCH]; -#else - u32 done_age = RADEON_READ( RADEON_LAST_DISPATCH_REG ); -#endif -#if ROTATE_BUFS + dev_priv->stats.freelist_loops++; + + for ( t = 0 ; t < 2 ; t++ ) { for ( i = start ; i < dma->buf_count ; i++ ) { -#else - for ( i = 0 ; i < dma->buf_count ; i++ ) { -#endif buf = dma->buflist[i]; buf_priv = buf->dev_private; - if ( buf->pending && buf_priv->age <= done_age ) { - /* The buffer has been processed, so it - * can now be used. - */ + if ( buf->pid == 0 || (buf->pending && + buf_priv->age <= done_age) ) { + dev_priv->stats.requested_bufs++; buf->pending = 0; - DRM_DEBUG( " ret buf=%d last=%d age=%d done=%d\n", buf->idx, dev_priv->last_buf, buf_priv->age, done_age ); return buf; } - DRM_DEBUG( " skipping buf=%d age=%d done=%d\n", - buf->idx, buf_priv->age, - done_age ); -#if ROTATE_BUFS - start = 0; -#endif } - udelay( 1 ); + start = 0; } - DRM_ERROR( "returning NULL!\n" ); return NULL; } +#endif void radeon_freelist_reset( drm_device_t *dev ) { drm_device_dma_t *dma = dev->dma; -#if ROTATE_BUFS drm_radeon_private_t *dev_priv = dev->dev_private; -#endif int i; -#if ROTATE_BUFS dev_priv->last_buf = 0; -#endif for ( i = 0 ; i < dma->buf_count ; i++ ) { drm_buf_t *buf = dma->buflist[i]; drm_radeon_buf_priv_t *buf_priv = buf->dev_private; @@ -1349,11 +1557,23 @@ { drm_radeon_ring_buffer_t *ring = &dev_priv->ring; int i; + u32 last_head = GET_RING_HEAD(ring); for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { - radeon_update_ring_snapshot( ring ); + u32 head = GET_RING_HEAD(ring); + + ring->space = (head - ring->tail) * sizeof(u32); + if ( ring->space <= 0 ) + ring->space += ring->size; if ( ring->space > n ) return 0; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + if (head != last_head) + i = 0; + last_head = head; + udelay( 1 ); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_drm.h linux.21rc1-ac2/drivers/char/drm/radeon_drm.h --- linux.21rc1/drivers/char/drm/radeon_drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -2,6 +2,7 @@ * * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -26,6 +27,7 @@ * Authors: * Kevin E. Martin * Gareth Hughes + * Keith Whitwell */ #ifndef __RADEON_DRM_H__ @@ -37,7 +39,8 @@ #ifndef __RADEON_SAREA_DEFINES__ #define __RADEON_SAREA_DEFINES__ -/* What needs to be changed for the current vertex buffer? +/* Old style state flags, required for sarea interface (1.1 and 1.2 + * clears) and 1.2 drm_vertex2 ioctl. */ #define RADEON_UPLOAD_CONTEXT 0x00000001 #define RADEON_UPLOAD_VERTFMT 0x00000002 @@ -56,11 +59,136 @@ #define RADEON_UPLOAD_TEX2IMAGES 0x00004000 #define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ #define RADEON_REQUIRE_QUIESCENCE 0x00010000 -#define RADEON_UPLOAD_ALL 0x0001ffff +#define RADEON_UPLOAD_ZBIAS 0x00020000 /* version 1.2 and newer */ +#define RADEON_UPLOAD_ALL 0x003effff +#define RADEON_UPLOAD_CONTEXT_ALL 0x003e01ff + + +/* New style per-packet identifiers for use in cmd_buffer ioctl with + * the RADEON_EMIT_PACKET command. Comments relate new packets to old + * state bits and the packet size: + */ +#define RADEON_EMIT_PP_MISC 0 /* context/7 */ +#define RADEON_EMIT_PP_CNTL 1 /* context/3 */ +#define RADEON_EMIT_RB3D_COLORPITCH 2 /* context/1 */ +#define RADEON_EMIT_RE_LINE_PATTERN 3 /* line/2 */ +#define RADEON_EMIT_SE_LINE_WIDTH 4 /* line/1 */ +#define RADEON_EMIT_PP_LUM_MATRIX 5 /* bumpmap/1 */ +#define RADEON_EMIT_PP_ROT_MATRIX_0 6 /* bumpmap/2 */ +#define RADEON_EMIT_RB3D_STENCILREFMASK 7 /* masks/3 */ +#define RADEON_EMIT_SE_VPORT_XSCALE 8 /* viewport/6 */ +#define RADEON_EMIT_SE_CNTL 9 /* setup/2 */ +#define RADEON_EMIT_SE_CNTL_STATUS 10 /* setup/1 */ +#define RADEON_EMIT_RE_MISC 11 /* misc/1 */ +#define RADEON_EMIT_PP_TXFILTER_0 12 /* tex0/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_0 13 /* tex0/1 */ +#define RADEON_EMIT_PP_TXFILTER_1 14 /* tex1/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_1 15 /* tex1/1 */ +#define RADEON_EMIT_PP_TXFILTER_2 16 /* tex2/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_2 17 /* tex2/1 */ +#define RADEON_EMIT_SE_ZBIAS_FACTOR 18 /* zbias/2 */ +#define RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT 19 /* tcl/11 */ +#define RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED 20 /* material/17 */ +#define R200_EMIT_PP_TXCBLEND_0 21 /* tex0/4 */ +#define R200_EMIT_PP_TXCBLEND_1 22 /* tex1/4 */ +#define R200_EMIT_PP_TXCBLEND_2 23 /* tex2/4 */ +#define R200_EMIT_PP_TXCBLEND_3 24 /* tex3/4 */ +#define R200_EMIT_PP_TXCBLEND_4 25 /* tex4/4 */ +#define R200_EMIT_PP_TXCBLEND_5 26 /* tex5/4 */ +#define R200_EMIT_PP_TXCBLEND_6 27 /* /4 */ +#define R200_EMIT_PP_TXCBLEND_7 28 /* /4 */ +#define R200_EMIT_TCL_LIGHT_MODEL_CTL_0 29 /* tcl/7 */ +#define R200_EMIT_TFACTOR_0 30 /* tf/7 */ +#define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ +#define R200_EMIT_VAP_CTL 32 /* vap/1 */ +#define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ +#define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ +#define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ +#define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ +#define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ +#define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ +#define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ +#define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ +#define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ +#define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ +#define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ +#define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ +#define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ +#define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ +#define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ +#define R200_EMIT_VTE_CNTL 48 /* vte/1 */ +#define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ +#define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ +#define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ +#define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ +#define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ +#define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ +#define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ +#define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ +#define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ +#define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ +#define R200_EMIT_PP_CUBIC_FACES_0 61 +#define R200_EMIT_PP_CUBIC_OFFSETS_0 62 +#define R200_EMIT_PP_CUBIC_FACES_1 63 +#define R200_EMIT_PP_CUBIC_OFFSETS_1 64 +#define R200_EMIT_PP_CUBIC_FACES_2 65 +#define R200_EMIT_PP_CUBIC_OFFSETS_2 66 +#define R200_EMIT_PP_CUBIC_FACES_3 67 +#define R200_EMIT_PP_CUBIC_OFFSETS_3 68 +#define R200_EMIT_PP_CUBIC_FACES_4 69 +#define R200_EMIT_PP_CUBIC_OFFSETS_4 70 +#define R200_EMIT_PP_CUBIC_FACES_5 71 +#define R200_EMIT_PP_CUBIC_OFFSETS_5 72 +#define RADEON_MAX_STATE_PACKETS 73 + + +/* Commands understood by cmd_buffer ioctl. More can be added but + * obviously these can't be removed or changed: + */ +#define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ +#define RADEON_CMD_SCALARS 2 /* emit scalar data */ +#define RADEON_CMD_VECTORS 3 /* emit vector data */ +#define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ +#define RADEON_CMD_PACKET3 5 /* emit hw packet */ +#define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ +#define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ +#define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: + * doesn't make the cpu wait, just + * the graphics hardware */ + + +typedef union { + int i; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, packet_id, pad0, pad1; + } packet; + struct { + unsigned char cmd_type, offset, stride, count; + } scalars; + struct { + unsigned char cmd_type, offset, stride, count; + } vectors; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; +} drm_radeon_cmd_header_t; + +#define RADEON_WAIT_2D 0x1 +#define RADEON_WAIT_3D 0x2 + #define RADEON_FRONT 0x1 #define RADEON_BACK 0x2 #define RADEON_DEPTH 0x4 +#define RADEON_STENCIL 0x8 /* Primitive types */ @@ -78,12 +206,9 @@ /* Byte offsets for indirect buffer data */ #define RADEON_INDEX_PRIM_OFFSET 20 -#define RADEON_HOSTDATA_BLIT_OFFSET 32 #define RADEON_SCRATCH_REG_OFFSET 32 -/* Keep these small for testing - */ #define RADEON_NR_SAREA_CLIPRECTS 12 /* There are 2 heaps (local/AGP). Each region within a heap is a @@ -95,7 +220,7 @@ #define RADEON_NR_TEX_REGIONS 64 #define RADEON_LOG_TEX_GRANULARITY 16 -#define RADEON_MAX_TEXTURE_LEVELS 11 +#define RADEON_MAX_TEXTURE_LEVELS 12 #define RADEON_MAX_TEXTURE_UNITS 3 #endif /* __RADEON_SAREA_DEFINES__ */ @@ -155,28 +280,18 @@ /* Setup state */ unsigned int se_cntl_status; /* 0x2140 */ -#ifdef TCL_ENABLE - /* TCL state */ - radeon_color_regs_t se_tcl_material_emmissive; /* 0x2210 */ - radeon_color_regs_t se_tcl_material_ambient; - radeon_color_regs_t se_tcl_material_diffuse; - radeon_color_regs_t se_tcl_material_specular; - unsigned int se_tcl_shininess; - unsigned int se_tcl_output_vtx_fmt; - unsigned int se_tcl_output_vtx_sel; - unsigned int se_tcl_matrix_select_0; - unsigned int se_tcl_matrix_select_1; - unsigned int se_tcl_ucp_vert_blend_ctl; - unsigned int se_tcl_texture_proc_ctl; - unsigned int se_tcl_light_model_ctl; - unsigned int se_tcl_per_light_ctl[4]; -#endif - /* Misc state */ unsigned int re_top_left; /* 0x26c0 */ unsigned int re_misc; } drm_radeon_context_regs_t; +typedef struct { + /* Zbias state */ + unsigned int se_zbias_factor; /* 0x1dac */ + unsigned int se_zbias_constant; +} drm_radeon_context2_regs_t; + + /* Setup registers for each texture unit */ typedef struct { @@ -186,24 +301,37 @@ unsigned int pp_txcblend; unsigned int pp_txablend; unsigned int pp_tfactor; - unsigned int pp_border_color; - -#ifdef CUBIC_ENABLE - unsigned int pp_cubic_faces; - unsigned int pp_cubic_offset[5]; -#endif } drm_radeon_texture_regs_t; typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim:8; + unsigned int stateidx:8; + unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ + unsigned int vc_format; /* vertex format */ +} drm_radeon_prim_t; + + +typedef struct { + drm_radeon_context_regs_t context; + drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; + drm_radeon_context2_regs_t context2; + unsigned int dirty; +} drm_radeon_state_t; + + +typedef struct { unsigned char next, prev; unsigned char in_use; int age; } drm_radeon_tex_region_t; typedef struct { - /* The channel for communication of state information to the kernel - * on firing a vertex buffer. + /* The channel for communication of state information to the + * kernel on firing a vertex buffer with either of the + * obsoleted vertex/index ioctls. */ drm_radeon_context_regs_t context_state; drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; @@ -225,16 +353,50 @@ drm_radeon_tex_region_t tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS+1]; int tex_age[RADEON_NR_TEX_HEAPS]; int ctx_owner; + int pfState; /* number of 3d windows (0,1,2ormore) */ + int pfCurrentPage; /* which buffer is being displayed? */ + int crtc2_base; /* CRTC2 frame offset */ } drm_radeon_sarea_t; /* WARNING: If you change any of these defines, make sure to change the * defines in the Xserver file (xf86drmRadeon.h) + * + * KW: actually it's illegal to change any of this (backwards compatibility). */ + +/* Radeon specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( 0x40, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( 0x41) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( 0x42, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( 0x43) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( 0x44) +#define DRM_IOCTL_RADEON_RESET DRM_IO( 0x45) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( 0x46, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( 0x47) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( 0x48, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( 0x49, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( 0x4a, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( 0x4c, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(0x4d, drm_radeon_indirect_t) +#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(0x4e, drm_radeon_texture_t) +#define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( 0x4f, drm_radeon_vertex2_t) +#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( 0x50, drm_radeon_cmd_buffer_t) +#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(0x51, drm_radeon_getparam_t) +#define DRM_IOCTL_RADEON_FLIP DRM_IO( 0x52) +#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR( 0x53, drm_radeon_mem_alloc_t) +#define DRM_IOCTL_RADEON_FREE DRM_IOW( 0x54, drm_radeon_mem_free_t) +#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( 0x55, drm_radeon_mem_init_heap_t) +#define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR( 0x56, drm_radeon_irq_emit_t) +#define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( 0x57, drm_radeon_irq_wait_t) + typedef struct drm_radeon_init { enum { RADEON_INIT_CP = 0x01, - RADEON_CLEANUP_CP = 0x02 + RADEON_CLEANUP_CP = 0x02, + RADEON_INIT_R200_CP = 0x03, } func; unsigned long sarea_priv_offset; int is_pci; @@ -285,7 +447,7 @@ unsigned int clear_color; unsigned int clear_depth; unsigned int color_mask; - unsigned int depth_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ drm_radeon_clear_rect_t *depth_boxes; } drm_radeon_clear_t; @@ -304,6 +466,36 @@ int discard; /* Client finished with buffer? */ } drm_radeon_indices_t; +/* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices + * - allows multiple primitives and state changes in a single ioctl + * - supports driver change to emit native primitives + */ +typedef struct drm_radeon_vertex2 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + drm_radeon_state_t *state; + int nr_prims; + drm_radeon_prim_t *prim; +} drm_radeon_vertex2_t; + +/* v1.3 - obsoletes drm_radeon_vertex2 + * - allows arbitarily large cliprect list + * - allows updating of tcl packet, vector and scalar state + * - allows memory-efficient description of state updates + * - allows state to be emitted without a primitive + * (for clears, ctx switches) + * - allows more than one dma buffer to be referenced per ioctl + * - supports tcl driver + * - may be extended in future versions with new cmd types, packets + */ +typedef struct drm_radeon_cmd_buffer { + int bufsz; + char *buf; + int nbox; + drm_clip_rect_t *boxes; +} drm_radeon_cmd_buffer_t; + typedef struct drm_radeon_tex_image { unsigned int x, y; /* Blit coordinates */ unsigned int width, height; @@ -330,4 +522,55 @@ int discard; } drm_radeon_indirect_t; + +/* 1.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define RADEON_PARAM_AGP_BUFFER_OFFSET 1 /* card offset of 1st agp buffer */ +#define RADEON_PARAM_LAST_FRAME 2 +#define RADEON_PARAM_LAST_DISPATCH 3 +#define RADEON_PARAM_LAST_CLEAR 4 +#define RADEON_PARAM_IRQ_NR 5 +#define RADEON_PARAM_AGP_BASE 6 /* card offset of agp base */ + +typedef struct drm_radeon_getparam { + int param; + int *value; +} drm_radeon_getparam_t; + +/* 1.6: Set up a memory manager for regions of shared memory: + */ +#define RADEON_MEM_REGION_AGP 1 +#define RADEON_MEM_REGION_FB 2 + +typedef struct drm_radeon_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or agp */ +} drm_radeon_mem_alloc_t; + +typedef struct drm_radeon_mem_free { + int region; + int region_offset; +} drm_radeon_mem_free_t; + +typedef struct drm_radeon_mem_init_heap { + int region; + int size; + int start; +} drm_radeon_mem_init_heap_t; + + +/* 1.6: Userspace can request & wait on irq's: + */ +typedef struct drm_radeon_irq_emit { + int *irq_seq; +} drm_radeon_irq_emit_t; + +typedef struct drm_radeon_irq_wait { + int irq_seq; +} drm_radeon_irq_wait_t; + + #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_drv.c linux.21rc1-ac2/drivers/char/drm/radeon_drv.c --- linux.21rc1/drivers/char/drm/radeon_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -30,47 +30,11 @@ #include #include "radeon.h" #include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" #include "radeon_drv.h" #include "ati_pcigart.h" -#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." - -#define DRIVER_NAME "radeon" -#define DRIVER_DESC "ATI Radeon" -#define DRIVER_DATE "20010405" - -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 1 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { radeon_cp_buffers, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_INIT)] = { radeon_cp_init, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_START)] = { radeon_cp_start, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)] = { radeon_cp_stop, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)] = { radeon_cp_reset, 1, 1 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)] = { radeon_cp_idle, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)] = { radeon_engine_reset, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)] = { radeon_cp_swap, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CLEAR)] = { radeon_cp_clear, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX)] = { radeon_cp_vertex, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDICES)] = { radeon_cp_indices, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_TEXTURE)] = { radeon_cp_texture, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_STIPPLE)] = { radeon_cp_stipple, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDIRECT)] = { radeon_cp_indirect, 1, 1 }, - - -#if 0 -/* GH: Count data sent to card via ring or vertex/indirect buffers. - */ -#define __HAVE_COUNTERS 3 -#define __HAVE_COUNTER6 _DRM_STAT_IRQ -#define __HAVE_COUNTER7 _DRM_STAT_PRIMARY -#define __HAVE_COUNTER8 _DRM_STAT_SECONDARY -#endif - - #include "drm_agpsupport.h" #include "drm_auth.h" #include "drm_bufs.h" @@ -78,26 +42,6 @@ #include "drm_dma.h" #include "drm_drawable.h" #include "drm_drv.h" - -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init radeon_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", radeon_options ); -#endif - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_drv.h linux.21rc1-ac2/drivers/char/drm/radeon_drv.h --- linux.21rc1/drivers/char/drm/radeon_drv.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_drv.h 2003-04-22 16:44:36.000000000 +0100 @@ -31,6 +31,9 @@ #ifndef __RADEON_DRV_H__ #define __RADEON_DRV_H__ +#define GET_RING_HEAD(ring) readl( (volatile u32 *) (ring)->head ) +#define SET_RING_HEAD(ring,val) writel( (val), (volatile u32 *) (ring)->head ) + typedef struct drm_radeon_freelist { unsigned int age; drm_buf_t *buf; @@ -58,6 +61,15 @@ u32 se_cntl; } drm_radeon_depth_clear_t; + +struct mem_block { + struct mem_block *next; + struct mem_block *prev; + int start; + int size; + int pid; /* 0: free, -1: heap, other: real pids */ +}; + typedef struct drm_radeon_private { drm_radeon_ring_buffer_t ring; drm_radeon_sarea_t *sarea_priv; @@ -71,27 +83,32 @@ drm_radeon_freelist_t *head; drm_radeon_freelist_t *tail; -/* FIXME: ROTATE_BUFS is a hask to cycle through bufs until freelist - code is used. Note this hides a problem with the scratch register - (used to keep track of last buffer completed) being written to before - the last buffer has actually completed rendering. */ -#define ROTATE_BUFS 1 -#if ROTATE_BUFS int last_buf; -#endif volatile u32 *scratch; + int writeback_works; int usec_timeout; + + int is_r200; + int is_pci; unsigned long phys_pci_gart; dma_addr_t bus_pci_gart; - atomic_t idle_count; + struct { + u32 boxes; + int freelist_timeouts; + int freelist_loops; + int requested_bufs; + int last_frame_reads; + int last_clear_reads; + int clears; + int texture_uploads; + } stats; + int do_boxes; int page_flipping; int current_page; - u32 crtc_offset; - u32 crtc_offset_cntl; u32 color_fmt; unsigned int front_offset; @@ -116,14 +133,18 @@ drm_map_t *ring_rptr; drm_map_t *buffers; drm_map_t *agp_textures; + + struct mem_block *agp_heap; + struct mem_block *fb_heap; + + /* SW interrupt */ + wait_queue_head_t swi_queue; + atomic_t swi_emitted; + } drm_radeon_private_t; typedef struct drm_radeon_buf_priv { u32 age; - int prim; - int discard; - int dispatched; - drm_radeon_freelist_t *list_entry; } drm_radeon_buf_priv_t; /* radeon_cp.c */ @@ -149,14 +170,6 @@ extern int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n ); -static inline void -radeon_update_ring_snapshot( drm_radeon_ring_buffer_t *ring ) -{ - ring->space = (*(volatile int *)ring->head - ring->tail) * sizeof(u32); - if ( ring->space <= 0 ) - ring->space += ring->size; -} - extern int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ); extern int radeon_do_cleanup_cp( drm_device_t *dev ); extern int radeon_do_cleanup_pageflip( drm_device_t *dev ); @@ -176,6 +189,34 @@ unsigned int cmd, unsigned long arg ); extern int radeon_cp_indirect( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ); +extern int radeon_cp_vertex2(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_cp_cmdbuf(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_cp_getparam(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_cp_flip(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); + +extern int radeon_mem_alloc(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_mem_free(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_mem_init_heap(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern void radeon_mem_takedown( struct mem_block **heap ); +extern void radeon_mem_release( struct mem_block *heap ); + + /* radeon_irq.c */ +extern int radeon_irq_emit(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); +extern int radeon_irq_wait(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg ); + +extern int radeon_emit_and_wait_irq(drm_device_t *dev); +extern int radeon_wait_irq(drm_device_t *dev, int swi_nr); +extern int radeon_emit_irq(drm_device_t *dev); + + +/* Flags for stats.boxes + */ +#define RADEON_BOX_DMA_IDLE 0x1 +#define RADEON_BOX_RING_FULL 0x2 +#define RADEON_BOX_FLIP 0x4 +#define RADEON_BOX_WAIT_IDLE 0x8 +#define RADEON_BOX_TEXTURE_LOAD 0x10 + /* Register definitions, register access macros and drmAddMap constants @@ -202,10 +243,10 @@ #define RADEON_CRTC_OFFSET_CNTL 0x0228 # define RADEON_CRTC_TILE_EN (1 << 15) # define RADEON_CRTC_OFFSET_FLIP_CNTL (1 << 16) +#define RADEON_CRTC2_OFFSET 0x0324 +#define RADEON_CRTC2_OFFSET_CNTL 0x0328 #define RADEON_RB3D_COLORPITCH 0x1c48 -#define RADEON_RB3D_DEPTHCLEARVALUE 0x1c30 -#define RADEON_RB3D_DEPTHXY_OFFSET 0x1c60 #define RADEON_DP_GUI_MASTER_CNTL 0x146c # define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0) @@ -240,6 +281,24 @@ #define RADEON_SCRATCH_UMSK 0x0770 #define RADEON_SCRATCH_ADDR 0x0774 +#define GET_SCRATCH( x ) (dev_priv->writeback_works \ + ? readl( &dev_priv->scratch[(x)] ) \ + : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) + + +#define RADEON_GEN_INT_CNTL 0x0040 +# define RADEON_CRTC_VBLANK_MASK (1 << 0) +# define RADEON_GUI_IDLE_INT_ENABLE (1 << 19) +# define RADEON_SW_INT_ENABLE (1 << 25) + +#define RADEON_GEN_INT_STATUS 0x0044 +# define RADEON_CRTC_VBLANK_STAT (1 << 0) +# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0) +# define RADEON_GUI_IDLE_INT_TEST_ACK (1 << 19) +# define RADEON_SW_INT_TEST (1 << 25) +# define RADEON_SW_INT_TEST_ACK (1 << 25) +# define RADEON_SW_INT_FIRE (1 << 26) + #define RADEON_HOST_PATH_CNTL 0x0130 # define RADEON_HDP_SOFT_RESET (1 << 26) # define RADEON_HDP_WC_TIMEOUT_MASK (7 << 28) @@ -253,6 +312,12 @@ # define RADEON_ISYNC_WAIT_IDLEGUI (1 << 4) # define RADEON_ISYNC_CPSCRATCH_IDLEGUI (1 << 5) +#define RADEON_RBBM_GUICNTL 0x172c +# define RADEON_HOST_DATA_SWAP_NONE (0 << 0) +# define RADEON_HOST_DATA_SWAP_16BIT (1 << 0) +# define RADEON_HOST_DATA_SWAP_32BIT (2 << 0) +# define RADEON_HOST_DATA_SWAP_HDW (3 << 0) + #define RADEON_MC_AGP_LOCATION 0x014c #define RADEON_MC_FB_LOCATION 0x0148 #define RADEON_MCLK_CNTL 0x0012 @@ -290,10 +355,8 @@ # define RADEON_ROP_ENABLE (1 << 6) # define RADEON_STENCIL_ENABLE (1 << 7) # define RADEON_Z_ENABLE (1 << 8) -# define RADEON_DEPTH_XZ_OFFEST_ENABLE (1 << 9) -# define RADEON_ZBLOCK8 (0 << 15) -# define RADEON_ZBLOCK16 (1 << 15) #define RADEON_RB3D_DEPTHOFFSET 0x1c24 +#define RADEON_RB3D_DEPTHPITCH 0x1c28 #define RADEON_RB3D_PLANEMASK 0x1d84 #define RADEON_RB3D_STENCILREFMASK 0x1d7c #define RADEON_RB3D_ZCACHE_MODE 0x3250 @@ -306,9 +369,9 @@ # define RADEON_Z_TEST_MASK (7 << 4) # define RADEON_Z_TEST_ALWAYS (7 << 4) # define RADEON_STENCIL_TEST_ALWAYS (7 << 12) -# define RADEON_STENCIL_S_FAIL_KEEP (0 << 16) -# define RADEON_STENCIL_ZPASS_KEEP (0 << 20) -# define RADEON_STENCIL_ZFAIL_KEEP (0 << 20) +# define RADEON_STENCIL_S_FAIL_REPLACE (2 << 16) +# define RADEON_STENCIL_ZPASS_REPLACE (2 << 20) +# define RADEON_STENCIL_ZFAIL_REPLACE (2 << 24) # define RADEON_Z_WRITE_ENABLE (1 << 30) #define RADEON_RBBM_SOFT_RESET 0x00f0 # define RADEON_SOFT_RESET_CP (1 << 0) @@ -357,6 +420,16 @@ #define RADEON_SE_CNTL_STATUS 0x2140 #define RADEON_SE_LINE_WIDTH 0x1db8 #define RADEON_SE_VPORT_XSCALE 0x1d98 +#define RADEON_SE_ZBIAS_FACTOR 0x1db0 +#define RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED 0x2210 +#define RADEON_SE_TCL_OUTPUT_VTX_FMT 0x2254 +#define RADEON_SE_TCL_VECTOR_INDX_REG 0x2200 +# define RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT 16 +# define RADEON_VEC_INDX_DWORD_COUNT_SHIFT 28 +#define RADEON_SE_TCL_VECTOR_DATA_REG 0x2204 +#define RADEON_SE_TCL_SCALAR_INDX_REG 0x2208 +# define RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT 16 +#define RADEON_SE_TCL_SCALAR_DATA_REG 0x220C #define RADEON_SURFACE_ACCESS_FLAGS 0x0bf8 #define RADEON_SURFACE_ACCESS_CLR 0x0bfc #define RADEON_SURFACE_CNTL 0x0b00 @@ -421,6 +494,7 @@ #define RADEON_CP_RB_BASE 0x0700 #define RADEON_CP_RB_CNTL 0x0704 +# define RADEON_BUF_SWAP_32BIT (2 << 16) #define RADEON_CP_RB_RPTR_ADDR 0x070c #define RADEON_CP_RB_RPTR 0x0710 #define RADEON_CP_RB_WPTR 0x0714 @@ -457,11 +531,14 @@ #define RADEON_CP_PACKET3 0xC0000000 # define RADEON_3D_RNDR_GEN_INDX_PRIM 0x00002300 # define RADEON_WAIT_FOR_IDLE 0x00002600 +# define RADEON_3D_DRAW_VBUF 0x00002800 # define RADEON_3D_DRAW_IMMD 0x00002900 -# define RADEON_3D_CLEAR_ZMASK 0x00003200 +# define RADEON_3D_DRAW_INDX 0x00002A00 +# define RADEON_3D_LOAD_VBPNTR 0x00002F00 # define RADEON_CNTL_HOSTDATA_BLT 0x00009400 # define RADEON_CNTL_PAINT_MULTI 0x00009A00 # define RADEON_CNTL_BITBLT_MULTI 0x00009B00 +# define RADEON_CNTL_SET_SCISSORS 0xC0001E00 #define RADEON_CP_PACKET_MASK 0xC0000000 #define RADEON_CP_PACKET_COUNT_MASK 0x3fff0000 @@ -470,6 +547,7 @@ #define RADEON_CP_PACKET1_REG1_MASK 0x003ff800 #define RADEON_VTX_Z_PRESENT (1 << 31) +#define RADEON_VTX_PKCOLOR_PRESENT (1 << 3) #define RADEON_PRIM_TYPE_NONE (0 << 0) #define RADEON_PRIM_TYPE_POINT (1 << 0) @@ -482,6 +560,7 @@ #define RADEON_PRIM_TYPE_RECT_LIST (8 << 0) #define RADEON_PRIM_TYPE_3VRT_POINT_LIST (9 << 0) #define RADEON_PRIM_TYPE_3VRT_LINE_LIST (10 << 0) +#define RADEON_PRIM_TYPE_MASK 0xf #define RADEON_PRIM_WALK_IND (1 << 4) #define RADEON_PRIM_WALK_LIST (2 << 4) #define RADEON_PRIM_WALK_RING (3 << 4) @@ -508,6 +587,105 @@ #define RADEON_TXFORMAT_ARGB4444 5 #define RADEON_TXFORMAT_ARGB8888 6 #define RADEON_TXFORMAT_RGBA8888 7 +#define RADEON_TXFORMAT_VYUY422 10 +#define RADEON_TXFORMAT_YVYU422 11 +#define RADEON_TXFORMAT_DXT1 12 +#define RADEON_TXFORMAT_DXT23 14 +#define RADEON_TXFORMAT_DXT45 15 + +#define R200_PP_TXCBLEND_0 0x2f00 +#define R200_PP_TXCBLEND_1 0x2f10 +#define R200_PP_TXCBLEND_2 0x2f20 +#define R200_PP_TXCBLEND_3 0x2f30 +#define R200_PP_TXCBLEND_4 0x2f40 +#define R200_PP_TXCBLEND_5 0x2f50 +#define R200_PP_TXCBLEND_6 0x2f60 +#define R200_PP_TXCBLEND_7 0x2f70 +#define R200_SE_TCL_LIGHT_MODEL_CTL_0 0x2268 +#define R200_PP_TFACTOR_0 0x2ee0 +#define R200_SE_VTX_FMT_0 0x2088 +#define R200_SE_VAP_CNTL 0x2080 +#define R200_SE_TCL_MATRIX_SEL_0 0x2230 +#define R200_SE_TCL_TEX_PROC_CTL_2 0x22a8 +#define R200_SE_TCL_UCP_VERT_BLEND_CTL 0x22c0 +#define R200_PP_TXFILTER_5 0x2ca0 +#define R200_PP_TXFILTER_4 0x2c80 +#define R200_PP_TXFILTER_3 0x2c60 +#define R200_PP_TXFILTER_2 0x2c40 +#define R200_PP_TXFILTER_1 0x2c20 +#define R200_PP_TXFILTER_0 0x2c00 +#define R200_PP_TXOFFSET_5 0x2d78 +#define R200_PP_TXOFFSET_4 0x2d60 +#define R200_PP_TXOFFSET_3 0x2d48 +#define R200_PP_TXOFFSET_2 0x2d30 +#define R200_PP_TXOFFSET_1 0x2d18 +#define R200_PP_TXOFFSET_0 0x2d00 + +#define R200_PP_CUBIC_FACES_0 0x2c18 +#define R200_PP_CUBIC_FACES_1 0x2c38 +#define R200_PP_CUBIC_FACES_2 0x2c58 +#define R200_PP_CUBIC_FACES_3 0x2c78 +#define R200_PP_CUBIC_FACES_4 0x2c98 +#define R200_PP_CUBIC_FACES_5 0x2cb8 +#define R200_PP_CUBIC_OFFSET_F1_0 0x2d04 +#define R200_PP_CUBIC_OFFSET_F2_0 0x2d08 +#define R200_PP_CUBIC_OFFSET_F3_0 0x2d0c +#define R200_PP_CUBIC_OFFSET_F4_0 0x2d10 +#define R200_PP_CUBIC_OFFSET_F5_0 0x2d14 +#define R200_PP_CUBIC_OFFSET_F1_1 0x2d1c +#define R200_PP_CUBIC_OFFSET_F2_1 0x2d20 +#define R200_PP_CUBIC_OFFSET_F3_1 0x2d24 +#define R200_PP_CUBIC_OFFSET_F4_1 0x2d28 +#define R200_PP_CUBIC_OFFSET_F5_1 0x2d2c +#define R200_PP_CUBIC_OFFSET_F1_2 0x2d34 +#define R200_PP_CUBIC_OFFSET_F2_2 0x2d38 +#define R200_PP_CUBIC_OFFSET_F3_2 0x2d3c +#define R200_PP_CUBIC_OFFSET_F4_2 0x2d40 +#define R200_PP_CUBIC_OFFSET_F5_2 0x2d44 +#define R200_PP_CUBIC_OFFSET_F1_3 0x2d4c +#define R200_PP_CUBIC_OFFSET_F2_3 0x2d50 +#define R200_PP_CUBIC_OFFSET_F3_3 0x2d54 +#define R200_PP_CUBIC_OFFSET_F4_3 0x2d58 +#define R200_PP_CUBIC_OFFSET_F5_3 0x2d5c +#define R200_PP_CUBIC_OFFSET_F1_4 0x2d64 +#define R200_PP_CUBIC_OFFSET_F2_4 0x2d68 +#define R200_PP_CUBIC_OFFSET_F3_4 0x2d6c +#define R200_PP_CUBIC_OFFSET_F4_4 0x2d70 +#define R200_PP_CUBIC_OFFSET_F5_4 0x2d74 +#define R200_PP_CUBIC_OFFSET_F1_5 0x2d7c +#define R200_PP_CUBIC_OFFSET_F2_5 0x2d80 +#define R200_PP_CUBIC_OFFSET_F3_5 0x2d84 +#define R200_PP_CUBIC_OFFSET_F4_5 0x2d88 +#define R200_PP_CUBIC_OFFSET_F5_5 0x2d8c + +#define R200_RE_AUX_SCISSOR_CNTL 0x26f0 +#define R200_SE_VTE_CNTL 0x20b0 +#define R200_SE_TCL_OUTPUT_VTX_COMP_SEL 0x2250 +#define R200_PP_TAM_DEBUG3 0x2d9c +#define R200_PP_CNTL_X 0x2cc4 +#define R200_SE_VAP_CNTL_STATUS 0x2140 +#define R200_RE_SCISSOR_TL_0 0x1cd8 +#define R200_RE_SCISSOR_TL_1 0x1ce0 +#define R200_RE_SCISSOR_TL_2 0x1ce8 +#define R200_RB3D_DEPTHXY_OFFSET 0x1d60 +#define R200_RE_AUX_SCISSOR_CNTL 0x26f0 +#define R200_SE_VTX_STATE_CNTL 0x2180 +#define R200_RE_POINTSIZE 0x2648 +#define R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0 0x2254 + + +#define SE_VAP_CNTL__TCL_ENA_MASK 0x00000001 +#define SE_VAP_CNTL__FORCE_W_TO_ONE_MASK 0x00010000 +#define SE_VAP_CNTL__VF_MAX_VTX_NUM__SHIFT 0x00000012 +#define SE_VTE_CNTL__VTX_XY_FMT_MASK 0x00000100 +#define SE_VTE_CNTL__VTX_Z_FMT_MASK 0x00000200 +#define SE_VTX_FMT_0__VTX_Z0_PRESENT_MASK 0x00000001 +#define SE_VTX_FMT_0__VTX_W0_PRESENT_MASK 0x00000002 +#define SE_VTX_FMT_0__VTX_COLOR_0_FMT__SHIFT 0x0000000b +#define R200_3D_DRAW_IMMD_2 0xC0003500 +#define R200_SE_VTX_FMT_1 0x208c +#define R200_RE_CNTL 0x1c50 + /* Constants */ #define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */ @@ -515,6 +693,7 @@ #define RADEON_LAST_FRAME_REG RADEON_SCRATCH_REG0 #define RADEON_LAST_DISPATCH_REG RADEON_SCRATCH_REG1 #define RADEON_LAST_CLEAR_REG RADEON_SCRATCH_REG2 +#define RADEON_LAST_SWI_REG RADEON_SCRATCH_REG3 #define RADEON_LAST_DISPATCH 1 #define RADEON_MAX_VB_AGE 0x7fffffff @@ -526,41 +705,11 @@ #define RADEON_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) #define RADEON_ADDR(reg) (RADEON_BASE( reg ) + reg) -#define RADEON_DEREF(reg) *(volatile u32 *)RADEON_ADDR( reg ) -#ifdef __alpha__ -#define RADEON_READ(reg) (_RADEON_READ((u32 *)RADEON_ADDR( reg ))) -static inline u32 _RADEON_READ(u32 *addr) -{ - mb(); - return *(volatile u32 *)addr; -} -#define RADEON_WRITE(reg,val) \ -do { \ - wmb(); \ - RADEON_DEREF(reg) = val; \ -} while (0) -#else -#define RADEON_READ(reg) RADEON_DEREF( reg ) -#define RADEON_WRITE(reg, val) do { RADEON_DEREF( reg ) = val; } while (0) -#endif +#define RADEON_READ(reg) readl( (volatile u32 *) RADEON_ADDR(reg) ) +#define RADEON_WRITE(reg,val) writel( (val), (volatile u32 *) RADEON_ADDR(reg)) -#define RADEON_DEREF8(reg) *(volatile u8 *)RADEON_ADDR( reg ) -#ifdef __alpha__ -#define RADEON_READ8(reg) _RADEON_READ8((u8 *)RADEON_ADDR( reg )) -static inline u8 _RADEON_READ8(u8 *addr) -{ - mb(); - return *(volatile u8 *)addr; -} -#define RADEON_WRITE8(reg,val) \ -do { \ - wmb(); \ - RADEON_DEREF8( reg ) = val; \ -} while (0) -#else -#define RADEON_READ8(reg) RADEON_DEREF8( reg ) -#define RADEON_WRITE8(reg, val) do { RADEON_DEREF8( reg ) = val; } while (0) -#endif +#define RADEON_READ8(reg) readb( (volatile u8 *) RADEON_ADDR(reg) ) +#define RADEON_WRITE8(reg,val) writeb( (val), (volatile u8 *) RADEON_ADDR(reg)) #define RADEON_WRITE_PLL( addr, val ) \ do { \ @@ -647,20 +796,16 @@ } \ } while (0) + +/* Perfbox functionality only. + */ #define RING_SPACE_TEST_WITH_RETURN( dev_priv ) \ do { \ - drm_radeon_ring_buffer_t *ring = &dev_priv->ring; int i; \ - if ( ring->space < ring->high_mark ) { \ - for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { \ - radeon_update_ring_snapshot( ring ); \ - if ( ring->space >= ring->high_mark ) \ - goto __ring_space_done; \ - udelay( 1 ); \ - } \ - DRM_ERROR( "ring space check failed!\n" ); \ - return -EBUSY; \ + if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE)) { \ + u32 head = GET_RING_HEAD(&dev_priv->ring); \ + if (head == dev_priv->ring.tail) \ + dev_priv->stats.boxes |= RADEON_BOX_DMA_IDLE; \ } \ - __ring_space_done: ; \ } while (0) #define VB_AGE_TEST_WITH_RETURN( dev_priv ) \ @@ -668,7 +813,7 @@ drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; \ if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \ int __ret = radeon_do_cp_idle( dev_priv ); \ - if ( __ret < 0 ) return __ret; \ + if ( __ret ) return __ret; \ sarea_priv->last_dispatch = 0; \ radeon_freelist_reset( dev ); \ } \ @@ -694,12 +839,17 @@ * Ring control */ -#define radeon_flush_write_combine() mb() +#if defined(__powerpc__) +#define radeon_flush_write_combine() (void) GET_RING_HEAD( &dev_priv->ring ) +#else +#define radeon_flush_write_combine() wmb() +#warning PCI posting bug +#endif #define RADEON_VERBOSE 0 -#define RING_LOCALS int write; unsigned int mask; volatile u32 *ring; +#define RING_LOCALS int write, _nr; unsigned int mask; u32 *ring; #define BEGIN_RING( n ) do { \ if ( RADEON_VERBOSE ) { \ @@ -707,9 +857,10 @@ n, __FUNCTION__ ); \ } \ if ( dev_priv->ring.space <= (n) * sizeof(u32) ) { \ + COMMIT_RING(); \ radeon_wait_ring( dev_priv, (n) * sizeof(u32) ); \ } \ - dev_priv->ring.space -= (n) * sizeof(u32); \ + _nr = n; dev_priv->ring.space -= (n) * sizeof(u32); \ ring = dev_priv->ring.start; \ write = dev_priv->ring.tail; \ mask = dev_priv->ring.tail_mask; \ @@ -720,9 +871,22 @@ DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ write, dev_priv->ring.tail ); \ } \ - radeon_flush_write_combine(); \ - dev_priv->ring.tail = write; \ - RADEON_WRITE( RADEON_CP_RB_WPTR, write ); \ + if (((dev_priv->ring.tail + _nr) & mask) != write) { \ + DRM_ERROR( \ + "ADVANCE_RING(): mismatch: nr: %x write: %x line: %d\n", \ + ((dev_priv->ring.tail + _nr) & mask), \ + write, __LINE__); \ + } else \ + dev_priv->ring.tail = write; \ +} while (0) + +#define COMMIT_RING() do { \ + /* Flush writes to ring */ \ + rmb(); \ + GET_RING_HEAD( &dev_priv->ring ); \ + RADEON_WRITE( RADEON_CP_RB_WPTR, dev_priv->ring.tail ); \ + /* read from PCI bus to ensure correct posting */ \ + RADEON_READ( RADEON_CP_RB_RPTR ); \ } while (0) #define OUT_RING( x ) do { \ @@ -734,6 +898,33 @@ write &= mask; \ } while (0) -#define RADEON_PERFORMANCE_BOXES 0 +#define OUT_RING_REG( reg, val ) do { \ + OUT_RING( CP_PACKET0( reg, 0 ) ); \ + OUT_RING( val ); \ +} while (0) + + +#define OUT_RING_USER_TABLE( tab, sz ) do { \ + int _size = (sz); \ + int *_tab = (tab); \ + \ + if (write + _size > mask) { \ + int i = (mask+1) - write; \ + if (__copy_from_user( (int *)(ring+write), \ + _tab, i*4 )) \ + return -EFAULT; \ + write = 0; \ + _size -= i; \ + _tab += i; \ + } \ + \ + if (_size && __copy_from_user( (int *)(ring+write), \ + _tab, _size*4 )) \ + return -EFAULT; \ + \ + write += _size; \ + write &= mask; \ +} while (0) + #endif /* __RADEON_DRV_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon.h linux.21rc1-ac2/drivers/char/drm/radeon.h --- linux.21rc1/drivers/char/drm/radeon.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon.h 2003-04-22 16:44:36.000000000 +0100 @@ -44,7 +44,78 @@ #define __HAVE_SG 1 #define __HAVE_PCI_DMA 1 -/* Driver customization: +#define DRIVER_AUTHOR "Gareth Hughes, Keith Whitwell, others." + +#define DRIVER_NAME "radeon" +#define DRIVER_DESC "ATI Radeon" +#define DRIVER_DATE "20020828" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 7 +#define DRIVER_PATCHLEVEL 0 + +/* Interface history: + * + * 1.1 - ?? + * 1.2 - Add vertex2 ioctl (keith) + * - Add stencil capability to clear ioctl (gareth, keith) + * - Increase MAX_TEXTURE_LEVELS (brian) + * 1.3 - Add cmdbuf ioctl (keith) + * - Add support for new radeon packets (keith) + * - Add getparam ioctl (keith) + * - Add flip-buffers ioctl, deprecate fullscreen foo (keith). + * 1.4 - Add scratch registers to get_param ioctl. + * 1.5 - Add r200 packets to cmdbuf ioctl + * - Add r200 function to init ioctl + * - Add 'scalar2' instruction to cmdbuf + * 1.6 - Add static agp memory manager + * Add irq handler (won't be turned on unless X server knows to) + * Add irq ioctls and irq_active getparam. + * Add wait command for cmdbuf ioctl + * Add agp offset query for getparam + * 1.7 - Add support for cube map registers: R200_PP_CUBIC_FACES_[0..5] + * and R200_PP_CUBIC_OFFSET_F1_[0..5]. + * Added packets R200_EMIT_PP_CUBIC_FACES_[0..5] and + * R200_EMIT_PP_CUBIC_OFFSETS_[0..5]. (brian) + */ +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { radeon_cp_buffers, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_INIT)] = { radeon_cp_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_START)] = { radeon_cp_start, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)] = { radeon_cp_stop, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)] = { radeon_cp_reset, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)] = { radeon_cp_idle, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)] = { radeon_engine_reset, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)] = { radeon_cp_swap, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CLEAR)] = { radeon_cp_clear, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX)] = { radeon_cp_vertex, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDICES)] = { radeon_cp_indices, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_TEXTURE)] = { radeon_cp_texture, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_STIPPLE)] = { radeon_cp_stipple, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDIRECT)] = { radeon_cp_indirect, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX2)] = { radeon_cp_vertex2, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CMDBUF)] = { radeon_cp_cmdbuf, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_GETPARAM)] = { radeon_cp_getparam, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FLIP)] = { radeon_cp_flip, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_ALLOC)] = { radeon_mem_alloc, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FREE)] = { radeon_mem_free, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_INIT_HEAP)] = { radeon_mem_init_heap, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_IRQ_EMIT)] = { radeon_irq_emit, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_RADEON_IRQ_WAIT)] = { radeon_irq_wait, 1, 0 }, + + +#define USE_IRQS 1 +#if USE_IRQS +#define __HAVE_DMA_IRQ 1 +#define __HAVE_VBL_IRQ 1 +#define __HAVE_SHARED_IRQ 1 + +/* When a client dies: + * - Check for and clean up flipped page state + * - Free any alloced agp memory. + * + * DRM infrastructure takes care of reclaiming dma buffers. */ #define DRIVER_PRERELEASE() do { \ if ( dev->dev_private ) { \ @@ -52,31 +123,36 @@ if ( dev_priv->page_flipping ) { \ radeon_do_cleanup_pageflip( dev ); \ } \ + radeon_mem_release( dev_priv->agp_heap ); \ } \ } while (0) +/* On unloading the module: + * - Free memory heap structure + * - Remove mappings made at startup and free dev_private. + */ #define DRIVER_PRETAKEDOWN() do { \ - if ( dev->dev_private ) radeon_do_cleanup_cp( dev ); \ + if ( dev->dev_private ) { \ + drm_radeon_private_t *dev_priv = dev->dev_private; \ + radeon_mem_takedown( &(dev_priv->agp_heap) ); \ + radeon_do_cleanup_cp( dev ); \ + } \ } while (0) +#else +#define __HAVE_DMA_IRQ 0 +#endif + /* DMA customization: */ #define __HAVE_DMA 1 -#if 0 -/* GH: Remove this for now... */ -#define __HAVE_DMA_QUIESCENT 1 -#define DRIVER_DMA_QUIESCENT() do { \ - drm_radeon_private_t *dev_priv = dev->dev_private; \ - return radeon_do_cp_idle( dev_priv ); \ -} while (0) -#endif /* Buffer customization: */ #define DRIVER_BUF_PRIV_T drm_radeon_buf_priv_t -#define DRIVER_AGP_BUFFERS_MAP( dev ) \ +#define DRIVER_AGP_BUFFERS_MAP( dev ) \ ((drm_radeon_private_t *)((dev)->dev_private))->buffers #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_irq.c linux.21rc1-ac2/drivers/char/drm/radeon_irq.c --- linux.21rc1/drivers/char/drm/radeon_irq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_irq.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,258 @@ +/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*- + * + * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Keith Whitwell + * Michel Dänzer + */ + +#include "radeon.h" +#include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_drv.h" +#include "drm_os_linux.h" + +/* Interrupts - Used for device synchronization and flushing in the + * following circumstances: + * + * - Exclusive FB access with hw idle: + * - Wait for GUI Idle (?) interrupt, then do normal flush. + * + * - Frame throttling, NV_fence: + * - Drop marker irq's into command stream ahead of time. + * - Wait on irq's with lock *not held* + * - Check each for termination condition + * + * - Internally in cp_getbuffer, etc: + * - as above, but wait with lock held??? + * + * NOTE: These functions are misleadingly named -- the irq's aren't + * tied to dma at all, this is just a hangover from dri prehistory. + */ + +void DRM(dma_service)(int irq, void *arg, struct pt_regs *reg) +{ + drm_device_t *dev = (drm_device_t *) arg; + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + u32 stat; + + stat = RADEON_READ(RADEON_GEN_INT_STATUS) + & (RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT); + if (!stat) + return; + + /* SW interrupt */ + if (stat & RADEON_SW_INT_TEST) { + wake_up_interruptible( &dev_priv->swi_queue ); + } + + /* VBLANK interrupt */ + if (stat & RADEON_CRTC_VBLANK_STAT) { + atomic_inc(&dev->vbl_received); + wake_up_interruptible(&dev->vbl_queue); + DRM(vbl_send_signals)(dev); + } + + /* Acknowledge all the bits in GEN_INT_STATUS -- seem to get + * more than we asked for... + */ + RADEON_WRITE(RADEON_GEN_INT_STATUS, stat); +} + +static __inline__ void radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv) +{ + u32 tmp = RADEON_READ( RADEON_GEN_INT_STATUS ) + & (RADEON_SW_INT_TEST_ACK | RADEON_CRTC_VBLANK_STAT); + if (tmp) + RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp ); +} + +int radeon_emit_irq(drm_device_t *dev) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + unsigned int ret; + RING_LOCALS; + + atomic_inc(&dev_priv->swi_emitted); + ret = atomic_read(&dev_priv->swi_emitted); + + BEGIN_RING( 4 ); + OUT_RING_REG( RADEON_LAST_SWI_REG, ret ); + OUT_RING_REG( RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE ); + ADVANCE_RING(); + COMMIT_RING(); + + return ret; +} + + +int radeon_wait_irq(drm_device_t *dev, int swi_nr) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + int ret = 0; + + if (RADEON_READ( RADEON_LAST_SWI_REG ) >= swi_nr) + return 0; + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + /* This is a hack to work around mysterious freezes on certain + * systems: + */ + radeon_acknowledge_irqs( dev_priv ); + + DRM_WAIT_ON( ret, dev_priv->swi_queue, 3 * HZ, + RADEON_READ( RADEON_LAST_SWI_REG ) >= swi_nr ); + + return ret; +} + +int radeon_emit_and_wait_irq(drm_device_t *dev) +{ + return radeon_wait_irq( dev, radeon_emit_irq(dev) ); +} + + +int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + unsigned int cur_vblank; + int ret = 0; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + radeon_acknowledge_irqs( dev_priv ); + + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + + /* Assume that the user has missed the current sequence number + * by about a day rather than she wants to wait for years + * using vertical blanks... + */ + DRM_WAIT_ON( ret, dev->vbl_queue, 3*HZ, + ( ( ( cur_vblank = atomic_read(&dev->vbl_received ) ) + - *sequence ) <= (1<<23) ) ); + + *sequence = cur_vblank; + + return ret; +} + + +/* Needs the lock as it touches the ring. + */ +int radeon_irq_emit(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_irq_emit_t emit; + int result; + + LOCK_TEST_WITH_RETURN( dev ); + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( emit, (drm_radeon_irq_emit_t *)data, + sizeof(emit) ); + + result = radeon_emit_irq( dev ); + + if ( copy_to_user( emit.irq_seq, &result, sizeof(int) ) ) { + DRM_ERROR( "copy_to_user\n" ); + return -EFAULT; + } + + return 0; +} + + +/* Doesn't need the hardware lock. + */ +int radeon_irq_wait(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_irq_wait_t irqwait; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( irqwait, (drm_radeon_irq_wait_t *)data, + sizeof(irqwait) ); + + return radeon_wait_irq( dev, irqwait.irq_seq ); +} + + +/* drm_dma.h hooks +*/ +void DRM(driver_irq_preinstall)( drm_device_t *dev ) { + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + + /* Disable *all* interrupts */ + RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 ); + + /* Clear bits if they're already high */ + radeon_acknowledge_irqs( dev_priv ); +} + +void DRM(driver_irq_postinstall)( drm_device_t *dev ) { + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + + atomic_set(&dev_priv->swi_emitted, 0); + init_waitqueue_head( &dev_priv->swi_queue ); + + /* Turn on SW and VBL ints */ + RADEON_WRITE( RADEON_GEN_INT_CNTL, + RADEON_CRTC_VBLANK_MASK | + RADEON_SW_INT_ENABLE ); +} + +void DRM(driver_irq_uninstall)( drm_device_t *dev ) { + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *)dev->dev_private; + if ( dev_priv ) { + /* Disable *all* interrupts */ + RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 ); + } +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_mem.c linux.21rc1-ac2/drivers/char/drm/radeon_mem.c --- linux.21rc1/drivers/char/drm/radeon_mem.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_mem.c 2003-04-22 16:44:36.000000000 +0100 @@ -0,0 +1,338 @@ +/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*- + * + * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. + * + * The Weather Channel (TM) funded Tungsten Graphics to develop the + * initial release of the Radeon 8500 driver under the XFree86 license. + * This notice must be preserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Keith Whitwell + */ + +#include "radeon.h" +#include "drmP.h" +#include "drm.h" +#include "radeon_drm.h" +#include "radeon_drv.h" +#include "drm_os_linux.h" + +/* Very simple allocator for agp memory, working on a static range + * already mapped into each client's address space. + */ + +static struct mem_block *split_block(struct mem_block *p, int start, int size, + int pid ) +{ + /* Maybe cut off the start of an existing block */ + if (start > p->start) { + struct mem_block *newblock = kmalloc(sizeof(*newblock), GFP_KERNEL); + if (!newblock) + goto out; + newblock->start = start; + newblock->size = p->size - (start - p->start); + newblock->pid = 0; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size -= newblock->size; + p = newblock; + } + + /* Maybe cut off the end of an existing block */ + if (size < p->size) { + struct mem_block *newblock = kmalloc(sizeof(*newblock), GFP_KERNEL); + if (!newblock) + goto out; + newblock->start = start + size; + newblock->size = p->size - size; + newblock->pid = 0; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size = size; + } + + out: + /* Our block is in the middle */ + p->pid = pid; + return p; +} + +static struct mem_block *alloc_block( struct mem_block *heap, int size, + int align2, int pid ) +{ + struct mem_block *p; + int mask = (1 << align2)-1; + + for (p = heap->next ; p != heap ; p = p->next) { + int start = (p->start + mask) & ~mask; + if (p->pid == 0 && start + size <= p->start + p->size) + return split_block( p, start, size, pid ); + } + + return NULL; +} + +static struct mem_block *find_block( struct mem_block *heap, int start ) +{ + struct mem_block *p; + + for (p = heap->next ; p != heap ; p = p->next) + if (p->start == start) + return p; + + return NULL; +} + + +static void free_block( struct mem_block *p ) +{ + p->pid = 0; + + /* Assumes a single contiguous range. Needs a special pid in + * 'heap' to stop it being subsumed. + */ + if (p->next->pid == 0) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + kfree(q); + } + + if (p->prev->pid == 0) { + struct mem_block *q = p->prev; + q->size += p->size; + q->next = p->next; + q->next->prev = q; + kfree(p); + } +} + +static void print_heap( struct mem_block *heap ) +{ + struct mem_block *p; + + for (p = heap->next ; p != heap ; p = p->next) + DRM_DEBUG("0x%x..0x%x (0x%x) -- owner %d\n", + p->start, p->start + p->size, + p->size, p->pid); +} + +/* Initialize. How to check for an uninitialized heap? + */ +static int init_heap(struct mem_block **heap, int start, int size) +{ + struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL); + + if (!blocks) + return -ENOMEM; + + *heap = kmalloc(sizeof(**heap), GFP_KERNEL); + if (!*heap) { + kfree( blocks ); + return -ENOMEM; + } + + blocks->start = start; + blocks->size = size; + blocks->pid = 0; + blocks->next = blocks->prev = *heap; + + memset( *heap, 0, sizeof(**heap) ); + (*heap)->pid = -1; + (*heap)->next = (*heap)->prev = blocks; + return 0; +} + + +/* Free all blocks associated with the releasing pid. + */ +void radeon_mem_release( struct mem_block *heap ) +{ + int pid = current->pid; + struct mem_block *p; + + if (!heap || !heap->next) + return; + + for (p = heap->next ; p != heap ; p = p->next) { + if (p->pid == pid) + p->pid = 0; + } + + /* Assumes a single contiguous range. Needs a special pid in + * 'heap' to stop it being subsumed. + */ + for (p = heap->next ; p != heap ; p = p->next) { + while (p->pid == 0 && p->next->pid == 0) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + kfree(q); + } + } +} + +/* Shutdown. + */ +void radeon_mem_takedown( struct mem_block **heap ) +{ + struct mem_block *p; + + if (!*heap) + return; + + for (p = (*heap)->next ; p != *heap ; ) { + struct mem_block *q = p; + p = p->next; + kfree(q); + } + + kfree( *heap ); + *heap = 0; +} + + + +/* IOCTL HANDLERS */ + +static struct mem_block **get_heap( drm_radeon_private_t *dev_priv, + int region ) +{ + switch( region ) { + case RADEON_MEM_REGION_AGP: + return &dev_priv->agp_heap; + case RADEON_MEM_REGION_FB: + return &dev_priv->fb_heap; + default: + return 0; + } +} + +int radeon_mem_alloc(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_alloc_t alloc; + struct mem_block *block, **heap; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( alloc, (drm_radeon_mem_alloc_t *)data, + sizeof(alloc) ); + + heap = get_heap( dev_priv, alloc.region ); + if (!heap || !*heap) + return -EFAULT; + + /* Make things easier on ourselves: all allocations at least + * 4k aligned. + */ + if (alloc.alignment < 12) + alloc.alignment = 12; + + block = alloc_block( *heap, alloc.size, alloc.alignment, + current->pid ); + + if (!block) + return -ENOMEM; + + if ( copy_to_user( alloc.region_offset, &block->start, + sizeof(int) ) ) { + DRM_ERROR( "copy_to_user\n" ); + return -EFAULT; + } + + return 0; +} + + + +int radeon_mem_free(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_free_t memfree; + struct mem_block *block, **heap; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( memfree, (drm_radeon_mem_free_t *)data, + sizeof(memfree) ); + + heap = get_heap( dev_priv, memfree.region ); + if (!heap || !*heap) + return -EFAULT; + + block = find_block( *heap, memfree.region_offset ); + if (!block) + return -EFAULT; + + if (block->pid != current->pid) + return -EPERM; + + free_block( block ); + return 0; +} + +int radeon_mem_init_heap(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_mem_init_heap_t initheap; + struct mem_block **heap; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( initheap, (drm_radeon_mem_init_heap_t *)data, + sizeof(initheap) ); + + heap = get_heap( dev_priv, initheap.region ); + if (!heap) + return -EFAULT; + + if (*heap) { + DRM_ERROR("heap already initialized?"); + return -EFAULT; + } + + return init_heap( heap, initheap.start, initheap.size ); +} + + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/radeon_state.c linux.21rc1-ac2/drivers/char/drm/radeon_state.c --- linux.21rc1/drivers/char/drm/radeon_state.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/radeon_state.c 2003-04-22 16:44:36.000000000 +0100 @@ -29,10 +29,11 @@ #include "radeon.h" #include "drmP.h" -#include "radeon_drv.h" #include "drm.h" -#include - +#include "drm_sarea.h" +#include "radeon_drm.h" +#include "radeon_drv.h" +#include "drm_os_linux.h" /* ================================================================ * CP hardware state programming functions @@ -47,360 +48,254 @@ box->x1, box->y1, box->x2, box->y2 ); BEGIN_RING( 4 ); - OUT_RING( CP_PACKET0( RADEON_RE_TOP_LEFT, 0 ) ); OUT_RING( (box->y1 << 16) | box->x1 ); - OUT_RING( CP_PACKET0( RADEON_RE_WIDTH_HEIGHT, 0 ) ); OUT_RING( ((box->y2 - 1) << 16) | (box->x2 - 1) ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_context( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 14 ); - - OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) ); - OUT_RING( ctx->pp_misc ); - OUT_RING( ctx->pp_fog_color ); - OUT_RING( ctx->re_solid_color ); - OUT_RING( ctx->rb3d_blendcntl ); - OUT_RING( ctx->rb3d_depthoffset ); - OUT_RING( ctx->rb3d_depthpitch ); - OUT_RING( ctx->rb3d_zstencilcntl ); - - OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 2 ) ); - OUT_RING( ctx->pp_cntl ); - OUT_RING( ctx->rb3d_cntl ); - OUT_RING( ctx->rb3d_coloroffset ); - - OUT_RING( CP_PACKET0( RADEON_RB3D_COLORPITCH, 0 ) ); - OUT_RING( ctx->rb3d_colorpitch ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_vertfmt( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 2 ); - - OUT_RING( CP_PACKET0( RADEON_SE_COORD_FMT, 0 ) ); - OUT_RING( ctx->se_coord_fmt ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_line( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 5 ); - - OUT_RING( CP_PACKET0( RADEON_RE_LINE_PATTERN, 1 ) ); - OUT_RING( ctx->re_line_pattern ); - OUT_RING( ctx->re_line_state ); - - OUT_RING( CP_PACKET0( RADEON_SE_LINE_WIDTH, 0 ) ); - OUT_RING( ctx->se_line_width ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_bumpmap( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 5 ); - - OUT_RING( CP_PACKET0( RADEON_PP_LUM_MATRIX, 0 ) ); - OUT_RING( ctx->pp_lum_matrix ); - - OUT_RING( CP_PACKET0( RADEON_PP_ROT_MATRIX_0, 1 ) ); - OUT_RING( ctx->pp_rot_matrix_0 ); - OUT_RING( ctx->pp_rot_matrix_1 ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_masks( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 4 ); - - OUT_RING( CP_PACKET0( RADEON_RB3D_STENCILREFMASK, 2 ) ); - OUT_RING( ctx->rb3d_stencilrefmask ); - OUT_RING( ctx->rb3d_ropcntl ); - OUT_RING( ctx->rb3d_planemask ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_viewport( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 7 ); - - OUT_RING( CP_PACKET0( RADEON_SE_VPORT_XSCALE, 5 ) ); - OUT_RING( ctx->se_vport_xscale ); - OUT_RING( ctx->se_vport_xoffset ); - OUT_RING( ctx->se_vport_yscale ); - OUT_RING( ctx->se_vport_yoffset ); - OUT_RING( ctx->se_vport_zscale ); - OUT_RING( ctx->se_vport_zoffset ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_setup( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 4 ); - - OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) ); - OUT_RING( ctx->se_cntl ); - OUT_RING( CP_PACKET0( RADEON_SE_CNTL_STATUS, 0 ) ); - OUT_RING( ctx->se_cntl_status ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_tcl( drm_radeon_private_t *dev_priv ) -{ -#ifdef TCL_ENABLE - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 29 ); - - OUT_RING( CP_PACKET0( RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED, 27 ) ); - OUT_RING( ctx->se_tcl_material_emmissive.red ); - OUT_RING( ctx->se_tcl_material_emmissive.green ); - OUT_RING( ctx->se_tcl_material_emmissive.blue ); - OUT_RING( ctx->se_tcl_material_emmissive.alpha ); - OUT_RING( ctx->se_tcl_material_ambient.red ); - OUT_RING( ctx->se_tcl_material_ambient.green ); - OUT_RING( ctx->se_tcl_material_ambient.blue ); - OUT_RING( ctx->se_tcl_material_ambient.alpha ); - OUT_RING( ctx->se_tcl_material_diffuse.red ); - OUT_RING( ctx->se_tcl_material_diffuse.green ); - OUT_RING( ctx->se_tcl_material_diffuse.blue ); - OUT_RING( ctx->se_tcl_material_diffuse.alpha ); - OUT_RING( ctx->se_tcl_material_specular.red ); - OUT_RING( ctx->se_tcl_material_specular.green ); - OUT_RING( ctx->se_tcl_material_specular.blue ); - OUT_RING( ctx->se_tcl_material_specular.alpha ); - OUT_RING( ctx->se_tcl_shininess ); - OUT_RING( ctx->se_tcl_output_vtx_fmt ); - OUT_RING( ctx->se_tcl_output_vtx_sel ); - OUT_RING( ctx->se_tcl_matrix_select_0 ); - OUT_RING( ctx->se_tcl_matrix_select_1 ); - OUT_RING( ctx->se_tcl_ucp_vert_blend_ctl ); - OUT_RING( ctx->se_tcl_texture_proc_ctl ); - OUT_RING( ctx->se_tcl_light_model_ctl ); - for ( i = 0 ; i < 4 ; i++ ) { - OUT_RING( ctx->se_tcl_per_light_ctl[i] ); - } - ADVANCE_RING(); -#else - DRM_ERROR( "TCL not enabled!\n" ); -#endif } -static inline void radeon_emit_misc( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_context_regs_t *ctx = &sarea_priv->context_state; - RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 2 ); - - OUT_RING( CP_PACKET0( RADEON_RE_MISC, 0 ) ); - OUT_RING( ctx->re_misc ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_tex0( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[0]; - RING_LOCALS; - DRM_DEBUG( " %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset ); - - BEGIN_RING( 9 ); - - OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_0, 5 ) ); - OUT_RING( tex->pp_txfilter ); - OUT_RING( tex->pp_txformat ); - OUT_RING( tex->pp_txoffset ); - OUT_RING( tex->pp_txcblend ); - OUT_RING( tex->pp_txablend ); - OUT_RING( tex->pp_tfactor ); - - OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_0, 0 ) ); - OUT_RING( tex->pp_border_color ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_tex1( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[1]; - RING_LOCALS; - DRM_DEBUG( " %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset ); - - BEGIN_RING( 9 ); - - OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_1, 5 ) ); - OUT_RING( tex->pp_txfilter ); - OUT_RING( tex->pp_txformat ); - OUT_RING( tex->pp_txoffset ); - OUT_RING( tex->pp_txcblend ); - OUT_RING( tex->pp_txablend ); - OUT_RING( tex->pp_tfactor ); - - OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_1, 0 ) ); - OUT_RING( tex->pp_border_color ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_tex2( drm_radeon_private_t *dev_priv ) +/* Emit 1.1 state + */ +static void radeon_emit_state( drm_radeon_private_t *dev_priv, + drm_radeon_context_regs_t *ctx, + drm_radeon_texture_regs_t *tex, + unsigned int dirty ) { - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[2]; RING_LOCALS; - DRM_DEBUG( " %s\n", __FUNCTION__ ); - - BEGIN_RING( 9 ); - - OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_2, 5 ) ); - OUT_RING( tex->pp_txfilter ); - OUT_RING( tex->pp_txformat ); - OUT_RING( tex->pp_txoffset ); - OUT_RING( tex->pp_txcblend ); - OUT_RING( tex->pp_txablend ); - OUT_RING( tex->pp_tfactor ); - - OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_2, 0 ) ); - OUT_RING( tex->pp_border_color ); - - ADVANCE_RING(); -} - -static inline void radeon_emit_state( drm_radeon_private_t *dev_priv ) -{ - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - unsigned int dirty = sarea_priv->dirty; - - DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty ); + DRM_DEBUG( "dirty=0x%08x\n", dirty ); if ( dirty & RADEON_UPLOAD_CONTEXT ) { - radeon_emit_context( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_CONTEXT; + BEGIN_RING( 14 ); + OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) ); + OUT_RING( ctx->pp_misc ); + OUT_RING( ctx->pp_fog_color ); + OUT_RING( ctx->re_solid_color ); + OUT_RING( ctx->rb3d_blendcntl ); + OUT_RING( ctx->rb3d_depthoffset ); + OUT_RING( ctx->rb3d_depthpitch ); + OUT_RING( ctx->rb3d_zstencilcntl ); + OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 2 ) ); + OUT_RING( ctx->pp_cntl ); + OUT_RING( ctx->rb3d_cntl ); + OUT_RING( ctx->rb3d_coloroffset ); + OUT_RING( CP_PACKET0( RADEON_RB3D_COLORPITCH, 0 ) ); + OUT_RING( ctx->rb3d_colorpitch ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_VERTFMT ) { - radeon_emit_vertfmt( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_VERTFMT; + BEGIN_RING( 2 ); + OUT_RING( CP_PACKET0( RADEON_SE_COORD_FMT, 0 ) ); + OUT_RING( ctx->se_coord_fmt ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_LINE ) { - radeon_emit_line( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_LINE; + BEGIN_RING( 5 ); + OUT_RING( CP_PACKET0( RADEON_RE_LINE_PATTERN, 1 ) ); + OUT_RING( ctx->re_line_pattern ); + OUT_RING( ctx->re_line_state ); + OUT_RING( CP_PACKET0( RADEON_SE_LINE_WIDTH, 0 ) ); + OUT_RING( ctx->se_line_width ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_BUMPMAP ) { - radeon_emit_bumpmap( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_BUMPMAP; + BEGIN_RING( 5 ); + OUT_RING( CP_PACKET0( RADEON_PP_LUM_MATRIX, 0 ) ); + OUT_RING( ctx->pp_lum_matrix ); + OUT_RING( CP_PACKET0( RADEON_PP_ROT_MATRIX_0, 1 ) ); + OUT_RING( ctx->pp_rot_matrix_0 ); + OUT_RING( ctx->pp_rot_matrix_1 ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_MASKS ) { - radeon_emit_masks( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_MASKS; + BEGIN_RING( 4 ); + OUT_RING( CP_PACKET0( RADEON_RB3D_STENCILREFMASK, 2 ) ); + OUT_RING( ctx->rb3d_stencilrefmask ); + OUT_RING( ctx->rb3d_ropcntl ); + OUT_RING( ctx->rb3d_planemask ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_VIEWPORT ) { - radeon_emit_viewport( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_VIEWPORT; + BEGIN_RING( 7 ); + OUT_RING( CP_PACKET0( RADEON_SE_VPORT_XSCALE, 5 ) ); + OUT_RING( ctx->se_vport_xscale ); + OUT_RING( ctx->se_vport_xoffset ); + OUT_RING( ctx->se_vport_yscale ); + OUT_RING( ctx->se_vport_yoffset ); + OUT_RING( ctx->se_vport_zscale ); + OUT_RING( ctx->se_vport_zoffset ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_SETUP ) { - radeon_emit_setup( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_SETUP; - } - - if ( dirty & RADEON_UPLOAD_TCL ) { -#ifdef TCL_ENABLE - radeon_emit_tcl( dev_priv ); -#endif - sarea_priv->dirty &= ~RADEON_UPLOAD_TCL; + BEGIN_RING( 4 ); + OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) ); + OUT_RING( ctx->se_cntl ); + OUT_RING( CP_PACKET0( RADEON_SE_CNTL_STATUS, 0 ) ); + OUT_RING( ctx->se_cntl_status ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_MISC ) { - radeon_emit_misc( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_MISC; + BEGIN_RING( 2 ); + OUT_RING( CP_PACKET0( RADEON_RE_MISC, 0 ) ); + OUT_RING( ctx->re_misc ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_TEX0 ) { - radeon_emit_tex0( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_TEX0; + BEGIN_RING( 9 ); + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_0, 5 ) ); + OUT_RING( tex[0].pp_txfilter ); + OUT_RING( tex[0].pp_txformat ); + OUT_RING( tex[0].pp_txoffset ); + OUT_RING( tex[0].pp_txcblend ); + OUT_RING( tex[0].pp_txablend ); + OUT_RING( tex[0].pp_tfactor ); + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_0, 0 ) ); + OUT_RING( tex[0].pp_border_color ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_TEX1 ) { - radeon_emit_tex1( dev_priv ); - sarea_priv->dirty &= ~RADEON_UPLOAD_TEX1; + BEGIN_RING( 9 ); + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_1, 5 ) ); + OUT_RING( tex[1].pp_txfilter ); + OUT_RING( tex[1].pp_txformat ); + OUT_RING( tex[1].pp_txoffset ); + OUT_RING( tex[1].pp_txcblend ); + OUT_RING( tex[1].pp_txablend ); + OUT_RING( tex[1].pp_tfactor ); + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_1, 0 ) ); + OUT_RING( tex[1].pp_border_color ); + ADVANCE_RING(); } if ( dirty & RADEON_UPLOAD_TEX2 ) { -#if 0 - radeon_emit_tex2( dev_priv ); -#endif - sarea_priv->dirty &= ~RADEON_UPLOAD_TEX2; + BEGIN_RING( 9 ); + OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_2, 5 ) ); + OUT_RING( tex[2].pp_txfilter ); + OUT_RING( tex[2].pp_txformat ); + OUT_RING( tex[2].pp_txoffset ); + OUT_RING( tex[2].pp_txcblend ); + OUT_RING( tex[2].pp_txablend ); + OUT_RING( tex[2].pp_tfactor ); + OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_2, 0 ) ); + OUT_RING( tex[2].pp_border_color ); + ADVANCE_RING(); } +} - sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | - RADEON_UPLOAD_TEX1IMAGES | - RADEON_UPLOAD_TEX2IMAGES | - RADEON_REQUIRE_QUIESCENCE); +/* Emit 1.2 state + */ +static void radeon_emit_state2( drm_radeon_private_t *dev_priv, + drm_radeon_state_t *state ) +{ + RING_LOCALS; + + if (state->dirty & RADEON_UPLOAD_ZBIAS) { + BEGIN_RING( 3 ); + OUT_RING( CP_PACKET0( RADEON_SE_ZBIAS_FACTOR, 1 ) ); + OUT_RING( state->context2.se_zbias_factor ); + OUT_RING( state->context2.se_zbias_constant ); + ADVANCE_RING(); + } + + radeon_emit_state( dev_priv, &state->context, + state->tex, state->dirty ); } +/* New (1.3) state mechanism. 3 commands (packet, scalar, vector) in + * 1.3 cmdbuffers allow all previous state to be updated as well as + * the tcl scalar and vector areas. + */ +static struct { + int start; + int len; + const char *name; +} packet[RADEON_MAX_STATE_PACKETS] = { + { RADEON_PP_MISC,7,"RADEON_PP_MISC" }, + { RADEON_PP_CNTL,3,"RADEON_PP_CNTL" }, + { RADEON_RB3D_COLORPITCH,1,"RADEON_RB3D_COLORPITCH" }, + { RADEON_RE_LINE_PATTERN,2,"RADEON_RE_LINE_PATTERN" }, + { RADEON_SE_LINE_WIDTH,1,"RADEON_SE_LINE_WIDTH" }, + { RADEON_PP_LUM_MATRIX,1,"RADEON_PP_LUM_MATRIX" }, + { RADEON_PP_ROT_MATRIX_0,2,"RADEON_PP_ROT_MATRIX_0" }, + { RADEON_RB3D_STENCILREFMASK,3,"RADEON_RB3D_STENCILREFMASK" }, + { RADEON_SE_VPORT_XSCALE,6,"RADEON_SE_VPORT_XSCALE" }, + { RADEON_SE_CNTL,2,"RADEON_SE_CNTL" }, + { RADEON_SE_CNTL_STATUS,1,"RADEON_SE_CNTL_STATUS" }, + { RADEON_RE_MISC,1,"RADEON_RE_MISC" }, + { RADEON_PP_TXFILTER_0,6,"RADEON_PP_TXFILTER_0" }, + { RADEON_PP_BORDER_COLOR_0,1,"RADEON_PP_BORDER_COLOR_0" }, + { RADEON_PP_TXFILTER_1,6,"RADEON_PP_TXFILTER_1" }, + { RADEON_PP_BORDER_COLOR_1,1,"RADEON_PP_BORDER_COLOR_1" }, + { RADEON_PP_TXFILTER_2,6,"RADEON_PP_TXFILTER_2" }, + { RADEON_PP_BORDER_COLOR_2,1,"RADEON_PP_BORDER_COLOR_2" }, + { RADEON_SE_ZBIAS_FACTOR,2,"RADEON_SE_ZBIAS_FACTOR" }, + { RADEON_SE_TCL_OUTPUT_VTX_FMT,11,"RADEON_SE_TCL_OUTPUT_VTX_FMT" }, + { RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED,17,"RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED" }, + { R200_PP_TXCBLEND_0, 4, "R200_PP_TXCBLEND_0" }, + { R200_PP_TXCBLEND_1, 4, "R200_PP_TXCBLEND_1" }, + { R200_PP_TXCBLEND_2, 4, "R200_PP_TXCBLEND_2" }, + { R200_PP_TXCBLEND_3, 4, "R200_PP_TXCBLEND_3" }, + { R200_PP_TXCBLEND_4, 4, "R200_PP_TXCBLEND_4" }, + { R200_PP_TXCBLEND_5, 4, "R200_PP_TXCBLEND_5" }, + { R200_PP_TXCBLEND_6, 4, "R200_PP_TXCBLEND_6" }, + { R200_PP_TXCBLEND_7, 4, "R200_PP_TXCBLEND_7" }, + { R200_SE_TCL_LIGHT_MODEL_CTL_0, 6, "R200_SE_TCL_LIGHT_MODEL_CTL_0" }, + { R200_PP_TFACTOR_0, 6, "R200_PP_TFACTOR_0" }, + { R200_SE_VTX_FMT_0, 4, "R200_SE_VTX_FMT_0" }, + { R200_SE_VAP_CNTL, 1, "R200_SE_VAP_CNTL" }, + { R200_SE_TCL_MATRIX_SEL_0, 5, "R200_SE_TCL_MATRIX_SEL_0" }, + { R200_SE_TCL_TEX_PROC_CTL_2, 5, "R200_SE_TCL_TEX_PROC_CTL_2" }, + { R200_SE_TCL_UCP_VERT_BLEND_CTL, 1, "R200_SE_TCL_UCP_VERT_BLEND_CTL" }, + { R200_PP_TXFILTER_0, 6, "R200_PP_TXFILTER_0" }, + { R200_PP_TXFILTER_1, 6, "R200_PP_TXFILTER_1" }, + { R200_PP_TXFILTER_2, 6, "R200_PP_TXFILTER_2" }, + { R200_PP_TXFILTER_3, 6, "R200_PP_TXFILTER_3" }, + { R200_PP_TXFILTER_4, 6, "R200_PP_TXFILTER_4" }, + { R200_PP_TXFILTER_5, 6, "R200_PP_TXFILTER_5" }, + { R200_PP_TXOFFSET_0, 1, "R200_PP_TXOFFSET_0" }, + { R200_PP_TXOFFSET_1, 1, "R200_PP_TXOFFSET_1" }, + { R200_PP_TXOFFSET_2, 1, "R200_PP_TXOFFSET_2" }, + { R200_PP_TXOFFSET_3, 1, "R200_PP_TXOFFSET_3" }, + { R200_PP_TXOFFSET_4, 1, "R200_PP_TXOFFSET_4" }, + { R200_PP_TXOFFSET_5, 1, "R200_PP_TXOFFSET_5" }, + { R200_SE_VTE_CNTL, 1, "R200_SE_VTE_CNTL" }, + { R200_SE_TCL_OUTPUT_VTX_COMP_SEL, 1, "R200_SE_TCL_OUTPUT_VTX_COMP_SEL" }, + { R200_PP_TAM_DEBUG3, 1, "R200_PP_TAM_DEBUG3" }, + { R200_PP_CNTL_X, 1, "R200_PP_CNTL_X" }, + { R200_RB3D_DEPTHXY_OFFSET, 1, "R200_RB3D_DEPTHXY_OFFSET" }, + { R200_RE_AUX_SCISSOR_CNTL, 1, "R200_RE_AUX_SCISSOR_CNTL" }, + { R200_RE_SCISSOR_TL_0, 2, "R200_RE_SCISSOR_TL_0" }, + { R200_RE_SCISSOR_TL_1, 2, "R200_RE_SCISSOR_TL_1" }, + { R200_RE_SCISSOR_TL_2, 2, "R200_RE_SCISSOR_TL_2" }, + { R200_SE_VAP_CNTL_STATUS, 1, "R200_SE_VAP_CNTL_STATUS" }, + { R200_SE_VTX_STATE_CNTL, 1, "R200_SE_VTX_STATE_CNTL" }, + { R200_RE_POINTSIZE, 1, "R200_RE_POINTSIZE" }, + { R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0, 4, "R200_SE_TCL_INPUT_VTX_VECTOR_ADDR_0" }, + { R200_PP_CUBIC_FACES_0, 1, "R200_PP_CUBIC_FACES_0" }, /* 61 */ + { R200_PP_CUBIC_OFFSET_F1_0, 5, "R200_PP_CUBIC_OFFSET_F1_0" }, /* 62 */ + { R200_PP_CUBIC_FACES_1, 1, "R200_PP_CUBIC_FACES_1" }, + { R200_PP_CUBIC_OFFSET_F1_1, 5, "R200_PP_CUBIC_OFFSET_F1_1" }, + { R200_PP_CUBIC_FACES_2, 1, "R200_PP_CUBIC_FACES_2" }, + { R200_PP_CUBIC_OFFSET_F1_2, 5, "R200_PP_CUBIC_OFFSET_F1_2" }, + { R200_PP_CUBIC_FACES_3, 1, "R200_PP_CUBIC_FACES_3" }, + { R200_PP_CUBIC_OFFSET_F1_3, 5, "R200_PP_CUBIC_OFFSET_F1_3" }, + { R200_PP_CUBIC_FACES_4, 1, "R200_PP_CUBIC_FACES_4" }, + { R200_PP_CUBIC_OFFSET_F1_4, 5, "R200_PP_CUBIC_OFFSET_F1_4" }, + { R200_PP_CUBIC_FACES_5, 1, "R200_PP_CUBIC_FACES_5" }, + { R200_PP_CUBIC_OFFSET_F1_5, 5, "R200_PP_CUBIC_OFFSET_F1_5" }, +}; + + -#if RADEON_PERFORMANCE_BOXES /* ================================================================ * Performance monitoring functions */ @@ -409,10 +304,12 @@ int x, int y, int w, int h, int r, int g, int b ) { - u32 pitch, offset; u32 color; RING_LOCALS; + x += dev_priv->sarea_priv->boxes[0].x1; + y += dev_priv->sarea_priv->boxes[0].y1; + switch ( dev_priv->color_fmt ) { case RADEON_COLOR_FORMAT_RGB565: color = (((r & 0xf8) << 8) | @@ -425,8 +322,11 @@ break; } - offset = dev_priv->back_offset; - pitch = dev_priv->back_pitch >> 3; + BEGIN_RING( 4 ); + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) ); + OUT_RING( 0xffffffff ); + ADVANCE_RING(); BEGIN_RING( 6 ); @@ -438,7 +338,12 @@ RADEON_ROP3_P | RADEON_GMC_CLR_CMP_CNTL_DIS ); - OUT_RING( (pitch << 22) | (offset >> 5) ); + if ( dev_priv->page_flipping && dev_priv->current_page == 1 ) { + OUT_RING( dev_priv->front_pitch_offset ); + } else { + OUT_RING( dev_priv->back_pitch_offset ); + } + OUT_RING( color ); OUT_RING( (x << 16) | y ); @@ -449,53 +354,77 @@ static void radeon_cp_performance_boxes( drm_radeon_private_t *dev_priv ) { - if ( atomic_read( &dev_priv->idle_count ) == 0 ) { - radeon_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 ); - } else { - atomic_set( &dev_priv->idle_count, 0 ); + /* Collapse various things into a wait flag -- trying to + * guess if userspase slept -- better just to have them tell us. + */ + if (dev_priv->stats.last_frame_reads > 1 || + dev_priv->stats.last_clear_reads > dev_priv->stats.clears) { + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; } -} -#endif + if (dev_priv->stats.freelist_loops) { + dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + } + + /* Purple box for page flipping + */ + if ( dev_priv->stats.boxes & RADEON_BOX_FLIP ) + radeon_clear_box( dev_priv, 4, 4, 8, 8, 255, 0, 255 ); + /* Red box if we have to wait for idle at any point + */ + if ( dev_priv->stats.boxes & RADEON_BOX_WAIT_IDLE ) + radeon_clear_box( dev_priv, 16, 4, 8, 8, 255, 0, 0 ); + /* Blue box: lost context? + */ + + /* Yellow box for texture swaps + */ + if ( dev_priv->stats.boxes & RADEON_BOX_TEXTURE_LOAD ) + radeon_clear_box( dev_priv, 40, 4, 8, 8, 255, 255, 0 ); + + /* Green box if hardware never idles (as far as we can tell) + */ + if ( !(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE) ) + radeon_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 ); + + + /* Draw bars indicating number of buffers allocated + * (not a great measure, easily confused) + */ + if (dev_priv->stats.requested_bufs) { + if (dev_priv->stats.requested_bufs > 100) + dev_priv->stats.requested_bufs = 100; + + radeon_clear_box( dev_priv, 4, 16, + dev_priv->stats.requested_bufs, 4, + 196, 128, 128 ); + } + + memset( &dev_priv->stats, 0, sizeof(dev_priv->stats) ); + +} /* ================================================================ * CP command dispatch functions */ -static void radeon_print_dirty( const char *msg, unsigned int flags ) -{ - DRM_DEBUG( "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - msg, - flags, - (flags & RADEON_UPLOAD_CONTEXT) ? "context, " : "", - (flags & RADEON_UPLOAD_VERTFMT) ? "vertfmt, " : "", - (flags & RADEON_UPLOAD_LINE) ? "line, " : "", - (flags & RADEON_UPLOAD_BUMPMAP) ? "bumpmap, " : "", - (flags & RADEON_UPLOAD_MASKS) ? "masks, " : "", - (flags & RADEON_UPLOAD_VIEWPORT) ? "viewport, " : "", - (flags & RADEON_UPLOAD_SETUP) ? "setup, " : "", - (flags & RADEON_UPLOAD_TCL) ? "tcl, " : "", - (flags & RADEON_UPLOAD_MISC) ? "misc, " : "", - (flags & RADEON_UPLOAD_TEX0) ? "tex0, " : "", - (flags & RADEON_UPLOAD_TEX1) ? "tex1, " : "", - (flags & RADEON_UPLOAD_TEX2) ? "tex2, " : "", - (flags & RADEON_UPLOAD_CLIPRECTS) ? "cliprects, " : "", - (flags & RADEON_REQUIRE_QUIESCENCE) ? "quiescence, " : "" ); -} - static void radeon_cp_dispatch_clear( drm_device_t *dev, drm_radeon_clear_t *clear, drm_radeon_clear_rect_t *depth_boxes ) { drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_depth_clear_t *depth_clear = &dev_priv->depth_clear; int nbox = sarea_priv->nbox; drm_clip_rect_t *pbox = sarea_priv->boxes; unsigned int flags = clear->flags; + u32 rb3d_cntl = 0, rb3d_stencilrefmask= 0; int i; RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "flags = 0x%x\n", flags ); + + dev_priv->stats.clears++; if ( dev_priv->page_flipping && dev_priv->current_page == 1 ) { unsigned int tmp = flags; @@ -505,127 +434,277 @@ if ( tmp & RADEON_BACK ) flags |= RADEON_FRONT; } - for ( i = 0 ; i < nbox ; i++ ) { - int x = pbox[i].x1; - int y = pbox[i].y1; - int w = pbox[i].x2 - x; - int h = pbox[i].y2 - y; + if ( flags & (RADEON_FRONT | RADEON_BACK) ) { - DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n", - x, y, w, h, flags ); + BEGIN_RING( 4 ); - if ( flags & (RADEON_FRONT | RADEON_BACK) ) { - BEGIN_RING( 4 ); + /* Ensure the 3D stream is idle before doing a + * 2D fill to clear the front or back buffer. + */ + RADEON_WAIT_UNTIL_3D_IDLE(); + + OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) ); + OUT_RING( clear->color_mask ); - /* Ensure the 3D stream is idle before doing a - * 2D fill to clear the front or back buffer. - */ - RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); - OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) ); - OUT_RING( clear->color_mask ); + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->ctx_owner = 0; - ADVANCE_RING(); + for ( i = 0 ; i < nbox ; i++ ) { + int x = pbox[i].x1; + int y = pbox[i].y1; + int w = pbox[i].x2 - x; + int h = pbox[i].y2 - y; + + DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n", + x, y, w, h, flags ); + + if ( flags & RADEON_FRONT ) { + BEGIN_RING( 6 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS ); + + OUT_RING( dev_priv->front_pitch_offset ); + OUT_RING( clear->clear_color ); + + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } + + if ( flags & RADEON_BACK ) { + BEGIN_RING( 6 ); + + OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); + OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | + RADEON_GMC_BRUSH_SOLID_COLOR | + (dev_priv->color_fmt << 8) | + RADEON_GMC_SRC_DATATYPE_COLOR | + RADEON_ROP3_P | + RADEON_GMC_CLR_CMP_CNTL_DIS ); + + OUT_RING( dev_priv->back_pitch_offset ); + OUT_RING( clear->clear_color ); - /* Make sure we restore the 3D state next time. - */ - dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | - RADEON_UPLOAD_MASKS); + OUT_RING( (x << 16) | y ); + OUT_RING( (w << 16) | h ); + + ADVANCE_RING(); + } } + } - if ( flags & RADEON_FRONT ) { - BEGIN_RING( 6 ); + /* We have to clear the depth and/or stencil buffers by + * rendering a quad into just those buffers. Thus, we have to + * make sure the 3D engine is configured correctly. + */ + if ( dev_priv->is_r200 && + (flags & (RADEON_DEPTH | RADEON_STENCIL)) ) { - OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); - OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | - RADEON_GMC_BRUSH_SOLID_COLOR | - (dev_priv->color_fmt << 8) | - RADEON_GMC_SRC_DATATYPE_COLOR | - RADEON_ROP3_P | - RADEON_GMC_CLR_CMP_CNTL_DIS ); + int tempPP_CNTL; + int tempRE_CNTL; + int tempRB3D_CNTL; + int tempRB3D_ZSTENCILCNTL; + int tempRB3D_STENCILREFMASK; + int tempRB3D_PLANEMASK; + int tempSE_CNTL; + int tempSE_VTE_CNTL; + int tempSE_VTX_FMT_0; + int tempSE_VTX_FMT_1; + int tempSE_VAP_CNTL; + int tempRE_AUX_SCISSOR_CNTL; - OUT_RING( dev_priv->front_pitch_offset ); - OUT_RING( clear->clear_color ); + tempPP_CNTL = 0; + tempRE_CNTL = 0; - OUT_RING( (x << 16) | y ); - OUT_RING( (w << 16) | h ); + tempRB3D_CNTL = depth_clear->rb3d_cntl; + tempRB3D_CNTL &= ~(1<<15); /* unset radeon magic flag */ - ADVANCE_RING(); + tempRB3D_ZSTENCILCNTL = depth_clear->rb3d_zstencilcntl; + tempRB3D_STENCILREFMASK = 0x0; + + tempSE_CNTL = depth_clear->se_cntl; + + + + /* Disable TCL */ + + tempSE_VAP_CNTL = (/* SE_VAP_CNTL__FORCE_W_TO_ONE_MASK | */ + (0x9 << SE_VAP_CNTL__VF_MAX_VTX_NUM__SHIFT)); + + tempRB3D_PLANEMASK = 0x0; + + tempRE_AUX_SCISSOR_CNTL = 0x0; + + tempSE_VTE_CNTL = + SE_VTE_CNTL__VTX_XY_FMT_MASK | + SE_VTE_CNTL__VTX_Z_FMT_MASK; + + /* Vertex format (X, Y, Z, W)*/ + tempSE_VTX_FMT_0 = + SE_VTX_FMT_0__VTX_Z0_PRESENT_MASK | + SE_VTX_FMT_0__VTX_W0_PRESENT_MASK; + tempSE_VTX_FMT_1 = 0x0; + + + /* + * Depth buffer specific enables + */ + if (flags & RADEON_DEPTH) { + /* Enable depth buffer */ + tempRB3D_CNTL |= RADEON_Z_ENABLE; + } else { + /* Disable depth buffer */ + tempRB3D_CNTL &= ~RADEON_Z_ENABLE; } - if ( flags & RADEON_BACK ) { - BEGIN_RING( 6 ); + /* + * Stencil buffer specific enables + */ + if ( flags & RADEON_STENCIL ) { + tempRB3D_CNTL |= RADEON_STENCIL_ENABLE; + tempRB3D_STENCILREFMASK = clear->depth_mask; + } else { + tempRB3D_CNTL &= ~RADEON_STENCIL_ENABLE; + tempRB3D_STENCILREFMASK = 0x00000000; + } - OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) ); - OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL | - RADEON_GMC_BRUSH_SOLID_COLOR | - (dev_priv->color_fmt << 8) | - RADEON_GMC_SRC_DATATYPE_COLOR | - RADEON_ROP3_P | - RADEON_GMC_CLR_CMP_CNTL_DIS ); + BEGIN_RING( 26 ); + RADEON_WAIT_UNTIL_2D_IDLE(); - OUT_RING( dev_priv->back_pitch_offset ); - OUT_RING( clear->clear_color ); + OUT_RING_REG( RADEON_PP_CNTL, tempPP_CNTL ); + OUT_RING_REG( R200_RE_CNTL, tempRE_CNTL ); + OUT_RING_REG( RADEON_RB3D_CNTL, tempRB3D_CNTL ); + OUT_RING_REG( RADEON_RB3D_ZSTENCILCNTL, + tempRB3D_ZSTENCILCNTL ); + OUT_RING_REG( RADEON_RB3D_STENCILREFMASK, + tempRB3D_STENCILREFMASK ); + OUT_RING_REG( RADEON_RB3D_PLANEMASK, tempRB3D_PLANEMASK ); + OUT_RING_REG( RADEON_SE_CNTL, tempSE_CNTL ); + OUT_RING_REG( R200_SE_VTE_CNTL, tempSE_VTE_CNTL ); + OUT_RING_REG( R200_SE_VTX_FMT_0, tempSE_VTX_FMT_0 ); + OUT_RING_REG( R200_SE_VTX_FMT_1, tempSE_VTX_FMT_1 ); + OUT_RING_REG( R200_SE_VAP_CNTL, tempSE_VAP_CNTL ); + OUT_RING_REG( R200_RE_AUX_SCISSOR_CNTL, + tempRE_AUX_SCISSOR_CNTL ); + ADVANCE_RING(); - OUT_RING( (x << 16) | y ); - OUT_RING( (w << 16) | h ); + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->ctx_owner = 0; - ADVANCE_RING(); + for ( i = 0 ; i < nbox ; i++ ) { + + /* Funny that this should be required -- + * sets top-left? + */ + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); + BEGIN_RING( 14 ); + OUT_RING( CP_PACKET3( R200_3D_DRAW_IMMD_2, 12 ) ); + OUT_RING( (RADEON_PRIM_TYPE_RECT_LIST | + RADEON_PRIM_WALK_RING | + (3 << RADEON_NUM_VERTICES_SHIFT)) ); + OUT_RING( depth_boxes[i].ui[CLEAR_X1] ); + OUT_RING( depth_boxes[i].ui[CLEAR_Y1] ); + OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x3f800000 ); + OUT_RING( depth_boxes[i].ui[CLEAR_X1] ); + OUT_RING( depth_boxes[i].ui[CLEAR_Y2] ); + OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x3f800000 ); + OUT_RING( depth_boxes[i].ui[CLEAR_X2] ); + OUT_RING( depth_boxes[i].ui[CLEAR_Y2] ); + OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x3f800000 ); + ADVANCE_RING(); } + } + else if ( (flags & (RADEON_DEPTH | RADEON_STENCIL)) ) { + + rb3d_cntl = depth_clear->rb3d_cntl; if ( flags & RADEON_DEPTH ) { - drm_radeon_depth_clear_t *depth_clear = - &dev_priv->depth_clear; + rb3d_cntl |= RADEON_Z_ENABLE; + } else { + rb3d_cntl &= ~RADEON_Z_ENABLE; + } - if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { - radeon_emit_state( dev_priv ); - } + if ( flags & RADEON_STENCIL ) { + rb3d_cntl |= RADEON_STENCIL_ENABLE; + rb3d_stencilrefmask = clear->depth_mask; /* misnamed field */ + } else { + rb3d_cntl &= ~RADEON_STENCIL_ENABLE; + rb3d_stencilrefmask = 0x00000000; + } - /* FIXME: Render a rectangle to clear the depth - * buffer. So much for those "fast Z clears"... - */ - BEGIN_RING( 23 ); + BEGIN_RING( 13 ); + RADEON_WAIT_UNTIL_2D_IDLE(); + + OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 1 ) ); + OUT_RING( 0x00000000 ); + OUT_RING( rb3d_cntl ); + + OUT_RING_REG( RADEON_RB3D_ZSTENCILCNTL, + depth_clear->rb3d_zstencilcntl ); + OUT_RING_REG( RADEON_RB3D_STENCILREFMASK, + rb3d_stencilrefmask ); + OUT_RING_REG( RADEON_RB3D_PLANEMASK, + 0x00000000 ); + OUT_RING_REG( RADEON_SE_CNTL, + depth_clear->se_cntl ); + ADVANCE_RING(); + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->ctx_owner = 0; - RADEON_WAIT_UNTIL_2D_IDLE(); + for ( i = 0 ; i < nbox ; i++ ) { + + /* Funny that this should be required -- + * sets top-left? + */ + radeon_emit_clip_rect( dev_priv, + &sarea_priv->boxes[i] ); - OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 1 ) ); - OUT_RING( 0x00000000 ); - OUT_RING( depth_clear->rb3d_cntl ); - OUT_RING( CP_PACKET0( RADEON_RB3D_ZSTENCILCNTL, 0 ) ); - OUT_RING( depth_clear->rb3d_zstencilcntl ); - OUT_RING( CP_PACKET0( RADEON_RB3D_PLANEMASK, 0 ) ); - OUT_RING( 0x00000000 ); - OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) ); - OUT_RING( depth_clear->se_cntl ); + BEGIN_RING( 15 ); - OUT_RING( CP_PACKET3( RADEON_3D_DRAW_IMMD, 10 ) ); - OUT_RING( RADEON_VTX_Z_PRESENT ); + OUT_RING( CP_PACKET3( RADEON_3D_DRAW_IMMD, 13 ) ); + OUT_RING( RADEON_VTX_Z_PRESENT | + RADEON_VTX_PKCOLOR_PRESENT); OUT_RING( (RADEON_PRIM_TYPE_RECT_LIST | RADEON_PRIM_WALK_RING | RADEON_MAOS_ENABLE | RADEON_VTX_FMT_RADEON_MODE | (3 << RADEON_NUM_VERTICES_SHIFT)) ); + OUT_RING( depth_boxes[i].ui[CLEAR_X1] ); OUT_RING( depth_boxes[i].ui[CLEAR_Y1] ); OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x0 ); OUT_RING( depth_boxes[i].ui[CLEAR_X1] ); OUT_RING( depth_boxes[i].ui[CLEAR_Y2] ); OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x0 ); OUT_RING( depth_boxes[i].ui[CLEAR_X2] ); OUT_RING( depth_boxes[i].ui[CLEAR_Y2] ); OUT_RING( depth_boxes[i].ui[CLEAR_DEPTH] ); + OUT_RING( 0x0 ); ADVANCE_RING(); - - /* Make sure we restore the 3D state next time. - */ - dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | - RADEON_UPLOAD_SETUP | - RADEON_UPLOAD_MASKS); } } @@ -651,13 +730,13 @@ drm_clip_rect_t *pbox = sarea_priv->boxes; int i; RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); -#if RADEON_PERFORMANCE_BOXES /* Do some trivial performance monitoring... */ - radeon_cp_performance_boxes( dev_priv ); -#endif + if (dev_priv->do_boxes) + radeon_cp_performance_boxes( dev_priv ); + /* Wait for the 3D stream to idle before dispatching the bitblt. * This will prevent data corruption between the two streams. @@ -689,9 +768,17 @@ RADEON_DP_SRC_SOURCE_MEMORY | RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS ); - - OUT_RING( dev_priv->back_pitch_offset ); - OUT_RING( dev_priv->front_pitch_offset ); + + /* Make this work even if front & back are flipped: + */ + if (dev_priv->current_page == 0) { + OUT_RING( dev_priv->back_pitch_offset ); + OUT_RING( dev_priv->front_pitch_offset ); + } + else { + OUT_RING( dev_priv->front_pitch_offset ); + OUT_RING( dev_priv->back_pitch_offset ); + } OUT_RING( (x << 16) | y ); OUT_RING( (x << 16) | y ); @@ -717,29 +804,33 @@ static void radeon_cp_dispatch_flip( drm_device_t *dev ) { drm_radeon_private_t *dev_priv = dev->dev_private; - RING_LOCALS; - DRM_DEBUG( "%s: page=%d\n", __FUNCTION__, dev_priv->current_page ); + drm_sarea_t *sarea = (drm_sarea_t *)dev_priv->sarea->handle; + int offset = (dev_priv->current_page == 1) + ? dev_priv->front_offset : dev_priv->back_offset; + RING_LOCALS; + DRM_DEBUG( "%s: page=%d pfCurrentPage=%d\n", + __FUNCTION__, + dev_priv->current_page, + dev_priv->sarea_priv->pfCurrentPage); -#if RADEON_PERFORMANCE_BOXES /* Do some trivial performance monitoring... */ - radeon_cp_performance_boxes( dev_priv ); -#endif + if (dev_priv->do_boxes) { + dev_priv->stats.boxes |= RADEON_BOX_FLIP; + radeon_cp_performance_boxes( dev_priv ); + } + /* Update the frame offsets for both CRTCs + */ BEGIN_RING( 6 ); RADEON_WAIT_UNTIL_3D_IDLE(); - RADEON_WAIT_UNTIL_PAGE_FLIPPED(); - - OUT_RING( CP_PACKET0( RADEON_CRTC_OFFSET, 0 ) ); - - if ( dev_priv->current_page == 0 ) { - OUT_RING( dev_priv->back_offset ); - dev_priv->current_page = 1; - } else { - OUT_RING( dev_priv->front_offset ); - dev_priv->current_page = 0; - } + OUT_RING_REG( RADEON_CRTC_OFFSET, ( ( sarea->frame.y * dev_priv->front_pitch + + sarea->frame.x + * ( dev_priv->color_fmt - 2 ) ) & ~7 ) + + offset ); + OUT_RING_REG( RADEON_CRTC2_OFFSET, dev_priv->sarea_priv->crtc2_base + + offset ); ADVANCE_RING(); @@ -748,6 +839,8 @@ * performing the swapbuffer ioctl. */ dev_priv->sarea_priv->last_frame++; + dev_priv->sarea_priv->pfCurrentPage = dev_priv->current_page = + 1 - dev_priv->current_page; BEGIN_RING( 2 ); @@ -756,82 +849,118 @@ ADVANCE_RING(); } +static int bad_prim_vertex_nr( int primitive, int nr ) +{ + switch (primitive & RADEON_PRIM_TYPE_MASK) { + case RADEON_PRIM_TYPE_NONE: + case RADEON_PRIM_TYPE_POINT: + return nr < 1; + case RADEON_PRIM_TYPE_LINE: + return (nr & 1) || nr == 0; + case RADEON_PRIM_TYPE_LINE_STRIP: + return nr < 2; + case RADEON_PRIM_TYPE_TRI_LIST: + case RADEON_PRIM_TYPE_3VRT_POINT_LIST: + case RADEON_PRIM_TYPE_3VRT_LINE_LIST: + case RADEON_PRIM_TYPE_RECT_LIST: + return nr % 3 || nr == 0; + case RADEON_PRIM_TYPE_TRI_FAN: + case RADEON_PRIM_TYPE_TRI_STRIP: + return nr < 3; + default: + return 1; + } +} + + + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim; + unsigned int numverts; + unsigned int offset; + unsigned int vc_format; +} drm_radeon_tcl_prim_t; + static void radeon_cp_dispatch_vertex( drm_device_t *dev, - drm_buf_t *buf ) + drm_buf_t *buf, + drm_radeon_tcl_prim_t *prim, + drm_clip_rect_t *boxes, + int nbox ) + { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_buf_priv_t *buf_priv = buf->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - int format = sarea_priv->vc_format; - int offset = dev_priv->agp_buffers_offset + buf->offset; - int size = buf->used; - int prim = buf_priv->prim; + drm_clip_rect_t box; + int offset = dev_priv->agp_buffers_offset + buf->offset + prim->start; + int numverts = (int)prim->numverts; int i = 0; RING_LOCALS; - DRM_DEBUG( "%s: nbox=%d\n", __FUNCTION__, sarea_priv->nbox ); - if ( 0 ) - radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty ); + DRM_DEBUG("hwprim 0x%x vfmt 0x%x %d..%d %d verts\n", + prim->prim, + prim->vc_format, + prim->start, + prim->finish, + prim->numverts); + + if (bad_prim_vertex_nr( prim->prim, prim->numverts )) { + DRM_ERROR( "bad prim %x numverts %d\n", + prim->prim, prim->numverts ); + return; + } + + do { + /* Emit the next cliprect */ + if ( i < nbox ) { + if (__copy_from_user( &box, &boxes[i], sizeof(box) )) + return; - if ( buf->used ) { - buf_priv->dispatched = 1; - - if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { - radeon_emit_state( dev_priv ); + radeon_emit_clip_rect( dev_priv, &box ); } - do { - /* Emit the next set of up to three cliprects */ - if ( i < sarea_priv->nbox ) { - radeon_emit_clip_rect( dev_priv, - &sarea_priv->boxes[i] ); - } + /* Emit the vertex buffer rendering commands */ + BEGIN_RING( 5 ); - /* Emit the vertex buffer rendering commands */ - BEGIN_RING( 5 ); + OUT_RING( CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, 3 ) ); + OUT_RING( offset ); + OUT_RING( numverts ); + OUT_RING( prim->vc_format ); + OUT_RING( prim->prim | RADEON_PRIM_WALK_LIST | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (numverts << RADEON_NUM_VERTICES_SHIFT) ); - OUT_RING( CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, 3 ) ); - OUT_RING( offset ); - OUT_RING( size ); - OUT_RING( format ); - OUT_RING( prim | RADEON_PRIM_WALK_LIST | - RADEON_COLOR_ORDER_RGBA | - RADEON_VTX_FMT_RADEON_MODE | - (size << RADEON_NUM_VERTICES_SHIFT) ); + ADVANCE_RING(); - ADVANCE_RING(); + i++; + } while ( i < nbox ); +} - i++; - } while ( i < sarea_priv->nbox ); - } - if ( buf_priv->discard ) { - buf_priv->age = dev_priv->sarea_priv->last_dispatch; - /* Emit the vertex buffer age */ - BEGIN_RING( 2 ); - RADEON_DISPATCH_AGE( buf_priv->age ); - ADVANCE_RING(); +static void radeon_cp_discard_buffer( drm_device_t *dev, drm_buf_t *buf ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_buf_priv_t *buf_priv = buf->dev_private; + RING_LOCALS; - buf->pending = 1; - buf->used = 0; - /* FIXME: Check dispatched field */ - buf_priv->dispatched = 0; - } + buf_priv->age = ++dev_priv->sarea_priv->last_dispatch; - dev_priv->sarea_priv->last_dispatch++; + /* Emit the vertex buffer age */ + BEGIN_RING( 2 ); + RADEON_DISPATCH_AGE( buf_priv->age ); + ADVANCE_RING(); - sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS; - sarea_priv->nbox = 0; + buf->pending = 1; + buf->used = 0; } - static void radeon_cp_dispatch_indirect( drm_device_t *dev, drm_buf_t *buf, int start, int end ) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_buf_priv_t *buf_priv = buf->dev_private; RING_LOCALS; DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n", buf->idx, start, end ); @@ -852,8 +981,6 @@ data[dwords++] = RADEON_CP_PACKET2; } - buf_priv->dispatched = 1; - /* Fire off the indirect buffer */ BEGIN_RING( 3 ); @@ -863,100 +990,75 @@ ADVANCE_RING(); } - - if ( buf_priv->discard ) { - buf_priv->age = dev_priv->sarea_priv->last_dispatch; - - /* Emit the indirect buffer age */ - BEGIN_RING( 2 ); - RADEON_DISPATCH_AGE( buf_priv->age ); - ADVANCE_RING(); - - buf->pending = 1; - buf->used = 0; - /* FIXME: Check dispatched field */ - buf_priv->dispatched = 0; - } - - dev_priv->sarea_priv->last_dispatch++; } + static void radeon_cp_dispatch_indices( drm_device_t *dev, - drm_buf_t *buf, - int start, int end, - int count ) + drm_buf_t *elt_buf, + drm_radeon_tcl_prim_t *prim, + drm_clip_rect_t *boxes, + int nbox ) { drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_buf_priv_t *buf_priv = buf->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - int format = sarea_priv->vc_format; - int offset = dev_priv->agp_buffers_offset; - int prim = buf_priv->prim; + drm_clip_rect_t box; + int offset = dev_priv->agp_buffers_offset + prim->offset; u32 *data; int dwords; int i = 0; - RING_LOCALS; - DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count ); - - if ( 0 ) - radeon_print_dirty( "dispatch_indices", sarea_priv->dirty ); - - if ( start != end ) { - buf_priv->dispatched = 1; - - if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { - radeon_emit_state( dev_priv ); - } - - dwords = (end - start + 3) / sizeof(u32); - - data = (u32 *)((char *)dev_priv->buffers->handle - + buf->offset + start); + int start = prim->start + RADEON_INDEX_PRIM_OFFSET; + int count = (prim->finish - start) / sizeof(u16); - data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 ); - - data[1] = offset; - data[2] = RADEON_MAX_VB_VERTS; - data[3] = format; - data[4] = (prim | RADEON_PRIM_WALK_IND | - RADEON_COLOR_ORDER_RGBA | - RADEON_VTX_FMT_RADEON_MODE | - (count << RADEON_NUM_VERTICES_SHIFT) ); - - if ( count & 0x1 ) { - data[dwords-1] &= 0x0000ffff; - } - - do { - /* Emit the next set of up to three cliprects */ - if ( i < sarea_priv->nbox ) { - radeon_emit_clip_rect( dev_priv, - &sarea_priv->boxes[i] ); - } - - radeon_cp_dispatch_indirect( dev, buf, start, end ); - - i++; - } while ( i < sarea_priv->nbox ); + DRM_DEBUG("hwprim 0x%x vfmt 0x%x %d..%d offset: %x nr %d\n", + prim->prim, + prim->vc_format, + prim->start, + prim->finish, + prim->offset, + prim->numverts); + + if (bad_prim_vertex_nr( prim->prim, count )) { + DRM_ERROR( "bad prim %x count %d\n", + prim->prim, count ); + return; } - if ( buf_priv->discard ) { - buf_priv->age = dev_priv->sarea_priv->last_dispatch; - /* Emit the vertex buffer age */ - BEGIN_RING( 2 ); - RADEON_DISPATCH_AGE( buf_priv->age ); - ADVANCE_RING(); + if ( start >= prim->finish || + (prim->start & 0x7) ) { + DRM_ERROR( "buffer prim %d\n", prim->prim ); + return; + } + + dwords = (prim->finish - prim->start + 3) / sizeof(u32); + + data = (u32 *)((char *)dev_priv->buffers->handle + + elt_buf->offset + prim->start); + + data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 ); + data[1] = offset; + data[2] = prim->numverts; + data[3] = prim->vc_format; + data[4] = (prim->prim | + RADEON_PRIM_WALK_IND | + RADEON_COLOR_ORDER_RGBA | + RADEON_VTX_FMT_RADEON_MODE | + (count << RADEON_NUM_VERTICES_SHIFT) ); + + do { + if ( i < nbox ) { + if (__copy_from_user( &box, &boxes[i], sizeof(box) )) + return; + + radeon_emit_clip_rect( dev_priv, &box ); + } - buf->pending = 1; - /* FIXME: Check dispatched field */ - buf_priv->dispatched = 0; - } + radeon_cp_dispatch_indirect( dev, elt_buf, + prim->start, + prim->finish ); - dev_priv->sarea_priv->last_dispatch++; + i++; + } while ( i < nbox ); - sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS; - sarea_priv->nbox = 0; } #define RADEON_MAX_TEXTURE_SIZE (RADEON_BUFFER_SIZE - 8 * sizeof(u32)) @@ -967,25 +1069,35 @@ { drm_radeon_private_t *dev_priv = dev->dev_private; drm_buf_t *buf; - drm_radeon_buf_priv_t *buf_priv; u32 format; u32 *buffer; - u8 *data; + const u8 *data; int size, dwords, tex_width, blit_width; - u32 y, height; - int ret = 0, i; + u32 height; + int i; RING_LOCALS; - /* FIXME: Be smarter about this... + dev_priv->stats.boxes |= RADEON_BOX_TEXTURE_LOAD; + + /* Flush the pixel cache. This ensures no pixel data gets mixed + * up with the texture data from the host data blit, otherwise + * part of the texture image may be corrupted. */ - buf = radeon_freelist_get( dev ); - if ( !buf ) return -EAGAIN; + BEGIN_RING( 4 ); + RADEON_FLUSH_CACHE(); + RADEON_WAIT_UNTIL_IDLE(); + ADVANCE_RING(); - DRM_DEBUG( "tex: ofs=0x%x p=%d f=%d x=%hd y=%hd w=%hd h=%hd\n", - tex->offset >> 10, tex->pitch, tex->format, - image->x, image->y, image->width, image->height ); +#ifdef __BIG_ENDIAN + /* The Mesa texture functions provide the data in little endian as the + * chip wants it, but we need to compensate for the fact that the CP + * ring gets byte-swapped + */ + BEGIN_RING( 2 ); + OUT_RING_REG( RADEON_RBBM_GUICNTL, RADEON_HOST_DATA_SWAP_32BIT ); + ADVANCE_RING(); +#endif - buf_priv = buf->dev_private; /* The compiler won't optimize away a division by a variable, * even if the only legal values are powers of two. Thus, we'll @@ -1002,6 +1114,8 @@ case RADEON_TXFORMAT_ARGB1555: case RADEON_TXFORMAT_RGB565: case RADEON_TXFORMAT_ARGB4444: + case RADEON_TXFORMAT_VYUY422: + case RADEON_TXFORMAT_YVYU422: format = RADEON_COLOR_FORMAT_RGB565; tex_width = tex->width * 2; blit_width = image->width * 2; @@ -1017,56 +1131,46 @@ return -EINVAL; } - DRM_DEBUG( " tex=%dx%d blit=%d\n", - tex_width, tex->height, blit_width ); - - /* Flush the pixel cache. This ensures no pixel data gets mixed - * up with the texture data from the host data blit, otherwise - * part of the texture image may be corrupted. - */ - BEGIN_RING( 4 ); - - RADEON_FLUSH_CACHE(); - RADEON_WAIT_UNTIL_IDLE(); + DRM_DEBUG("tex=%dx%d blit=%d\n", tex_width, tex->height, blit_width ); - ADVANCE_RING(); + do { + DRM_DEBUG( "tex: ofs=0x%x p=%d f=%d x=%hd y=%hd w=%hd h=%hd\n", + tex->offset >> 10, tex->pitch, tex->format, + image->x, image->y, image->width, image->height ); - /* Make a copy of the parameters in case we have to update them - * for a multi-pass texture blit. + /* Make a copy of some parameters in case we have to + * update them for a multi-pass texture blit. */ - y = image->y; height = image->height; - data = (u8 *)image->data; + data = (const u8 *)image->data; size = height * blit_width; if ( size > RADEON_MAX_TEXTURE_SIZE ) { - /* Texture image is too large, do a multipass upload */ - ret = -EAGAIN; - - /* Adjust the blit size to fit the indirect buffer */ height = RADEON_MAX_TEXTURE_SIZE / blit_width; size = height * blit_width; - - /* Update the input parameters for next time */ - image->y += height; - image->height -= height; - image->data = (char *)image->data + size; - - if ( copy_to_user( tex->image, image, sizeof(*image) ) ) { - DRM_ERROR( "EFAULT on tex->image\n" ); - return -EFAULT; - } } else if ( size < 4 && size > 0 ) { size = 4; + } else if ( size == 0 ) { + return 0; + } + + buf = radeon_freelist_get( dev ); + if ( 0 && !buf ) { + radeon_do_cp_idle( dev_priv ); + buf = radeon_freelist_get( dev ); + } + if ( !buf ) { + DRM_DEBUG("radeon_cp_dispatch_texture: EAGAIN\n"); + copy_to_user( tex->image, image, sizeof(*image) ); + return -EAGAIN; } - dwords = size / 4; /* Dispatch the indirect buffer. */ - buffer = (u32 *)((char *)dev_priv->buffers->handle + buf->offset); - + buffer = (u32*)((char*)dev_priv->buffers->handle + buf->offset); + dwords = size / 4; buffer[0] = CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 ); buffer[1] = (RADEON_GMC_DST_PITCH_OFFSET_CNTL | RADEON_GMC_BRUSH_NONE | @@ -1080,7 +1184,7 @@ buffer[2] = (tex->pitch << 22) | (tex->offset >> 10); buffer[3] = 0xffffffff; buffer[4] = 0xffffffff; - buffer[5] = (y << 16) | image->x; + buffer[5] = (image->y << 16) | image->x; buffer[6] = (height << 16) | image->width; buffer[7] = dwords; @@ -1112,30 +1216,34 @@ buf->pid = current->pid; buf->used = (dwords + 8) * sizeof(u32); - buf_priv->discard = 1; radeon_cp_dispatch_indirect( dev, buf, 0, buf->used ); + radeon_cp_discard_buffer( dev, buf ); + + /* Update the input parameters for next time */ + image->y += height; + image->height -= height; + (const u8 *)image->data += size; + } while (image->height > 0); /* Flush the pixel cache after the blit completes. This ensures * the texture data is written out to memory before rendering * continues. */ BEGIN_RING( 4 ); - RADEON_FLUSH_CACHE(); RADEON_WAIT_UNTIL_2D_IDLE(); - ADVANCE_RING(); - - return ret; + return 0; } + static void radeon_cp_dispatch_stipple( drm_device_t *dev, u32 *stipple ) { drm_radeon_private_t *dev_priv = dev->dev_private; int i; RING_LOCALS; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); BEGIN_RING( 35 ); @@ -1158,31 +1266,95 @@ int radeon_cp_clear( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_radeon_clear_t clear; + drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS]; + DRM_DEBUG( "\n" ); + + LOCK_TEST_WITH_RETURN( dev ); + + if ( copy_from_user( &clear, (drm_radeon_clear_t *)arg, + sizeof(clear) ) ) + return -EFAULT; + + RING_SPACE_TEST_WITH_RETURN( dev_priv ); + + if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS ) + sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; + + if ( copy_from_user( &depth_boxes, clear.depth_boxes, + sarea_priv->nbox * sizeof(depth_boxes[0]) ) ) + return -EFAULT; + + radeon_cp_dispatch_clear( dev, &clear, depth_boxes ); + + COMMIT_RING(); + return 0; +} + + +/* Not sure why this isn't set all the time: + */ +static int radeon_do_init_pageflip( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG( "\n" ); + + BEGIN_RING( 6 ); + RADEON_WAIT_UNTIL_3D_IDLE(); + OUT_RING( CP_PACKET0( RADEON_CRTC_OFFSET_CNTL, 0 ) ); + OUT_RING( RADEON_READ( RADEON_CRTC_OFFSET_CNTL ) | RADEON_CRTC_OFFSET_FLIP_CNTL ); + OUT_RING( CP_PACKET0( RADEON_CRTC2_OFFSET_CNTL, 0 ) ); + OUT_RING( RADEON_READ( RADEON_CRTC2_OFFSET_CNTL ) | RADEON_CRTC_OFFSET_FLIP_CNTL ); + ADVANCE_RING(); + + dev_priv->page_flipping = 1; + dev_priv->current_page = 0; + dev_priv->sarea_priv->pfCurrentPage = dev_priv->current_page; + + return 0; +} + +/* Called whenever a client dies, from DRM(release). + * NOTE: Lock isn't necessarily held when this is called! + */ +int radeon_do_cleanup_pageflip( drm_device_t *dev ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + DRM_DEBUG( "\n" ); + + if (dev_priv->current_page != 0) + radeon_cp_dispatch_flip( dev ); + + dev_priv->page_flipping = 0; + return 0; +} + +/* Swapping and flipping are different operations, need different ioctls. + * They can & should be intermixed to support multiple 3d windows. + */ +int radeon_cp_flip(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; drm_radeon_private_t *dev_priv = dev->dev_private; - drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - drm_radeon_clear_t clear; - drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS]; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); LOCK_TEST_WITH_RETURN( dev ); - if ( copy_from_user( &clear, (drm_radeon_clear_t *)arg, - sizeof(clear) ) ) - return -EFAULT; - RING_SPACE_TEST_WITH_RETURN( dev_priv ); - if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS ) - sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; - - if ( copy_from_user( &depth_boxes, clear.depth_boxes, - sarea_priv->nbox * sizeof(depth_boxes[0]) ) ) - return -EFAULT; - - radeon_cp_dispatch_clear( dev, &clear, depth_boxes ); + if (!dev_priv->page_flipping) + radeon_do_init_pageflip( dev ); + + radeon_cp_dispatch_flip( dev ); + COMMIT_RING(); return 0; } @@ -1193,7 +1365,7 @@ drm_device_t *dev = priv->dev; drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; - DRM_DEBUG( "%s\n", __FUNCTION__ ); + DRM_DEBUG( "\n" ); LOCK_TEST_WITH_RETURN( dev ); @@ -1202,14 +1374,10 @@ if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS ) sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS; - if ( !dev_priv->page_flipping ) { - radeon_cp_dispatch_swap( dev ); - dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT | - RADEON_UPLOAD_MASKS); - } else { - radeon_cp_dispatch_flip( dev ); - } + radeon_cp_dispatch_swap( dev ); + dev_priv->sarea_priv->ctx_owner = 0; + COMMIT_RING(); return 0; } @@ -1219,10 +1387,11 @@ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; - drm_radeon_buf_priv_t *buf_priv; drm_radeon_vertex_t vertex; + drm_radeon_tcl_prim_t prim; LOCK_TEST_WITH_RETURN( dev ); @@ -1235,8 +1404,8 @@ sizeof(vertex) ) ) return -EFAULT; - DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n", - __FUNCTION__, current->pid, + DRM_DEBUG( "pid=%d index=%d count=%d discard=%d\n", + current->pid, vertex.idx, vertex.count, vertex.discard ); if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) { @@ -1254,7 +1423,6 @@ VB_AGE_TEST_WITH_RETURN( dev_priv ); buf = dma->buflist[vertex.idx]; - buf_priv = buf->dev_private; if ( buf->pid != current->pid ) { DRM_ERROR( "process %d using buffer owned by %d\n", @@ -1266,12 +1434,39 @@ return -EINVAL; } - buf->used = vertex.count; - buf_priv->prim = vertex.prim; - buf_priv->discard = vertex.discard; + /* Build up a prim_t record: + */ + if (vertex.count) { + buf->used = vertex.count; /* not used? */ + + if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { + radeon_emit_state( dev_priv, + &sarea_priv->context_state, + sarea_priv->tex_state, + sarea_priv->dirty ); + + sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | + RADEON_UPLOAD_TEX1IMAGES | + RADEON_UPLOAD_TEX2IMAGES | + RADEON_REQUIRE_QUIESCENCE); + } + + prim.start = 0; + prim.finish = vertex.count; /* unused */ + prim.prim = vertex.prim; + prim.numverts = vertex.count; + prim.vc_format = dev_priv->sarea_priv->vc_format; + + radeon_cp_dispatch_vertex( dev, buf, &prim, + dev_priv->sarea_priv->boxes, + dev_priv->sarea_priv->nbox ); + } - radeon_cp_dispatch_vertex( dev, buf ); + if (vertex.discard) { + radeon_cp_discard_buffer( dev, buf ); + } + COMMIT_RING(); return 0; } @@ -1281,10 +1476,11 @@ drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; - drm_radeon_buf_priv_t *buf_priv; drm_radeon_indices_t elts; + drm_radeon_tcl_prim_t prim; int count; LOCK_TEST_WITH_RETURN( dev ); @@ -1317,7 +1513,6 @@ VB_AGE_TEST_WITH_RETURN( dev_priv ); buf = dma->buflist[elts.idx]; - buf_priv = buf->dev_private; if ( buf->pid != current->pid ) { DRM_ERROR( "process %d using buffer owned by %d\n", @@ -1342,11 +1537,37 @@ } buf->used = elts.end; - buf_priv->prim = elts.prim; - buf_priv->discard = elts.discard; - radeon_cp_dispatch_indices( dev, buf, elts.start, elts.end, count ); + if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) { + radeon_emit_state( dev_priv, + &sarea_priv->context_state, + sarea_priv->tex_state, + sarea_priv->dirty ); + + sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES | + RADEON_UPLOAD_TEX1IMAGES | + RADEON_UPLOAD_TEX2IMAGES | + RADEON_REQUIRE_QUIESCENCE); + } + + + /* Build up a prim_t record: + */ + prim.start = elts.start; + prim.finish = elts.end; + prim.prim = elts.prim; + prim.offset = 0; /* offset from start of dma buffers */ + prim.numverts = RADEON_MAX_VB_VERTS; /* duh */ + prim.vc_format = dev_priv->sarea_priv->vc_format; + + radeon_cp_dispatch_indices( dev, buf, &prim, + dev_priv->sarea_priv->boxes, + dev_priv->sarea_priv->nbox ); + if (elts.discard) { + radeon_cp_discard_buffer( dev, buf ); + } + COMMIT_RING(); return 0; } @@ -1358,6 +1579,7 @@ drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_texture_t tex; drm_radeon_tex_image_t image; + int ret; LOCK_TEST_WITH_RETURN( dev ); @@ -1377,7 +1599,10 @@ RING_SPACE_TEST_WITH_RETURN( dev_priv ); VB_AGE_TEST_WITH_RETURN( dev_priv ); - return radeon_cp_dispatch_texture( dev, &tex, &image ); + ret = radeon_cp_dispatch_texture( dev, &tex, &image ); + + COMMIT_RING(); + return ret; } int radeon_cp_stipple( struct inode *inode, struct file *filp, @@ -1402,6 +1627,7 @@ radeon_cp_dispatch_stipple( dev, mask ); + COMMIT_RING(); return 0; } @@ -1413,7 +1639,6 @@ drm_radeon_private_t *dev_priv = dev->dev_private; drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; - drm_radeon_buf_priv_t *buf_priv; drm_radeon_indirect_t indirect; RING_LOCALS; @@ -1439,7 +1664,6 @@ } buf = dma->buflist[indirect.idx]; - buf_priv = buf->dev_private; if ( buf->pid != current->pid ) { DRM_ERROR( "process %d using buffer owned by %d\n", @@ -1461,7 +1685,6 @@ VB_AGE_TEST_WITH_RETURN( dev_priv ); buf->used = indirect.end; - buf_priv->discard = indirect.discard; /* Wait for the 3D stream to idle before the indirect buffer * containing 2D acceleration commands is processed. @@ -1477,6 +1700,526 @@ * privileged clients. */ radeon_cp_dispatch_indirect( dev, buf, indirect.start, indirect.end ); + if (indirect.discard) { + radeon_cp_discard_buffer( dev, buf ); + } + + + COMMIT_RING(); + return 0; +} + +int radeon_cp_vertex2(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_radeon_vertex2_t vertex; + int i; + unsigned char laststate; + + LOCK_TEST_WITH_RETURN( dev ); + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( vertex, (drm_radeon_vertex2_t *)data, + sizeof(vertex) ); + + DRM_DEBUG( "pid=%d index=%d discard=%d\n", + current->pid, + vertex.idx, vertex.discard ); + + if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + vertex.idx, dma->buf_count - 1 ); + return -EINVAL; + } + + RING_SPACE_TEST_WITH_RETURN( dev_priv ); + VB_AGE_TEST_WITH_RETURN( dev_priv ); + + buf = dma->buflist[vertex.idx]; + + if ( buf->pid != current->pid ) { + DRM_ERROR( "process %d using buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + + if ( buf->pending ) { + DRM_ERROR( "sending pending buffer %d\n", vertex.idx ); + return -EINVAL; + } + + if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS) + return -EINVAL; + + for (laststate = 0xff, i = 0 ; i < vertex.nr_prims ; i++) { + drm_radeon_prim_t prim; + drm_radeon_tcl_prim_t tclprim; + + if ( copy_from_user( &prim, &vertex.prim[i], sizeof(prim) ) ) + return -EFAULT; + + if ( prim.stateidx != laststate ) { + drm_radeon_state_t state; + + if ( copy_from_user( &state, + &vertex.state[prim.stateidx], + sizeof(state) ) ) + return -EFAULT; + + radeon_emit_state2( dev_priv, &state ); + + laststate = prim.stateidx; + } + + tclprim.start = prim.start; + tclprim.finish = prim.finish; + tclprim.prim = prim.prim; + tclprim.vc_format = prim.vc_format; + + if ( prim.prim & RADEON_PRIM_WALK_IND ) { + tclprim.offset = prim.numverts * 64; + tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */ + + radeon_cp_dispatch_indices( dev, buf, &tclprim, + sarea_priv->boxes, + sarea_priv->nbox); + } else { + tclprim.numverts = prim.numverts; + tclprim.offset = 0; /* not used */ + + radeon_cp_dispatch_vertex( dev, buf, &tclprim, + sarea_priv->boxes, + sarea_priv->nbox); + } + + if (sarea_priv->nbox == 1) + sarea_priv->nbox = 0; + } + + if ( vertex.discard ) { + radeon_cp_discard_buffer( dev, buf ); + } + + COMMIT_RING(); + return 0; +} + + +static int radeon_emit_packets( + drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_cmd_buffer_t *cmdbuf ) +{ + int id = (int)header.packet.packet_id; + int sz, reg; + int *data = (int *)cmdbuf->buf; + RING_LOCALS; + + if (id >= RADEON_MAX_STATE_PACKETS) + return -EINVAL; + + sz = packet[id].len; + reg = packet[id].start; + + if (sz * sizeof(int) > cmdbuf->bufsz) + return -EINVAL; + + BEGIN_RING(sz+1); + OUT_RING( CP_PACKET0( reg, (sz-1) ) ); + OUT_RING_USER_TABLE( data, sz ); + ADVANCE_RING(); + + cmdbuf->buf += sz * sizeof(int); + cmdbuf->bufsz -= sz * sizeof(int); + return 0; +} + +static __inline__ int radeon_emit_scalars( + drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_cmd_buffer_t *cmdbuf ) +{ + int sz = header.scalars.count; + int *data = (int *)cmdbuf->buf; + int start = header.scalars.offset; + int stride = header.scalars.stride; + RING_LOCALS; + + BEGIN_RING( 3+sz ); + OUT_RING( CP_PACKET0( RADEON_SE_TCL_SCALAR_INDX_REG, 0 ) ); + OUT_RING( start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); + OUT_RING( CP_PACKET0_TABLE( RADEON_SE_TCL_SCALAR_DATA_REG, sz-1 ) ); + OUT_RING_USER_TABLE( data, sz ); + ADVANCE_RING(); + cmdbuf->buf += sz * sizeof(int); + cmdbuf->bufsz -= sz * sizeof(int); + return 0; +} + +/* God this is ugly + */ +static __inline__ int radeon_emit_scalars2( + drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_cmd_buffer_t *cmdbuf ) +{ + int sz = header.scalars.count; + int *data = (int *)cmdbuf->buf; + int start = ((unsigned int)header.scalars.offset) + 0x100; + int stride = header.scalars.stride; + RING_LOCALS; + + BEGIN_RING( 3+sz ); + OUT_RING( CP_PACKET0( RADEON_SE_TCL_SCALAR_INDX_REG, 0 ) ); + OUT_RING( start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); + OUT_RING( CP_PACKET0_TABLE( RADEON_SE_TCL_SCALAR_DATA_REG, sz-1 ) ); + OUT_RING_USER_TABLE( data, sz ); + ADVANCE_RING(); + cmdbuf->buf += sz * sizeof(int); + cmdbuf->bufsz -= sz * sizeof(int); + return 0; +} + +static __inline__ int radeon_emit_vectors( + drm_radeon_private_t *dev_priv, + drm_radeon_cmd_header_t header, + drm_radeon_cmd_buffer_t *cmdbuf ) +{ + int sz = header.vectors.count; + int *data = (int *)cmdbuf->buf; + int start = header.vectors.offset; + int stride = header.vectors.stride; + RING_LOCALS; + + BEGIN_RING( 3+sz ); + OUT_RING( CP_PACKET0( RADEON_SE_TCL_VECTOR_INDX_REG, 0 ) ); + OUT_RING( start | (stride << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT)); + OUT_RING( CP_PACKET0_TABLE( RADEON_SE_TCL_VECTOR_DATA_REG, (sz-1) ) ); + OUT_RING_USER_TABLE( data, sz ); + ADVANCE_RING(); + + cmdbuf->buf += sz * sizeof(int); + cmdbuf->bufsz -= sz * sizeof(int); + return 0; +} + + +static int radeon_emit_packet3( drm_device_t *dev, + drm_radeon_cmd_buffer_t *cmdbuf ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + int cmdsz, tmp; + int *cmd = (int *)cmdbuf->buf; + RING_LOCALS; + + DRM_DEBUG("\n"); + + if (__get_user( tmp, &cmd[0])) + return -EFAULT; + + cmdsz = 2 + ((tmp & RADEON_CP_PACKET_COUNT_MASK) >> 16); + + if ((tmp & 0xc0000000) != RADEON_CP_PACKET3 || + cmdsz * 4 > cmdbuf->bufsz) + return -EINVAL; + + BEGIN_RING( cmdsz ); + OUT_RING_USER_TABLE( cmd, cmdsz ); + ADVANCE_RING(); + + cmdbuf->buf += cmdsz * 4; + cmdbuf->bufsz -= cmdsz * 4; + return 0; +} + + +static int radeon_emit_packet3_cliprect( drm_device_t *dev, + drm_radeon_cmd_buffer_t *cmdbuf, + int orig_nbox ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_clip_rect_t box; + int cmdsz, tmp; + int *cmd = (int *)cmdbuf->buf; + drm_clip_rect_t *boxes = cmdbuf->boxes; + int i = 0; + RING_LOCALS; + + DRM_DEBUG("\n"); + + if (__get_user( tmp, &cmd[0])) + return -EFAULT; + + cmdsz = 2 + ((tmp & RADEON_CP_PACKET_COUNT_MASK) >> 16); + + if ((tmp & 0xc0000000) != RADEON_CP_PACKET3 || + cmdsz * 4 > cmdbuf->bufsz) + return -EINVAL; + + if (!orig_nbox) + goto out; + + do { + if ( i < cmdbuf->nbox ) { + if (__copy_from_user( &box, &boxes[i], sizeof(box) )) + return -EFAULT; + /* FIXME The second and subsequent times round + * this loop, send a WAIT_UNTIL_3D_IDLE before + * calling emit_clip_rect(). This fixes a + * lockup on fast machines when sending + * several cliprects with a cmdbuf, as when + * waving a 2D window over a 3D + * window. Something in the commands from user + * space seems to hang the card when they're + * sent several times in a row. That would be + * the correct place to fix it but this works + * around it until I can figure that out - Tim + * Smith */ + if ( i ) { + BEGIN_RING( 2 ); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + } + radeon_emit_clip_rect( dev_priv, &box ); + } + + BEGIN_RING( cmdsz ); + OUT_RING_USER_TABLE( cmd, cmdsz ); + ADVANCE_RING(); + + } while ( ++i < cmdbuf->nbox ); + if (cmdbuf->nbox == 1) + cmdbuf->nbox = 0; + + out: + cmdbuf->buf += cmdsz * 4; + cmdbuf->bufsz -= cmdsz * 4; + return 0; +} + + +static int radeon_emit_wait( drm_device_t *dev, int flags ) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG("%s: %x\n", __FUNCTION__, flags); + switch (flags) { + case RADEON_WAIT_2D: + BEGIN_RING( 2 ); + RADEON_WAIT_UNTIL_2D_IDLE(); + ADVANCE_RING(); + break; + case RADEON_WAIT_3D: + BEGIN_RING( 2 ); + RADEON_WAIT_UNTIL_3D_IDLE(); + ADVANCE_RING(); + break; + case RADEON_WAIT_2D|RADEON_WAIT_3D: + BEGIN_RING( 2 ); + RADEON_WAIT_UNTIL_IDLE(); + ADVANCE_RING(); + break; + default: + return -EINVAL; + } + + return 0; +} + +int radeon_cp_cmdbuf(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf = 0; + int idx; + drm_radeon_cmd_buffer_t cmdbuf; + drm_radeon_cmd_header_t header; + int orig_nbox; + + LOCK_TEST_WITH_RETURN( dev ); + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( cmdbuf, (drm_radeon_cmd_buffer_t *)data, + sizeof(cmdbuf) ); + + RING_SPACE_TEST_WITH_RETURN( dev_priv ); + VB_AGE_TEST_WITH_RETURN( dev_priv ); + + + if (verify_area( VERIFY_READ, cmdbuf.buf, cmdbuf.bufsz )) + return -EFAULT; + + if (cmdbuf.nbox && + verify_area( VERIFY_READ, cmdbuf.boxes, + cmdbuf.nbox * sizeof(drm_clip_rect_t))) + return -EFAULT; + + orig_nbox = cmdbuf.nbox; + + while ( cmdbuf.bufsz >= sizeof(header) ) { + + if (__get_user( header.i, (int *)cmdbuf.buf )) { + DRM_ERROR("__get_user %p\n", cmdbuf.buf); + return -EFAULT; + } + + cmdbuf.buf += sizeof(header); + cmdbuf.bufsz -= sizeof(header); + + switch (header.header.cmd_type) { + case RADEON_CMD_PACKET: + DRM_DEBUG("RADEON_CMD_PACKET\n"); + if (radeon_emit_packets( dev_priv, header, &cmdbuf )) { + DRM_ERROR("radeon_emit_packets failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_SCALARS: + DRM_DEBUG("RADEON_CMD_SCALARS\n"); + if (radeon_emit_scalars( dev_priv, header, &cmdbuf )) { + DRM_ERROR("radeon_emit_scalars failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_VECTORS: + DRM_DEBUG("RADEON_CMD_VECTORS\n"); + if (radeon_emit_vectors( dev_priv, header, &cmdbuf )) { + DRM_ERROR("radeon_emit_vectors failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_DMA_DISCARD: + DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n"); + idx = header.dma.buf_idx; + if ( idx < 0 || idx >= dma->buf_count ) { + DRM_ERROR( "buffer index %d (of %d max)\n", + idx, dma->buf_count - 1 ); + return -EINVAL; + } + + buf = dma->buflist[idx]; + if ( buf->pid != current->pid || buf->pending ) { + DRM_ERROR( "bad buffer\n" ); + return -EINVAL; + } + + radeon_cp_discard_buffer( dev, buf ); + break; + + case RADEON_CMD_PACKET3: + DRM_DEBUG("RADEON_CMD_PACKET3\n"); + if (radeon_emit_packet3( dev, &cmdbuf )) { + DRM_ERROR("radeon_emit_packet3 failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_PACKET3_CLIP: + DRM_DEBUG("RADEON_CMD_PACKET3_CLIP\n"); + if (radeon_emit_packet3_cliprect( dev, &cmdbuf, orig_nbox )) { + DRM_ERROR("radeon_emit_packet3_clip failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_SCALARS2: + DRM_DEBUG("RADEON_CMD_SCALARS2\n"); + if (radeon_emit_scalars2( dev_priv, header, &cmdbuf )) { + DRM_ERROR("radeon_emit_scalars2 failed\n"); + return -EINVAL; + } + break; + + case RADEON_CMD_WAIT: + DRM_DEBUG("RADEON_CMD_WAIT\n"); + if (radeon_emit_wait( dev, header.wait.flags )) { + DRM_ERROR("radeon_emit_wait failed\n"); + return -EINVAL; + } + break; + default: + DRM_ERROR("bad cmd_type %d at %p\n", + header.header.cmd_type, + cmdbuf.buf - sizeof(header)); + return -EINVAL; + } + } + + + DRM_DEBUG("DONE\n"); + COMMIT_RING(); + return 0; +} + + + +int radeon_cp_getparam(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_radeon_private_t *dev_priv = dev->dev_private; + drm_radeon_getparam_t param; + int value; + + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); + return -EINVAL; + } + + DRM_COPY_FROM_USER_IOCTL( param, (drm_radeon_getparam_t *)data, + sizeof(param) ); + + DRM_DEBUG( "pid=%d\n", current->pid ); + + switch( param.param ) { + case RADEON_PARAM_AGP_BUFFER_OFFSET: + value = dev_priv->agp_buffers_offset; + break; + case RADEON_PARAM_LAST_FRAME: + dev_priv->stats.last_frame_reads++; + value = GET_SCRATCH( 0 ); + break; + case RADEON_PARAM_LAST_DISPATCH: + value = GET_SCRATCH( 1 ); + break; + case RADEON_PARAM_LAST_CLEAR: + dev_priv->stats.last_clear_reads++; + value = GET_SCRATCH( 2 ); + break; + case RADEON_PARAM_IRQ_NR: + value = dev->irq; + break; + case RADEON_PARAM_AGP_BASE: + value = dev_priv->agp_vm_start; + break; + default: + return -EINVAL; + } + + if ( copy_to_user( param.value, &value, sizeof(int) ) ) { + DRM_ERROR( "copy_to_user\n" ); + return -EFAULT; + } + return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/sis_drm.h linux.21rc1-ac2/drivers/char/drm/sis_drm.h --- linux.21rc1/drivers/char/drm/sis_drm.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/sis_drm.h 2003-04-22 16:44:36.000000000 +0100 @@ -2,6 +2,16 @@ #ifndef _sis_drm_public_h_ #define _sis_drm_public_h_ +/* SiS specific ioctls */ +#define SIS_IOCTL_FB_ALLOC DRM_IOWR(0x44, drm_sis_mem_t) +#define SIS_IOCTL_FB_FREE DRM_IOW( 0x45, drm_sis_mem_t) +#define SIS_IOCTL_AGP_INIT DRM_IOWR(0x53, drm_sis_agp_t) +#define SIS_IOCTL_AGP_ALLOC DRM_IOWR(0x54, drm_sis_mem_t) +#define SIS_IOCTL_AGP_FREE DRM_IOW( 0x55, drm_sis_mem_t) +#define SIS_IOCTL_FLIP DRM_IOW( 0x48, drm_sis_flip_t) +#define SIS_IOCTL_FLIP_INIT DRM_IO( 0x49) +#define SIS_IOCTL_FLIP_FINAL DRM_IO( 0x50) + typedef struct { int context; unsigned int offset; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/sis_drv.c linux.21rc1-ac2/drivers/char/drm/sis_drv.c --- linux.21rc1/drivers/char/drm/sis_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/sis_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -31,31 +31,6 @@ #include "sis_drm.h" #include "sis_drv.h" -#define DRIVER_AUTHOR "SIS" -#define DRIVER_NAME "sis" -#define DRIVER_DESC "SIS 300/630/540" -#define DRIVER_DATE "20010503" -#define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 0 - -#define DRIVER_IOCTLS \ - [DRM_IOCTL_NR(SIS_IOCTL_FB_ALLOC)] = { sis_fb_alloc, 1, 0 }, \ - [DRM_IOCTL_NR(SIS_IOCTL_FB_FREE)] = { sis_fb_free, 1, 0 }, \ - /* AGP Memory Management */ \ - [DRM_IOCTL_NR(SIS_IOCTL_AGP_INIT)] = { sisp_agp_init, 1, 0 }, \ - [DRM_IOCTL_NR(SIS_IOCTL_AGP_ALLOC)] = { sisp_agp_alloc, 1, 0 }, \ - [DRM_IOCTL_NR(SIS_IOCTL_AGP_FREE)] = { sisp_agp_free, 1, 0 } -#if 0 /* these don't appear to be defined */ - /* SIS Stereo */ - [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { sis_control, 1, 1 }, - [DRM_IOCTL_NR(SIS_IOCTL_FLIP)] = { sis_flip, 1, 1 }, - [DRM_IOCTL_NR(SIS_IOCTL_FLIP_INIT)] = { sis_flip_init, 1, 1 }, - [DRM_IOCTL_NR(SIS_IOCTL_FLIP_FINAL)] = { sis_flip_final, 1, 1 } -#endif - -#define __HAVE_COUNTERS 5 - #include "drm_auth.h" #include "drm_agpsupport.h" #include "drm_bufs.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/sis_ds.c linux.21rc1-ac2/drivers/char/drm/sis_ds.c --- linux.21rc1/drivers/char/drm/sis_ds.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/sis_ds.c 2003-04-22 16:44:36.000000000 +0100 @@ -49,16 +49,19 @@ set_t *set; set = (set_t *)MALLOC(sizeof(set_t)); - if (set) { + if(set) + { for(i = 0; i < SET_SIZE; i++){ set->list[i].free_next = i+1; set->list[i].alloc_next = -1; - } + } + set->list[SET_SIZE-1].free_next = -1; set->free = 0; set->alloc = -1; set->trace = -1; } + return set; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/sis.h linux.21rc1-ac2/drivers/char/drm/sis.h --- linux.21rc1/drivers/char/drm/sis.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/sis.h 2003-04-22 16:44:36.000000000 +0100 @@ -24,7 +24,7 @@ * DEALINGS IN THE SOFTWARE. * */ -/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/sis.h,v 1.1 2001/05/19 18:29:22 dawes Exp $ */ +/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/sis.h,v 1.2 2001/12/19 21:25:59 dawes Exp $ */ #ifndef __SIS_H__ #define __SIS_H__ @@ -42,6 +42,31 @@ #define __HAVE_MTRR 1 #define __HAVE_CTX_BITMAP 1 +#define DRIVER_AUTHOR "SIS" +#define DRIVER_NAME "sis" +#define DRIVER_DESC "SIS 300/630/540" +#define DRIVER_DATE "20010503" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(SIS_IOCTL_FB_ALLOC)] = { sis_fb_alloc, 1, 0 }, \ + [DRM_IOCTL_NR(SIS_IOCTL_FB_FREE)] = { sis_fb_free, 1, 0 }, \ + /* AGP Memory Management */ \ + [DRM_IOCTL_NR(SIS_IOCTL_AGP_INIT)] = { sisp_agp_init, 1, 0 }, \ + [DRM_IOCTL_NR(SIS_IOCTL_AGP_ALLOC)] = { sisp_agp_alloc, 1, 0 }, \ + [DRM_IOCTL_NR(SIS_IOCTL_AGP_FREE)] = { sisp_agp_free, 1, 0 } +#if 0 /* these don't appear to be defined */ + /* SIS Stereo */ + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { sis_control, 1, 1 }, + [DRM_IOCTL_NR(SIS_IOCTL_FLIP)] = { sis_flip, 1, 1 }, + [DRM_IOCTL_NR(SIS_IOCTL_FLIP_INIT)] = { sis_flip_init, 1, 1 }, + [DRM_IOCTL_NR(SIS_IOCTL_FLIP_FINAL)] = { sis_flip_final, 1, 1 } +#endif + +#define __HAVE_COUNTERS 5 + /* Buffer customization: */ #define DRIVER_AGP_BUFFERS_MAP( dev ) \ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm/tdfx_drv.c linux.21rc1-ac2/drivers/char/drm/tdfx_drv.c --- linux.21rc1/drivers/char/drm/tdfx_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm/tdfx_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -82,25 +82,6 @@ #include "drm_drawable.h" #include "drm_drv.h" -#ifndef MODULE -/* DRM(options) is called by the kernel to parse command-line options - * passed via the boot-loader (e.g., LILO). It calls the insmod option - * routine, drm_parse_drm. - */ - -/* JH- We have to hand expand the string ourselves because of the cpp. If - * anyone can think of a way that we can fit into the __setup macro without - * changing it, then please send the solution my way. - */ -static int __init tdfx_options( char *str ) -{ - DRM(parse_options)( str ); - return 1; -} - -__setup( DRIVER_NAME "=", tdfx_options ); -#endif - #include "drm_fops.h" #include "drm_init.h" #include "drm_ioctl.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm-4.0/agpsupport.c linux.21rc1-ac2/drivers/char/drm-4.0/agpsupport.c --- linux.21rc1/drivers/char/drm-4.0/agpsupport.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm-4.0/agpsupport.c 2003-04-22 16:44:36.000000000 +0100 @@ -277,7 +277,6 @@ break; case VIA_APOLLO_KT400: head->chipset = "VIA Apollo KT400"; break; - case VIA_APOLLO_P4X400: head->chipset = "VIA Apollo P4X400"; #endif case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/drm-4.0/tdfx_drv.c linux.21rc1-ac2/drivers/char/drm-4.0/tdfx_drv.c --- linux.21rc1/drivers/char/drm-4.0/tdfx_drv.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/drm-4.0/tdfx_drv.c 2003-04-22 16:44:36.000000000 +0100 @@ -554,7 +554,6 @@ lock.context, current->pid, j, dev->lock.lock_time, jiffies); current->state = TASK_INTERRUPTIBLE; - current->policy |= SCHED_YIELD; schedule_timeout(DRM_LOCK_SLICE-j); DRM_DEBUG("jiffies=%d\n", jiffies); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/dz.c linux.21rc1-ac2/drivers/char/dz.c --- linux.21rc1/drivers/char/dz.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/dz.c 2003-04-22 16:44:36.000000000 +0100 @@ -1054,7 +1054,7 @@ restore_flags(flags); return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/esp.c linux.21rc1-ac2/drivers/char/esp.c --- linux.21rc1/drivers/char/esp.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/esp.c 2003-04-22 16:44:36.000000000 +0100 @@ -136,7 +136,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) + kdevname(tty->device), (info->flags), serial_refcount,info->count,atomic_read(&tty->count),s) #else #define DBG_CNT(s) #endif @@ -2051,7 +2051,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/generic_serial.c linux.21rc1-ac2/drivers/char/generic_serial.c --- linux.21rc1/drivers/char/generic_serial.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/generic_serial.c 2003-04-22 16:44:36.000000000 +0100 @@ -753,7 +753,7 @@ return; } - if ((tty->count == 1) && (port->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { printk(KERN_ERR "gs: gs_close: bad port count;" " tty->count is 1, port count is %d\n", port->count); port->count = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ip2main.c linux.21rc1-ac2/drivers/char/ip2main.c --- linux.21rc1/drivers/char/ip2main.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ip2main.c 2003-04-22 16:44:37.000000000 +0100 @@ -372,7 +372,7 @@ #if defined(MODULE) && defined(IP2DEBUG_OPEN) #define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ kdevname(tty->device),(pCh->flags),ref_count, \ - tty->count,/*GET_USE_COUNT(module)*/0,s) + atomic_read(&tty->count),/*GET_USE_COUNT(module)*/0,s) #else #define DBG_CNT(s) #endif @@ -1740,7 +1740,7 @@ noblock: /* first open - Assign termios structure to port */ - if ( tty->count == 1 ) { + if ( atomic_read(&tty->count) == 1 ) { i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); if ( pCh->flags & ASYNC_SPLIT_TERMIOS ) { if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) { @@ -1805,7 +1805,7 @@ return; } - if ( tty->count > 1 ) { /* not the last close */ + if ( atomic_read(&tty->count) > 1 ) { /* not the last close */ MOD_DEC_USE_COUNT; ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 ); @@ -3364,7 +3364,7 @@ pCh = DevTable[i]; if (pCh) { tty = pCh->pTTY; - if (tty && tty->count) { + if (tty && atomic_read(&tty->count)) { len += sprintf(buf+len,FMTLINE,i,(int)tty->flags,pCh->flags, tty->termios->c_cflag,tty->termios->c_iflag); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ipmi/ipmi_devintf.c linux.21rc1-ac2/drivers/char/ipmi/ipmi_devintf.c --- linux.21rc1/drivers/char/ipmi/ipmi_devintf.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ipmi/ipmi_devintf.c 2003-04-22 18:21:37.000000000 +0100 @@ -81,10 +81,10 @@ unsigned int mask = 0; unsigned long flags; - spin_lock_irqsave(&priv->recv_msg_lock, flags); - poll_wait(file, &priv->wait, wait); + spin_lock_irqsave(&priv->recv_msg_lock, flags); + if (! list_empty(&(priv->recv_msgs))) mask |= (POLLIN | POLLRDNORM); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ipmi/ipmi_kcs_intf.c linux.21rc1-ac2/drivers/char/ipmi/ipmi_kcs_intf.c --- linux.21rc1/drivers/char/ipmi/ipmi_kcs_intf.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ipmi/ipmi_kcs_intf.c 2003-04-22 18:21:37.000000000 +0100 @@ -60,6 +60,14 @@ /* Measure times between events in the driver. */ #undef DEBUG_TIMING +/* Timing parameters. Call every 10 ms when not doing anything, + otherwise call every KCS_SHORT_TIMEOUT_USEC microseconds. */ +#define KCS_TIMEOUT_TIME_USEC 10000 +#define KCS_USEC_PER_JIFFY (1000000/HZ) +#define KCS_TIMEOUT_JIFFIES (KCS_TIMEOUT_TIME_USEC/KCS_USEC_PER_JIFFY) +#define KCS_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a + short timeout */ + #ifdef CONFIG_IPMI_KCS /* This forces a dependency to the config file for this option. */ #endif @@ -131,6 +139,8 @@ int interrupt_disabled; }; +static void kcs_restart_short_timer(struct kcs_info *kcs_info); + static void deliver_recv_msg(struct kcs_info *kcs_info, struct ipmi_smi_msg *msg) { /* Deliver the message to the upper layer with the lock @@ -308,6 +318,9 @@ #endif switch (kcs_info->kcs_state) { case KCS_NORMAL: + if (!kcs_info->curr_msg) + break; + kcs_info->curr_msg->rsp_size = kcs_get_result(kcs_info->kcs_sm, kcs_info->curr_msg->rsp, @@ -562,8 +575,9 @@ spin_lock_irqsave(&(kcs_info->kcs_lock), flags); result = kcs_event_handler(kcs_info, 0); while (result != KCS_SM_IDLE) { - udelay(500); - result = kcs_event_handler(kcs_info, 500); + udelay(KCS_SHORT_TIMEOUT_USEC); + result = kcs_event_handler(kcs_info, + KCS_SHORT_TIMEOUT_USEC); } spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags); return; @@ -581,6 +595,7 @@ && (kcs_info->curr_msg == NULL)) { start_next_msg(kcs_info); + kcs_restart_short_timer(kcs_info); } spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags); } @@ -597,8 +612,9 @@ if (i_run_to_completion) { result = kcs_event_handler(kcs_info, 0); while (result != KCS_SM_IDLE) { - udelay(500); - result = kcs_event_handler(kcs_info, 500); + udelay(KCS_SHORT_TIMEOUT_USEC); + result = kcs_event_handler(kcs_info, + KCS_SHORT_TIMEOUT_USEC); } } @@ -624,14 +640,40 @@ MOD_DEC_USE_COUNT; } -/* Call every 10 ms. */ -#define KCS_TIMEOUT_TIME_USEC 10000 -#define KCS_USEC_PER_JIFFY (1000000/HZ) -#define KCS_TIMEOUT_JIFFIES (KCS_TIMEOUT_TIME_USEC/KCS_USEC_PER_JIFFY) -#define KCS_SHORT_TIMEOUT_USEC 500 /* .5ms when the SM request a - short timeout */ static int initialized = 0; +/* Must be called with interrupts off and with the kcs_lock held. */ +static void kcs_restart_short_timer(struct kcs_info *kcs_info) +{ +#ifdef CONFIG_HIGH_RES_TIMERS + unsigned long jiffies_now; + + if (del_timer(&(kcs_info->kcs_timer))) { + /* If we don't delete the timer, then it will go off + immediately, anyway. So we only process if we + actually delete the timer. */ + + /* We already have irqsave on, so no need for it + here. */ + read_lock(&xtime_lock); + jiffies_now = jiffies; + kcs_info->kcs_timer.expires = jiffies_now; + + kcs_info->kcs_timer.sub_expires + = quick_update_jiffies_sub(jiffies_now); + read_unlock(&xtime_lock); + + kcs_info->kcs_timer.sub_expires + += usec_to_arch_cycles(KCS_SHORT_TIMEOUT_USEC); + while (kcs_info->kcs_timer.sub_expires >= cycles_per_jiffies) { + kcs_info->kcs_timer.expires++; + kcs_info->kcs_timer.sub_expires -= cycles_per_jiffies; + } + add_timer(&(kcs_info->kcs_timer)); + } +#endif +} + static void kcs_timeout(unsigned long data) { struct kcs_info *kcs_info = (struct kcs_info *) data; @@ -654,12 +696,11 @@ printk("**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif jiffies_now = jiffies; + time_diff = ((jiffies_now - kcs_info->last_timeout_jiffies) * KCS_USEC_PER_JIFFY); kcs_result = kcs_event_handler(kcs_info, time_diff); - spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags); - kcs_info->last_timeout_jiffies = jiffies_now; if ((kcs_info->irq) && (! kcs_info->interrupt_disabled)) { @@ -680,6 +721,7 @@ } } else { kcs_info->kcs_timer.expires = jiffies + KCS_TIMEOUT_JIFFIES; + kcs_info->kcs_timer.sub_expires = 0; } #else /* If requested, take the shortest delay possible */ @@ -692,6 +734,7 @@ do_add_timer: add_timer(&(kcs_info->kcs_timer)); + spin_unlock_irqrestore(&(kcs_info->kcs_lock), flags); } static void kcs_irq_handler(int irq, void *data, struct pt_regs *regs) @@ -838,7 +881,7 @@ if (kcs_port && kcs_physaddr) return -EINVAL; - new_kcs = kmalloc(kcs_size(), GFP_KERNEL); + new_kcs = kmalloc(sizeof(*new_kcs), GFP_KERNEL); if (!new_kcs) { printk(KERN_ERR "ipmi_kcs: out of memory\n"); return -ENOMEM; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ipmi/ipmi_kcs_sm.c linux.21rc1-ac2/drivers/char/ipmi/ipmi_kcs_sm.c --- linux.21rc1/drivers/char/ipmi/ipmi_kcs_sm.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ipmi/ipmi_kcs_sm.c 2003-04-22 18:21:37.000000000 +0100 @@ -468,7 +468,7 @@ break; case KCS_HOSED: - return KCS_SM_HOSED; + break; } if (kcs->state == KCS_HOSED) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ipmi/ipmi_msghandler.c linux.21rc1-ac2/drivers/char/ipmi/ipmi_msghandler.c --- linux.21rc1/drivers/char/ipmi/ipmi_msghandler.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ipmi/ipmi_msghandler.c 2003-04-22 18:21:37.000000000 +0100 @@ -174,8 +174,8 @@ int ipmi_register_all_cmd_rcvr(ipmi_user_t user) { - int flags; - int rv = -EBUSY; + unsigned long flags; + int rv = -EBUSY; write_lock_irqsave(&(user->intf->users_lock), flags); write_lock(&(user->intf->cmd_rcvr_lock)); @@ -193,8 +193,8 @@ int ipmi_unregister_all_cmd_rcvr(ipmi_user_t user) { - int flags; - int rv = -EINVAL; + unsigned long flags; + int rv = -EINVAL; write_lock_irqsave(&(user->intf->users_lock), flags); write_lock(&(user->intf->cmd_rcvr_lock)); @@ -1022,7 +1022,7 @@ int rv; ipmi_smi_t new_intf; struct list_head *entry; - unsigned int flags; + unsigned long flags; /* Make sure the driver is actually initialized, this handles @@ -1156,7 +1156,7 @@ int rv = -ENODEV; int i; struct list_head *entry; - unsigned int flags; + unsigned long flags; down_write(&interfaces_sem); if (list_empty(&(intf->users))) @@ -1773,9 +1773,13 @@ } +static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); +static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); + /* FIXME - convert these to slabs. */ static void free_smi_msg(struct ipmi_smi_msg *msg) { + atomic_dec(&smi_msg_inuse_count); kfree(msg); } @@ -1783,13 +1787,16 @@ { struct ipmi_smi_msg *rv; rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); - if (rv) + if (rv) { rv->done = free_smi_msg; + atomic_inc(&smi_msg_inuse_count); + } return rv; } static void free_recv_msg(struct ipmi_recv_msg *msg) { + atomic_dec(&recv_msg_inuse_count); kfree(msg); } @@ -1798,8 +1805,10 @@ struct ipmi_recv_msg *rv; rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); - if (rv) + if (rv) { rv->done = free_recv_msg; + atomic_inc(&recv_msg_inuse_count); + } return rv; } @@ -1932,6 +1941,8 @@ static __exit void cleanup_ipmi(void) { + int count; + if (!initialized) return; @@ -1948,6 +1959,16 @@ } initialized = 0; + + /* Check for buffer leaks. */ + count = atomic_read(&smi_msg_inuse_count); + if (count != 0) + printk("ipmi_msghandler: SMI message count %d at exit\n", + count); + count = atomic_read(&recv_msg_inuse_count); + if (count != 0) + printk("ipmi_msghandler: recv message count %d at exit\n", + count); } module_exit(cleanup_ipmi); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/ipmi/ipmi_watchdog.c linux.21rc1-ac2/drivers/char/ipmi/ipmi_watchdog.c --- linux.21rc1/drivers/char/ipmi/ipmi_watchdog.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/ipmi/ipmi_watchdog.c 2003-04-22 18:21:37.000000000 +0100 @@ -215,13 +215,13 @@ struct ipmi_recv_msg *recv_msg, int *send_heartbeat_now) { - struct ipmi_msg msg; - unsigned char data[6]; - int rv; + struct ipmi_msg msg; + unsigned char data[6]; + int rv; struct ipmi_system_interface_addr addr; + int hbnow = 0; - *send_heartbeat_now = 0; data[0] = 0; WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS); @@ -233,7 +233,7 @@ } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* In ipmi 1.0, setting the timer stops the watchdog, we need to start it back up again. */ - *send_heartbeat_now = 1; + hbnow = 1; } data[1] = 0; @@ -268,10 +268,18 @@ rv); } + if (send_heartbeat_now) + *send_heartbeat_now = hbnow; + return rv; } -static int ipmi_set_timeout(void) +/* Parameters to ipmi_set_timeout */ +#define IPMI_SET_TIMEOUT_NO_HB 0 +#define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 +#define IPMI_SET_TIMEOUT_FORCE_HB 2 + +static int ipmi_set_timeout(int do_heartbeat) { int send_heartbeat_now; int rv; @@ -288,8 +296,12 @@ if (rv) { up(&set_timeout_lock); } else { - if (send_heartbeat_now) + if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) + || ((send_heartbeat_now) + && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) + { rv = ipmi_heartbeat(); + } } return rv; @@ -312,7 +324,7 @@ /* Special call, doesn't claim any locks. This is only to be called at panic or halt time, in run-to-completion mode, when the caller - is the only CPU and the only thing that will be going IPMI + is the only CPU and the only thing that will be going is these IPMI calls. */ static void panic_halt_ipmi_set_timeout(void) { @@ -339,7 +351,7 @@ else ipmi_watchdog_state = WDOG_TIMEOUT_RESET; timeout = delay; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } /* We use a semaphore to make sure that only one thing can send a @@ -390,16 +402,14 @@ if (ipmi_start_timer_on_heartbeat) { ipmi_start_timer_on_heartbeat = 0; ipmi_watchdog_state = action_val; - return ipmi_set_timeout(); - } - - if (pretimeout_since_last_heartbeat) { + return ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); + } else if (pretimeout_since_last_heartbeat) { /* A pretimeout occurred, make sure we set the timeout. We don't want to set the action, though, we want to leave that alone (thus it can't be combined with the above operation. */ pretimeout_since_last_heartbeat = 0; - return ipmi_set_timeout(); + return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } down(&heartbeat_lock); @@ -501,7 +511,7 @@ if (i) return -EFAULT; timeout = val; - return ipmi_set_timeout(); + return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GETTIMEOUT: i = copy_to_user((void *) arg, @@ -516,7 +526,7 @@ if (i) return -EFAULT; pretimeout = val; - return ipmi_set_timeout(); + return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GET_PRETIMEOUT: i = copy_to_user((void *) arg, @@ -536,14 +546,14 @@ if (val & WDIOS_DISABLECARD) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); ipmi_start_timer_on_heartbeat = 0; } if (val & WDIOS_ENABLECARD) { ipmi_watchdog_state = action_val; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } return 0; @@ -679,7 +689,7 @@ { #ifndef CONFIG_WATCHDOG_NOWAYOUT ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); #endif ipmi_wdog_open = 0; } @@ -731,14 +741,14 @@ wake_up_interruptible(&read_q); kill_fasync(&fasync_q, SIGIO, POLL_IN); - /* On some machines, the heartbeat will give - an error and not work unless we re-enable - the timer. So do so. */ - pretimeout_since_last_heartbeat = 1; - spin_unlock(&ipmi_read_lock); } } + + /* On some machines, the heartbeat will give + an error and not work unless we re-enable + the timer. So do so. */ + pretimeout_since_last_heartbeat = 1; } static struct ipmi_user_hndl ipmi_hndlrs = @@ -751,7 +761,7 @@ { int rv = -EBUSY; - down_read(®ister_sem); + down_write(®ister_sem); if (watchdog_user) goto out; @@ -779,7 +789,7 @@ /* Run from startup, so start the timer now. */ start_now = 0; /* Disable this function after first startup. */ ipmi_watchdog_state = action_val; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); printk("Starting IPMI Watchdog now!\n"); } } @@ -792,6 +802,12 @@ watchdog. */ if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) panic("IPMI watchdog pre-timeout"); + + /* On some machines, the heartbeat will give + an error and not work unless we re-enable + the timer. So do so. */ + pretimeout_since_last_heartbeat = 1; + return NOTIFY_DONE; } @@ -916,7 +932,7 @@ } else if (strcmp(preaction, "pre_int") == 0) { preaction_val = WDOG_PRETIMEOUT_MSG_INT; } else { - action_val = WDOG_PRETIMEOUT_NONE; + preaction_val = WDOG_PRETIMEOUT_NONE; printk("ipmi_watchdog: Unknown preaction '%s', defaulting to" " none\n", preaction); } @@ -928,7 +944,7 @@ } else if (strcmp(preop, "preop_give_data") == 0) { preop_val = WDOG_PREOP_GIVE_DATA; } else { - action_val = WDOG_PREOP_NONE; + preop_val = WDOG_PREOP_NONE; printk("ipmi_watchdog: Unknown preop '%s', defaulting to" " none\n", preop); } @@ -1008,7 +1024,7 @@ /* Disable the timer. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); /* Wait to make sure the message makes it out. The lower layer has pointers to our buffers, we want to make sure they are done before diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/isicom.c linux.21rc1-ac2/drivers/char/isicom.c --- linux.21rc1/drivers/char/isicom.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/isicom.c 2003-04-22 16:44:37.000000000 +0100 @@ -1160,7 +1160,7 @@ return; } - if ((tty->count == 1) && (port->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count" "tty->count = 1 port count = %d.\n", card->base, port->count); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/istallion.c linux.21rc1-ac2/drivers/char/istallion.c --- linux.21rc1/drivers/char/istallion.c 2003-04-22 16:38:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/istallion.c 2003-04-22 16:44:37.000000000 +0100 @@ -1173,7 +1173,7 @@ restore_flags(flags); return; } - if ((tty->count == 1) && (portp->refcount != 1)) + if ((atomic_read(&tty->count) == 1) && (portp->refcount != 1)) portp->refcount = 1; if (portp->refcount-- > 1) { MOD_DEC_USE_COUNT; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/Makefile linux.21rc1-ac2/drivers/char/Makefile --- linux.21rc1/drivers/char/Makefile 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -199,6 +199,12 @@ obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_N_HDLC) += n_hdlc.o obj-$(CONFIG_SPECIALIX) += specialix.o + +subdir-$(CONFIG_ATI_CD1865) += cd1865 +ifeq ($(CONFIG_ATI_CD1865),y) + obj-y += cd1865/SILX.o +endif + obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o obj-$(CONFIG_SX) += sx.o generic_serial.o diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/moxa.c linux.21rc1-ac2/drivers/char/moxa.c --- linux.21rc1/drivers/char/moxa.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/moxa.c 2003-04-22 16:44:37.000000000 +0100 @@ -642,7 +642,7 @@ } ch = (struct moxa_str *) tty->driver_data; - if ((tty->count == 1) && (ch->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (ch->count != 1)) { printk("moxa_close: bad serial port count; tty->count is 1, " "ch->count is %d\n", ch->count); ch->count = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/mwave/mwavedd.c linux.21rc1-ac2/drivers/char/mwave/mwavedd.c --- linux.21rc1/drivers/char/mwave/mwavedd.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/mwave/mwavedd.c 2003-04-22 16:44:37.000000000 +0100 @@ -279,7 +279,6 @@ pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - current->nice = -20; /* boost to provide priority timing */ #else current->priority = 0x28; /* boost to provide priority timing */ #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/mxser.c linux.21rc1-ac2/drivers/char/mxser.c --- linux.21rc1/drivers/char/mxser.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/mxser.c 2003-04-22 16:44:37.000000000 +0100 @@ -824,7 +824,7 @@ MOD_DEC_USE_COUNT; return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/n_hdlc.c linux.21rc1-ac2/drivers/char/n_hdlc.c --- linux.21rc1/drivers/char/n_hdlc.c 2003-04-22 16:38:46.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/n_hdlc.c 2003-04-22 16:44:37.000000000 +0100 @@ -9,7 +9,7 @@ * Al Longyear , Paul Mackerras * * Original release 01/11/99 - * $Id: n_hdlc.c,v 3.3 2001/11/08 16:16:03 paulkf Exp $ + * $Id: n_hdlc.c,v 3.6 2002/12/19 18:58:56 paulkf Exp $ * * This code is released under the GNU General Public License (GPL) * @@ -78,7 +78,7 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "$Revision: 3.3 $" +#define HDLC_VERSION "$Revision: 3.6 $" #include #include @@ -172,9 +172,9 @@ /* * HDLC buffer list manipulation functions */ -void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list); -void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf); -N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list); +static void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list); +static void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf); +static N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list); /* Local functions */ @@ -186,10 +186,10 @@ /* debug level can be set by insmod for debugging purposes */ #define DEBUG_LEVEL_INFO 1 -int debuglevel=0; +static int debuglevel=0; /* max frame size for memory allocations */ -ssize_t maxframe=4096; +static ssize_t maxframe=4096; /* TTY callbacks */ @@ -265,7 +265,8 @@ } else break; } - + if (n_hdlc->tbuf) + kfree(n_hdlc->tbuf); kfree(n_hdlc); } /* end of n_hdlc_release() */ @@ -905,7 +906,7 @@ * Arguments: list pointer to buffer list * Return Value: None */ -void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list) +static void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list) { memset(list,0,sizeof(N_HDLC_BUF_LIST)); spin_lock_init(&list->spinlock); @@ -922,7 +923,7 @@ * * Return Value: None */ -void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf) +static void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf) { unsigned long flags; spin_lock_irqsave(&list->spinlock,flags); @@ -952,7 +953,7 @@ * * pointer to HDLC buffer if available, otherwise NULL */ -N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list) +static N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list) { unsigned long flags; N_HDLC_BUF *buf; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/n_tty.c linux.21rc1-ac2/drivers/char/n_tty.c --- linux.21rc1/drivers/char/n_tty.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/n_tty.c 2003-04-22 16:44:37.000000000 +0100 @@ -119,7 +119,7 @@ */ static void check_unthrottle(struct tty_struct * tty) { - if (tty->count && + if (atomic_read(&tty->count) && test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver.unthrottle) tty->driver.unthrottle(tty); @@ -1170,7 +1170,8 @@ retval = -ERESTARTSYS; break; } - if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { + if (tty_hung_up_p(file) || + (tty->link && !atomic_read(&tty->link->count))) { retval = -EIO; break; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/nwflash.c linux.21rc1-ac2/drivers/char/nwflash.c --- linux.21rc1/drivers/char/nwflash.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/nwflash.c 2003-04-22 16:44:37.000000000 +0100 @@ -154,12 +154,11 @@ if (down_interruptible(&nwflash_sem)) return -ERESTARTSYS; - ret = copy_to_user(buf, (void *)(FLASH_BASE + p), count); - if (ret == 0) { - ret = count; - *ppos += count; - } + ret = count - copy_to_user(buf, (void *)(FLASH_BASE + p), count); + *ppos += ret; up(&nwflash_sem); + if (ret == 0) + ret = -EFAULT; } return ret; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/pc_keyb.c linux.21rc1-ac2/drivers/char/pc_keyb.c --- linux.21rc1/drivers/char/pc_keyb.c 2003-04-22 16:38:46.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/pc_keyb.c 2003-04-22 16:44:37.000000000 +0100 @@ -1225,41 +1225,13 @@ #endif /* CONFIG_PSMOUSE */ -static int blink_frequency = HZ/2; +void pckbd_blink (char led) { + led = led ? (0x01 | 0x04) : 0x00; -/* Tell the user who may be running in X and not see the console that we have - panic'ed. This is to distingush panics from "real" lockups. - Could in theory send the panic message as morse, but that is left as an - exercise for the reader. */ -void panic_blink(void) -{ - static unsigned long last_jiffie; - static char led; - /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is - different. */ - if (!blink_frequency) - return; - if (jiffies - last_jiffie > blink_frequency) { - led ^= 0x01 | 0x04; while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); kbd_write_output(KBD_CMD_SET_LEDS); mdelay(1); while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); mdelay(1); kbd_write_output(led); - last_jiffie = jiffies; - } -} - -static int __init panicblink_setup(char *str) -{ - int par; - if (get_option(&str,&par)) - blink_frequency = par*(1000/HZ); - return 1; } - -/* panicblink=0 disables the blinking as it caused problems with some console - switches. otherwise argument is ms of a blink period. */ -__setup("panicblink=", panicblink_setup); - diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/pcmcia/synclink_cs.c linux.21rc1-ac2/drivers/char/pcmcia/synclink_cs.c --- linux.21rc1/drivers/char/pcmcia/synclink_cs.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/pcmcia/synclink_cs.c 2003-04-22 16:44:37.000000000 +0100 @@ -2626,7 +2626,7 @@ if (!info->count || tty_hung_up_p(filp)) goto cleanup; - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * tty->count is 1 and the tty structure will be freed. * info->count should be one in this case. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/pcxx.c linux.21rc1-ac2/drivers/char/pcxx.c --- linux.21rc1/drivers/char/pcxx.c 2003-04-22 16:38:46.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/pcxx.c 2003-04-22 16:44:37.000000000 +0100 @@ -581,7 +581,7 @@ return; } /* this check is in serial.c, it won't hurt to do it here too */ - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/pty.c linux.21rc1-ac2/drivers/char/pty.c --- linux.21rc1/drivers/char/pty.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/pty.c 2003-04-22 16:44:37.000000000 +0100 @@ -70,13 +70,18 @@ static void pty_close(struct tty_struct * tty, struct file * filp) { + int count; + if (!tty) return; + + count = atomic_read(&tty->count); if (tty->driver.subtype == PTY_TYPE_MASTER) { - if (tty->count > 1) - printk("master pty_close: count = %d!!\n", tty->count); + if (count > 1) + printk("master pty_close: count = %d!!\n", + atomic_read(&tty->count)); } else { - if (tty->count > 2) + if (count > 2) return; } wake_up_interruptible(&tty->read_wait); @@ -329,7 +334,7 @@ goto out; if (test_bit(TTY_PTY_LOCK, &tty->link->flags)) goto out; - if (tty->link->count != 1) + if (atomic_read(&tty->link->count) != 1) goto out; clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/raw.c linux.21rc1-ac2/drivers/char/raw.c --- linux.21rc1/drivers/char/raw.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/raw.c 2003-04-22 16:44:37.000000000 +0100 @@ -86,12 +86,6 @@ filp->f_op = &raw_ctl_fops; return 0; } - - if (!filp->f_iobuf) { - err = alloc_kiovec(1, &filp->f_iobuf); - if (err) - return err; - } down(&raw_devices[minor].mutex); /* @@ -292,7 +286,6 @@ size_t size, loff_t *offp) { struct kiobuf * iobuf; - int new_iobuf; int err = 0; unsigned long blocknr, blocks; size_t transferred; @@ -311,18 +304,10 @@ minor = MINOR(filp->f_dentry->d_inode->i_rdev); - new_iobuf = 0; - iobuf = filp->f_iobuf; - if (test_and_set_bit(0, &filp->f_iobuf_lock)) { - /* - * A parallel read/write is using the preallocated iobuf - * so just run slow and allocate a new one. - */ - err = alloc_kiovec(1, &iobuf); - if (err) - goto out; - new_iobuf = 1; - } + err = alloc_kiovec(1, &iobuf); + if (err) + return err; + dev = to_kdev_t(raw_devices[minor].binding->bd_dev); sector_size = raw_devices[minor].sector_size; @@ -395,10 +380,6 @@ } out_free: - if (!new_iobuf) - clear_bit(0, &filp->f_iobuf_lock); - else - free_kiovec(1, &iobuf); - out: + free_kiovec(1, &iobuf); return err; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/rio/rioboot.c linux.21rc1-ac2/drivers/char/rio/rioboot.c --- linux.21rc1/drivers/char/rio/rioboot.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/rio/rioboot.c 2003-04-23 14:30:53.000000000 +0100 @@ -328,6 +328,7 @@ if ( copyin((int)rbp->DataP,DownCode,rbp->Count)==COPYFAIL ) { rio_dprintk (RIO_DEBUG_BOOT, "Bad copyin of host data\n"); + sysfree( DownCode, rbp->Count ); p->RIOError.Error = COPYIN_FAILED; func_exit (); return EFAULT; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/riscom8.c linux.21rc1-ac2/drivers/char/riscom8.c --- linux.21rc1/drivers/char/riscom8.c 2003-04-22 16:38:46.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/riscom8.c 2003-04-22 16:44:37.000000000 +0100 @@ -1142,7 +1142,7 @@ goto out; bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { printk(KERN_INFO "rc%d: rc_close: bad port count;" " tty->count is 1, port count is %d\n", board_No(bp), port->count); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/rocket.c linux.21rc1-ac2/drivers/char/rocket.c --- linux.21rc1/drivers/char/rocket.c 2003-04-22 16:38:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/rocket.c 2003-04-22 16:44:37.000000000 +0100 @@ -1052,7 +1052,7 @@ restore_flags(flags); return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/serial167.c linux.21rc1-ac2/drivers/char/serial167.c --- linux.21rc1/drivers/char/serial167.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/serial167.c 2003-04-22 16:44:37.000000000 +0100 @@ -1877,7 +1877,7 @@ printk("cy_close ttyS%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/serial_amba.c linux.21rc1-ac2/drivers/char/serial_amba.c --- linux.21rc1/drivers/char/serial_amba.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/serial_amba.c 2003-04-22 16:44:37.000000000 +0100 @@ -1404,7 +1404,7 @@ return; } - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/serial.c linux.21rc1-ac2/drivers/char/serial.c --- linux.21rc1/drivers/char/serial.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/serial.c 2003-04-22 16:44:37.000000000 +0100 @@ -62,6 +62,10 @@ * Robert Schwebel , * Juergen Beisert , * Theodore Ts'o + * 4/02: added TTY_DO_WRITE_WAKEUP to enable n_tty to send POLL_OUTS + * to waiting processes + * Sapan Bhatia + * */ static char *serial_version = "5.05c"; @@ -203,6 +207,7 @@ #include #include #include +#include #if (LINUX_VERSION_CODE >= 131343) #include #endif @@ -370,7 +375,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) + kdevname(tty->device), (info->flags), serial_refcount,info->count,atomic_read(&tty->count),s) #else #define DBG_CNT(s) #endif @@ -1218,7 +1223,7 @@ if (!page) return -ENOMEM; - save_flags(flags); cli(); + spin_lock_irqsave( &info->irq_spinlock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); @@ -1456,11 +1461,11 @@ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore( &info->irq_spinlock, flags); return 0; errout: - restore_flags(flags); + spin_unlock_irqrestore( &info->irq_spinlock, flags); return retval; } @@ -1484,7 +1489,7 @@ state->irq); #endif - save_flags(flags); cli(); /* Disable interrupts */ + spin_lock_irqsave( &info->irq_spinlock, flags); /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq @@ -1492,41 +1497,6 @@ */ wake_up_interruptible(&info->delta_msr_wait); - /* - * First unlink the serial port from the IRQ chain... - */ - if (info->next_port) - info->next_port->prev_port = info->prev_port; - if (info->prev_port) - info->prev_port->next_port = info->next_port; - else - IRQ_ports[state->irq] = info->next_port; - figure_IRQ_timeout(state->irq); - - /* - * Free the IRQ, if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - free_irq(state->irq, &IRQ_ports[state->irq]); - retval = request_irq(state->irq, rs_interrupt_single, - SA_SHIRQ, "serial", - &IRQ_ports[state->irq]); - - if (retval) - printk("serial shutdown: request_irq: error %d" - " Couldn't reacquire IRQ.\n", retval); - } else - free_irq(state->irq, &IRQ_ports[state->irq]); - } - - if (info->xmit.buf) { - unsigned long pg = (unsigned long) info->xmit.buf; - info->xmit.buf = 0; - free_page(pg); - } - info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ #ifdef CONFIG_SERIAL_MANY_PORTS @@ -1583,7 +1553,43 @@ serial_outp(info, UART_IER, UART_IERX_SLEEP); } info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + /* + * Free the IRQ, if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + spin_unlock_irqrestore( &info->irq_spinlock, flags); } #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ @@ -2791,7 +2797,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always @@ -3128,6 +3134,7 @@ info->tqueue.routine = do_softint; info->tqueue.data = info; info->state = sstate; + spin_lock_init(&info->irq_spinlock); if (sstate->info) { kfree(info); *ret_info = sstate->info; @@ -3242,6 +3249,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_open ttys%d successful...", info->line); #endif + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); return 0; } @@ -3655,6 +3663,7 @@ info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; save_flags(flags); cli(); @@ -3907,7 +3916,14 @@ case 6: /* BAR 4*/ case 7: base_idx=idx-2; /* BAR 5*/ } - + + /* AFAVLAB uses a different mixture of BARs and offsets */ + /* Not that ugly ;) -- HW */ + if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { + base_idx = 4; + offset = (idx - 4) * 8; + } + /* Some Titan cards are also a little weird */ if (dev->vendor == PCI_VENDOR_ID_TITAN && (dev->device == PCI_DEVICE_ID_TITAN_400L || @@ -4311,9 +4327,11 @@ pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, + pbn_b0_bt_8_115200, pbn_b0_bt_1_460800, pbn_b0_bt_2_460800, pbn_b0_bt_2_921600, + pbn_b0_bt_4_460800, pbn_b1_1_115200, pbn_b1_2_115200, @@ -4392,9 +4410,11 @@ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b0_bt_2_921600 */ + { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 460800 }, /* pbn_b0_bt_4_460800 */ { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ @@ -4859,6 +4879,12 @@ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_1_115200 }, @@ -4871,6 +4897,11 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_115200 }, + /* AFAVLAB serial card, from Harald Welte */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + /* EKF addition for i960 Boards form EKF with serial port */ { PCI_VENDOR_ID_INTEL, 0x1960, 0xE4BF, PCI_ANY_ID, 0, 0, @@ -4890,6 +4921,7 @@ 0x1048, 0x1500, 0, 0, pbn_b1_1_115200 }, + /* SGI IOC3 board */ { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, 0xFF00, 0, 0, 0, pbn_sgi_ioc3 }, @@ -5542,12 +5574,22 @@ tty_register_devfs(&callout_driver, 0, callout_driver.minor_start + state->line); } +#ifdef CONFIG_SERIAL_GSC + probe_serial_gsc(); +#endif +#ifdef CONFIG_SUPERIO + superio_serial_init(); +#endif #ifdef ENABLE_SERIAL_PCI probe_serial_pci(); #endif #ifdef ENABLE_SERIAL_PNP probe_serial_pnp(); #endif + // FIXME: Clean this one up +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_PARISC) + serial_console_init(); +#endif return 0; } @@ -5657,6 +5699,7 @@ info->io_type = req->io_type; info->iomem_base = req->iomem_base; info->iomem_reg_shift = req->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; } autoconfig(state); if (state->type == PORT_UNKNOWN) { @@ -5964,6 +6007,7 @@ info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; + info->irq_spinlock= (spinlock_t) SPIN_LOCK_UNLOCKED; quot = state->baud_base / baud; cval = cflag & (CSIZE | CSTOPB); #if defined(__powerpc__) || defined(__alpha__) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/serial_txx927.c linux.21rc1-ac2/drivers/char/serial_txx927.c --- linux.21rc1/drivers/char/serial_txx927.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/serial_txx927.c 2003-04-22 16:44:37.000000000 +0100 @@ -155,7 +155,7 @@ #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s) + kdevname(tty->device), (info->flags), serial_refcount,info->count,atomic_read(&tty->count),s) #else #define DBG_CNT(s) #endif @@ -1407,7 +1407,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, state->count); #endif - if ((tty->count == 1) && (state->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/sonypi.c linux.21rc1-ac2/drivers/char/sonypi.c --- linux.21rc1/drivers/char/sonypi.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/sonypi.c 2003-04-22 16:44:37.000000000 +0100 @@ -162,7 +162,7 @@ } /* Disables the device - this comes from the AML code in the ACPI bios */ -static void __devexit sonypi_type1_dis(void) { +static void sonypi_type1_dis(void) { u32 v; pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v); @@ -174,7 +174,7 @@ outl(v, SONYPI_IRQ_PORT); } -static void __devexit sonypi_type2_dis(void) { +static void sonypi_type2_dis(void) { if (ec_write(SONYPI_SHIB, 0)) printk(KERN_WARNING "ec_write failed\n"); if (ec_write(SONYPI_SLOB, 0)) @@ -696,14 +696,36 @@ } for (i = 0; irq_list[i].irq; i++) { - if (!request_irq(irq_list[i].irq, sonypi_irq, - SA_SHIRQ, "sonypi", sonypi_irq)) { - sonypi_device.irq = irq_list[i].irq; - sonypi_device.bits = irq_list[i].bits; + + sonypi_device.irq = irq_list[i].irq; + sonypi_device.bits = irq_list[i].bits; + + /* Enable sonypi IRQ settings */ + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_srs(); + else + sonypi_type1_srs(); + + sonypi_call1(0x82); + sonypi_call2(0x81, 0xff); + if (compat) + sonypi_call1(0x92); + else + sonypi_call1(0x82); + + /* Now try requesting the irq from the system */ + if (!request_irq(sonypi_device.irq, sonypi_irq, + SA_SHIRQ, "sonypi", sonypi_irq)) break; - } + + /* If request_irq failed, disable sonypi IRQ settings */ + if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) + sonypi_type2_dis(); + else + sonypi_type1_dis(); } - if (!sonypi_device.irq ) { + + if (!irq_list[i].irq) { printk(KERN_ERR "sonypi: request_irq failed\n"); ret = -ENODEV; goto out3; @@ -715,17 +737,6 @@ outb(0xf0, 0xb2); #endif - if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) - sonypi_type2_srs(); - else - sonypi_type1_srs(); - - sonypi_call1(0x82); - sonypi_call2(0x81, 0xff); - if (compat) - sonypi_call1(0x92); - else - sonypi_call1(0x82); printk(KERN_INFO "sonypi: Sony Programmable I/O Controller Driver v%d.%d.\n", SONYPI_DRIVER_MAJORVERSION, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/sonypi.h linux.21rc1-ac2/drivers/char/sonypi.h --- linux.21rc1/drivers/char/sonypi.h 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/sonypi.h 2003-04-25 16:05:08.000000000 +0100 @@ -37,7 +37,7 @@ #ifdef __KERNEL__ #define SONYPI_DRIVER_MAJORVERSION 1 -#define SONYPI_DRIVER_MINORVERSION 18 +#define SONYPI_DRIVER_MINORVERSION 19 #define SONYPI_DEVICE_MODEL_TYPE1 1 #define SONYPI_DEVICE_MODEL_TYPE2 2 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/specialix.c linux.21rc1-ac2/drivers/char/specialix.c --- linux.21rc1/drivers/char/specialix.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/specialix.c 2003-04-22 16:44:37.000000000 +0100 @@ -1517,7 +1517,7 @@ } bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { printk(KERN_ERR "sx%d: sx_close: bad port count;" " tty->count is 1, port count is %d\n", board_No(bp), port->count); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/stallion.c linux.21rc1-ac2/drivers/char/stallion.c --- linux.21rc1/drivers/char/stallion.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/stallion.c 2003-04-22 16:44:37.000000000 +0100 @@ -1197,7 +1197,7 @@ restore_flags(flags); return; } - if ((tty->count == 1) && (portp->refcount != 1)) + if ((atomic_read(&tty->count) == 1) && (portp->refcount != 1)) portp->refcount = 1; if (portp->refcount-- > 1) { MOD_DEC_USE_COUNT; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/synclink.c linux.21rc1-ac2/drivers/char/synclink.c --- linux.21rc1/drivers/char/synclink.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/synclink.c 2003-04-22 16:44:37.000000000 +0100 @@ -3261,7 +3261,7 @@ if (!info->count || tty_hung_up_p(filp)) goto cleanup; - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * tty->count is 1 and the tty structure will be freed. * info->count should be one in this case. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/synclinkmp.c linux.21rc1-ac2/drivers/char/synclinkmp.c --- linux.21rc1/drivers/char/synclinkmp.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/synclinkmp.c 2003-04-22 16:44:37.000000000 +0100 @@ -851,7 +851,7 @@ if (!info->count || tty_hung_up_p(filp)) goto cleanup; - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * tty->count is 1 and the tty structure will be freed. * info->count should be one in this case. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/tty_io.c linux.21rc1-ac2/drivers/char/tty_io.c --- linux.21rc1/drivers/char/tty_io.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/tty_io.c 2003-04-25 13:53:30.000000000 +0100 @@ -150,8 +150,8 @@ extern void console_8xx_init(void); extern int rs_8xx_init(void); extern void mac_scc_console_init(void); -extern void hwc_console_init(void); -extern void hwc_tty_init(void); +extern void sclp_console_init(void); +extern void sclp_tty_init(void); extern void con3215_init(void); extern void tty3215_init(void); extern void tub3270_con_init(void); @@ -247,14 +247,15 @@ file_list_unlock(); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_SLAVE && - tty->link && tty->link->count) + tty->link && atomic_read(&tty->link->count)) count++; - if (tty->count != count) { + if (atomic_read(&tty->count) != count) { printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) " "!= #fd's(%d) in %s\n", - kdevname(tty->device), tty->count, count, routine); + kdevname(tty->device), atomic_read(&tty->count), + count, routine); return count; - } + } #endif return 0; } @@ -904,7 +905,7 @@ o_tty->termios_locked = *o_ltp_loc; (*driver->other->refcount)++; if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; + atomic_inc(&o_tty->count); /* Establish the links in both directions */ tty->link = o_tty; @@ -925,7 +926,7 @@ tty->termios = *tp_loc; tty->termios_locked = *ltp_loc; (*driver->refcount)++; - tty->count++; + atomic_inc(&tty->count); /* * Structures all installed ... call the ldisc open routines. @@ -964,13 +965,13 @@ * special case for PTY masters: only one open permitted, * and the slave side open count is incremented as well. */ - if (tty->count) { + if (atomic_read(&tty->count)) { retval = -EIO; goto end_init; } - tty->link->count++; + atomic_inc(&tty->link->count); } - tty->count++; + atomic_inc(&tty->count); tty->driver = *driver; /* N.B. why do this every time?? */ success: @@ -1094,7 +1095,7 @@ #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", - tty_name(tty, buf), tty->count); + tty_name(tty, buf), atomic_read(&tty->count)); #endif #ifdef TTY_PARANOIA_CHECK @@ -1146,9 +1147,9 @@ * each iteration we avoid any problems. */ while (1) { - tty_closing = tty->count <= 1; + tty_closing = atomic_read(&tty->count) <= 1; o_tty_closing = o_tty && - (o_tty->count <= (pty_master ? 1 : 0)); + (atomic_read(&o_tty->count) <= (pty_master ? 1 : 0)); do_sleep = 0; if (tty_closing) { @@ -1185,17 +1186,20 @@ * block, so it's safe to proceed with closing. */ if (pty_master) { - if (--o_tty->count < 0) { + atomic_dec(&o_tty->count); + if (atomic_read(&o_tty->count) < 0) { printk(KERN_WARNING "release_dev: bad pty slave count " "(%d) for %s\n", - o_tty->count, tty_name(o_tty, buf)); - o_tty->count = 0; + atomic_read(&o_tty->count), + tty_name(o_tty, buf)); + atomic_set(&o_tty->count, 0); } } - if (--tty->count < 0) { + atomic_dec(&tty->count); + if (atomic_read(&tty->count) < 0) { printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", - tty->count, tty_name(tty, buf)); - tty->count = 0; + atomic_read(&tty->count), tty_name(tty, buf)); + atomic_set(&tty->count, 0); } /* @@ -1414,7 +1418,7 @@ } if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) && (tty->driver.subtype == SERIAL_TYPE_CALLOUT) && - (tty->count == 1)) { + (atomic_read(&tty->count) == 1)) { static int nr_warns; if (nr_warns < 5) { printk(KERN_WARNING "tty_io.c: " @@ -2245,8 +2249,8 @@ #ifdef CONFIG_TN3215 con3215_init(); #endif -#ifdef CONFIG_HWC - hwc_console_init(); +#ifdef CONFIG_SCLP_CONSOLE + sclp_console_init(); #endif #ifdef CONFIG_STDIO_CONSOLE stdio_console_init(); @@ -2405,8 +2409,8 @@ #ifdef CONFIG_TN3215 tty3215_init(); #endif -#ifdef CONFIG_HWC - hwc_tty_init(); +#ifdef CONFIG_SCLP_TTY + sclp_tty_init(); #endif #ifdef CONFIG_A2232 a2232board_init(); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/char/vt.c linux.21rc1-ac2/drivers/char/vt.c --- linux.21rc1/drivers/char/vt.c 2003-04-22 16:38:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/char/vt.c 2003-04-22 16:44:37.000000000 +0100 @@ -40,7 +40,8 @@ char vt_dont_switch; extern struct tty_driver console_driver; -#define VT_IS_IN_USE(i) (console_driver.table[i] && console_driver.table[i]->count) +#define VT_IS_IN_USE(i) (console_driver.table[i] && \ + atomic_read(&console_driver.table[i]->count)) #define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || i == sel_cons) /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/hotplug/Config.in linux.21rc1-ac2/drivers/hotplug/Config.in --- linux.21rc1/drivers/hotplug/Config.in 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/drivers/hotplug/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -6,11 +6,11 @@ dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_EXPERIMENTAL $CONFIG_PCI +dep_tristate ' ACPI PCI Hotplug driver' CONFIG_HOTPLUG_PCI_ACPI $CONFIG_ACPI $CONFIG_HOTPLUG_PCI dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI $CONFIG_X86 dep_mbool ' Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ if [ "$CONFIG_X86_IO_APIC" = "y" ]; then dep_tristate ' IBM PCI Hotplug driver' CONFIG_HOTPLUG_PCI_IBM $CONFIG_HOTPLUG_PCI $CONFIG_X86_IO_APIC $CONFIG_X86 fi -dep_tristate ' ACPI PCI Hotplug driver' CONFIG_HOTPLUG_PCI_ACPI $CONFIG_ACPI $CONFIG_HOTPLUG_PCI - +dep_tristate ' IBM Thinkpad (20H2999) Docking driver (VERY EXPERIMENTAL) ' CONFIG_HOTPLUG_PCI_H2999 $CONFIG_HOTPLUG_PCI $CONFIG_X86 endmenu diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/hotplug/Makefile linux.21rc1-ac2/drivers/hotplug/Makefile --- linux.21rc1/drivers/hotplug/Makefile 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/hotplug/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -12,6 +12,7 @@ obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o +obj-$(CONFIG_HOTPLUG_PCI_H2999) += tp600.o pci_hotplug-objs := pci_hotplug_core.o \ pci_hotplug_util.o diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/hotplug/tp600.c linux.21rc1-ac2/drivers/hotplug/tp600.c --- linux.21rc1/drivers/hotplug/tp600.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/hotplug/tp600.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,500 @@ +/* + * Drivers for the IBM 20H2999 found in the IBM thinkpad series + * machines. + * + * This driver was done without documentation from IBM + * + * _ + * { } + * | | All reverse engineering done + * | | in accordance with 92/250/EEC + * .-.! !.-. and Copyright (Computer Programs) + * .-! ! ! !.-. Regulations 1992 (S.I. 1992 No. 3233) + * ! ! ! ; + * \ ; + * \ ; + * ! : + * ! | + * | | + * + * + * Various other IBM's tried to obtain docs but failed. For that + * reason we only support warm not hot undocking at the moment. + * + * Known bugs: + * Sometimes we hang with an IRQ storm. I don't know what + * deals with the IRQ disables yet. (Hot dock) + * Sometimes busmastering (and maybe IRQs) don't come back + * (Seems to be a buffering issue for hot dock) + * + * Yet to do: + * ISA is not yet handled (oh god help us) + * Instead of saving/restoring pci devices we should + * re-enumerate that subtree so you can change devices + * (That also deals with stale save problems) + * We need to do a proper warm save/restore interface + * Bridged cards don't yet work + * + * Usage: + * Load module + * Pray + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pci_hotplug.h" +#include "tp600.h" + +static struct h2999_dev *testdev; + +/** + * pci_save_slot - save slot PCI data + * @slot: slot to save + * + * Save the slot data from a PCI device + */ + +static void pci_save_slot(struct h2999_slot *s) +{ + int i, n; + + for(i=0;i<8;i++) + { + struct pci_dev *p = pci_find_slot(s->dev->hotplug_bus, PCI_DEVFN(s->slotid, i)); + s->pci[i] = p; + if(p) + { + for(n = 0; n < 64; n++) + pci_read_config_dword(p, n * 4, &s->save[i][n]); +// printk("Saved %02X:%02X.%X\n", +// s->dev->hotplug_bus, s->slotid, i); + } + } +} + +static void pci_restore_slot(struct h2999_slot *s) +{ + int i,n; + + for(i = 0 ; i < 8; i++) + { + if(s->pci[i]) + { + pci_set_power_state(s->pci[i], 0); + + for(n = 0; n < 54; n++) + if(n!=1) + pci_write_config_dword(s->pci[i], n * 4, s->save[i][n]); + pci_write_config_dword(s->pci[i], 4, s->save[i][1]); +// printk("Restored %02X:%02X.%X\n", +// s->dev->hotplug_bus, s->slotid, i); + } + } +} + +/** + * slot_enable - enable H2999 slot + * @slot: slot to enable + * + * Enable a slot. Its not actually clear what this means with + * a hot dock. We can certainly 'discover' the PCI device in the + * slot when asked. + */ + +static int slot_enable(struct hotplug_slot *slot) +{ + struct h2999_slot *s = slot->private; + int i; + pci_restore_slot(s); + for(i=0; i < 8; i++) + { + if(s->pci[i] && (s->drivermap&(1<pci[i]); + } + return 0; +} + +/** + * slot_disable - disable H2999 slot + * @slot: slot to disable + * + * Disable a slot. Its not actually clear what to do here. We could + * report the device as having been removed when we are told to do + * this. + */ + +static int slot_disable(struct hotplug_slot *slot) +{ + struct h2999_slot *s = slot->private; + struct pci_dev *pdev; + int i; + + for(i = 0; i < 8; i++) + { + pdev = s->pci[i]; + /* Hack for now */ + if (pdev && pdev->driver) { + if (!pdev->driver->remove) + return -EBUSY; + } + } + + s->drivermap = 0; + + for(i = 0; i < 8; i++) + { + pdev = s->pci[i]; + if(pdev) + { + if(pdev->driver) + { + s->drivermap|=(1<driver->remove(pdev); + pdev->driver = NULL; + } + } + } + return 0; +} + +/** + * set_attention_status - set attention callback + * @slot: slot to set + * @value: on/off + * + * Called when the hotplug layer wants to set the attention status of + * the hotplug slot. The H2999 doesn't have an attention control (at + * least not that we know of). So we ignore this. + */ + +static int set_attention_status(struct hotplug_slot *slot, u8 value) +{ + return 0; +} + +/** + * hardware_test - test hardware callback + * @slot: slot to test + * value: test to run + * + * The H2999 does not support any hardware tests that we know of. + */ + +static int hardware_test(struct hotplug_slot *slot, u32 value) +{ + return 0; +} + +/** + * get_power_status - power query callback + * @slot; slot to query + * @value: returned state + * + * Called when the hotplug layer wants to ask us if the slot is + * powered. We work on the basis that all slots are powered when + * the unit is docked. This seems to be correct but I've not actually + * rammed a voltmeter into the slots to see if they are cleverer than + * that. + */ + +static int get_power_status(struct hotplug_slot *slot, u8 *value) +{ + struct h2999_slot *s = slot->private; + + /* Slots are all powered when docked */ + if(s->dev->docked > 0) + *value = 1; + else + *value = 0; + return 0; +} + +/** + * get_adapter_status - card presence query + * @slot: slot to query + * @value: returned state + * + * If we are not docked, we know the "slot" is empty. If we are + * docked its a bit more complicated. + */ + +static int get_adapter_status(struct hotplug_slot *slot, u8 *value) +{ + struct h2999_slot *s = slot->private; + + *value = 0; + + if(s->dev->docked) + *value = 1; + return 0; +} + +static struct hotplug_slot_ops h2999_ops = { + THIS_MODULE, + slot_enable, + slot_disable, + set_attention_status, + hardware_test, + get_power_status, + NULL, + NULL, + get_adapter_status +}; + +/** + * h2999_is_docked - check if docked + * @dev: h2999 device + * + * Check if we are currently docked. The method we use at the moment + * relies on poking around behind the bridge. There is no doubt a + * correct way to do this. Maybe one day IBM will be decide to + * actually provide documentation + */ + +static int h2999_is_docked(struct h2999_dev *dev) +{ + struct pci_dev *pdev = pci_find_slot(dev->hotplug_bus, PCI_DEVFN(0,0)); + u32 status; + + if(pdev == NULL) + return 0; /* Shouldnt happen - must be undocked */ + + if(pci_read_config_dword(pdev, PCI_VENDOR_ID, &status)) + return 0; /* Config read failed - its missing */ + + if(status == 0xFFFFFFFFUL) /* Failed */ + return 0; + + /* Must be docked */ + return 1; +} +/** + * h2999_reconfigure_dock - redock event handler + * @dev: h2999 device + * + * A redocking event has occurred. There may also have been an undock + * before hand. If so then the unconfigure routine is called first. + */ + +static void h2999_reconfigure_dock(struct h2999_dev *dev) +{ + int docked, i; + + docked = h2999_is_docked(dev); + + if(docked ^ dev->docked) + { + printk("h2999: Now %sdocked.\n", + docked?"":"un"); + if(docked) + { + /* We should do the re-enumeration of the bus here + The current save/restore is a test hack */ + for(i=0; i < H2999_SLOTS; i++) + { + if(dev->slots[i].hotplug_slot.private != &dev->slots[i]) + BUG(); + slot_enable(&dev->slots[i].hotplug_slot); + } + } + else + { + for(i=0; i < H2999_SLOTS; i++) + { + if(slot_disable(&dev->slots[i].hotplug_slot)) + printk(KERN_ERR "h2999: someone undocked while devices were in use, how rude!\n"); + } + } + dev->docked = docked; + } + /* Clear bits */ + pci_write_config_byte(dev->pdev, 0x1f, 0xf0); +} + +/* + * h2999_attach - attach an H2999 bridge + * @pdev: PCI device + * @unused: unused + * + * Called when the PCI layer discovers an H2999 docking bridge is + * present in the system. We scan the bridge to obtain its current + * status and register it with the hot plug layer + */ + +static int __devinit h2999_attach(struct pci_dev *pdev, const struct pci_device_id *unused) +{ + /* PCI core found a new H2999 */ + struct h2999_dev *dev; + u8 bus; + int i; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if(dev == NULL) + goto nomem; + + memset(dev, 0, sizeof(*dev)); + + dev->pdev = pdev; + + pci_read_config_byte(pdev, PCI_SECONDARY_BUS, &bus); + dev->hotplug_bus = bus; + + /* Requires hotplug_bus and pdev are set */ + + dev->docked = h2999_is_docked(dev); + + printk(KERN_INFO "Found IBM 20H2999. Status is %sdocked, docking bus is %d.\n", + dev->docked?"":"un", dev->hotplug_bus); + + /* + * Allow for 8 devices. On the TP600 at least we have + * 0-3 as the onboard devices, and 4-7 as the slots. + * To add more fun there is an ISA bridge which we + * don't really handle yet. + */ + + for(i = 0; i < H2999_SLOTS; i++) + { + struct h2999_slot *s = &dev->slots[i]; + int ret; + + s->hotplug_slot.info = &s->hotplug_info; + s->hotplug_slot.private = s; + s->slotid = i; + s->dev = dev; + s->live = 1; + s->hotplug_slot.ops = &h2999_ops; + s->hotplug_slot.name = s->name; + s->hotplug_info.power_status = dev->docked; + /* FIXME - should probe here + In truth the hp_register ought to call thse as needed! */ + s->hotplug_info.adapter_status = 0; + s->pdev = pci_find_slot(dev->hotplug_bus, PCI_DEVFN(i, 0)); + snprintf(s->name, SLOT_NAME_SIZE, "Dock%d.%d", dev->hotplug_bus, i); + pci_save_slot(s); + ret = pci_hp_register(&s->hotplug_slot); + if(ret) + { + printk(KERN_ERR "pci_hp_register failed for slot %d with error %d\n", i, ret); + s->live = 0; + } + } + pci_set_drvdata(pdev, dev); + + testdev = dev; + return 0; +nomem: + printk(KERN_ERR "h2999_attach: out of memory.\n"); + return -ENOMEM; +} + +/** + * h2999_cleanup - free H2999 memory resources + * @dev: h2999 device + * + * Unregister and free up all of our slots + */ + +static int __devinit h2999_cleanup(struct h2999_dev *dev) +{ + struct h2999_slot *s; + int slot; + + for(slot = 0; slot < H2999_SLOTS; slot++) + { + s = &dev->slots[slot]; + if(s->live) + pci_hp_deregister(&s->hotplug_slot); + } + kfree(dev); +} + +/** + * h2999_detach - an H2999 controller vanished + * @dev: device that vanished + * + * Called when the PCI layer sees the bridge unplugged. At the moment + * this doesn't happen and since its currently unclear what to do + * in the hot plug layer if it does this may be a good thing 8) + */ + +static void __devinit h2999_detach(struct pci_dev *pdev) +{ + struct h2999_dev *dev = pci_get_drvdata(pdev); + h2999_cleanup(dev); +} + + +static struct pci_device_id h2999_id_tbl[] __devinitdata = { + { PCI_VENDOR_ID_IBM, 0x0095, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, h2999_id_tbl); + +static struct pci_driver h2999_driver = { + name: "h2999", + id_table: h2999_id_tbl, + probe: h2999_attach, + remove: __devexit_p(h2999_detach) + /* FIXME - PM functions */ +}; + +/* + * Test harness + */ + +static struct completion thread_done; + +static int h2999_thread(void *unused) +{ + lock_kernel(); + while(testdev != NULL) + { + set_current_state(TASK_INTERRUPTIBLE); + if(signal_pending(current)) + break; + schedule_timeout(HZ); + h2999_reconfigure_dock(testdev); + } + unlock_kernel(); + complete_and_exit(&thread_done, 0); +} + +static int __init h2999_init_module(void) +{ + int rc; + printk(KERN_INFO "IBM 20H2999 PCI docking bridge driver v0.01\n"); + + init_completion(&thread_done); + + rc = pci_module_init(&h2999_driver); + if (rc == 0) + { + if( kernel_thread(h2999_thread, NULL, CLONE_SIGHAND) >= 0) + return 0; + } + complete(&thread_done); + return rc; +} + +static void __exit h2999_cleanup_module(void) +{ + pci_unregister_driver(&h2999_driver); + wait_for_completion(&thread_done); +} + +module_init(h2999_init_module); +module_exit(h2999_cleanup_module); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("IBM 20H2999 Docking Bridge Driver"); +MODULE_LICENSE("GPL"); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/hotplug/tp600.h linux.21rc1-ac2/drivers/hotplug/tp600.h --- linux.21rc1/drivers/hotplug/tp600.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/hotplug/tp600.h 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,29 @@ +#define SLOT_NAME_SIZE 12 + +struct h2999_slot +{ + int slotid; + + struct hotplug_slot hotplug_slot; + struct hotplug_slot_info hotplug_info; + char name[SLOT_NAME_SIZE]; + + struct h2999_dev *dev; + struct pci_dev *pdev; + int live; + + struct pci_dev *pci[8]; + u32 save[8][64]; + u8 drivermap; +}; + +#define H2999_SLOTS 8 + +struct h2999_dev +{ + int docked; + int hotplug_bus; + struct pci_dev *pdev; + + struct h2999_slot slots[H2999_SLOTS]; +}; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/i2c/i2c-dev.c linux.21rc1-ac2/drivers/i2c/i2c-dev.c --- linux.21rc1/drivers/i2c/i2c-dev.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/i2c/i2c-dev.c 2003-04-22 16:44:37.000000000 +0100 @@ -254,6 +254,11 @@ sizeof(rdwr_arg))) return -EFAULT; + /* Put an arbritrary limit on the number of messages that can + * be sent at once */ + if (rdwr_arg.nmsgs > 42) + return -EINVAL; + rdwr_pa = (struct i2c_msg *) kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); @@ -270,6 +275,11 @@ res = -EFAULT; break; } + /* Limit the size of the message to a sane amount */ + if (rdwr_pa[i].len > 8192) { + res = -EINVAL; + break; + } rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); if(rdwr_pa[i].buf == NULL) { @@ -280,11 +290,17 @@ rdwr_arg.msgs[i].buf, rdwr_pa[i].len)) { - kfree(rdwr_pa[i].buf); res = -EFAULT; break; } } + if (res < 0) { + int j; + for (j = 0; j < i; ++j) + kfree(rdwr_pa[j].buf); + kfree(rdwr_pa); + return res; + } if (!res) { res = i2c_transfer(client->adapter, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/Config.in linux.21rc1-ac2/drivers/ide/Config.in --- linux.21rc1/drivers/ide/Config.in 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/Config.in 2003-04-25 13:43:47.000000000 +0100 @@ -43,7 +43,7 @@ define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL # dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP - dep_tristate ' Pacific Digital ADMA-100 basic support' CONFIG_BLK_DEV_ADMA100 + dep_tristate ' Pacific Digital ADMA-100 basic support' CONFIG_BLK_DEV_ADMA100 $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI dep_mbool ' ALI M15x3 WDC support (DANGEROUS)' CONFIG_WDC_ALI15X3 $CONFIG_BLK_DEV_ALI15X3 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/ide.c linux.21rc1-ac2/drivers/ide/ide.c --- linux.21rc1/drivers/ide/ide.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/ide.c 2003-04-25 14:14:45.000000000 +0100 @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/ide.c Version 7.00beta2 Mar 05 2003 + * linux/drivers/ide/ide.c Version 7.00beta3 Apr 22 2003 * * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ @@ -121,8 +121,8 @@ * */ -#define REVISION "Revision: 7.00beta-2.4" -#define VERSION "Id: ide.c 7.00b1 20021129" +#define REVISION "Revision: 7.00beta3-.2.4" +#define VERSION "Id: ide.c 7.00b3 20030422" #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -1185,13 +1185,12 @@ * * Automatically remove all the driver specific settings for this * drive. This function may sleep and must not be called from IRQ - * context. Takes the settings_lock + * context. Caller must hold the setting lock. */ static void auto_remove_settings (ide_drive_t *drive) { ide_settings_t *setting; - down(&ide_setting_sem); repeat: setting = drive->settings; while (setting) { @@ -1201,7 +1200,6 @@ } setting = setting->next; } - up(&ide_setting_sem); } /** @@ -1407,8 +1405,11 @@ ide_add_setting(drive, "init_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL); ide_add_setting(drive, "current_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate); ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL); +#if 0 + /* Experimental, but this needs the setting/register locking rewritten to be used */ if (drive->media != ide_disk) ide_add_setting(drive, "ide-scsi", SETTING_RW, -1, HDIO_SET_IDE_SCSI, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, ide_atapi_to_scsi); +#endif } /* @@ -2598,10 +2599,12 @@ int ide_unregister_subdriver (ide_drive_t *drive) { unsigned long flags; - + + down(&ide_setting_sem); spin_lock_irqsave(&io_request_lock, flags); if (drive->usage || drive->busy || DRIVER(drive)->busy) { spin_unlock_irqrestore(&io_request_lock, flags); + up(&ide_setting_sem); return 1; } #if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE) @@ -2611,10 +2614,11 @@ ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc); ide_remove_proc_entries(drive->proc, generic_subdriver_entries); #endif - auto_remove_settings(drive); drive->driver = &idedefault_driver; setup_driver_defaults(drive); + auto_remove_settings(drive); spin_unlock_irqrestore(&io_request_lock, flags); + up(&ide_setting_sem); return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/ide-disk.c linux.21rc1-ac2/drivers/ide/ide-disk.c --- linux.21rc1/drivers/ide/ide-disk.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/ide-disk.c 2003-04-25 16:08:11.000000000 +0100 @@ -354,11 +354,12 @@ /* - * do_rw_disk() issues READ and WRITE commands to a disk, + * __ide_do_rw_disk() issues READ and WRITE commands to a disk, * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */ -static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) + +ide_startstop_t __ide_do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { ide_hwif_t *hwif = HWIF(drive); u8 lba48 = (drive->addressing == 1) ? 1 : 0; @@ -370,11 +371,6 @@ if (driver_blocked) panic("Request while ide driver is blocked?"); -#if defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_PDC4030_MODULE) - if (IS_PDC4030_DRIVE) - return promise_rw_disk(drive, rq, block); -#endif /* CONFIG_BLK_DEV_PDC4030 */ - if (IDE_CONTROL_REG) hwif->OUTB(drive->ctl, IDE_CONTROL_REG); @@ -527,6 +523,7 @@ return ide_stopped; } + #else /* CONFIG_IDE_TASKFILE_IO */ static ide_startstop_t chs_rw_disk(ide_drive_t *, struct request *, unsigned long); @@ -534,11 +531,11 @@ static ide_startstop_t lba_48_rw_disk(ide_drive_t *, struct request *, unsigned long long); /* - * do_rw_disk() issues READ and WRITE commands to a disk, + * __ide_do_rw_disk() issues READ and WRITE commands to a disk, * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */ -static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +ide_startstop_t __ide_do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { if (!blk_fs_request(rq)) { printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd); @@ -552,11 +549,6 @@ * need to add split taskfile operations based on 28bit threshold. */ -#if defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_PDC4030_MODULE) - if (IS_PDC4030_DRIVE) - return promise_rw_disk(drive, rq, block); -#endif /* CONFIG_BLK_DEV_PDC4030 */ - if (drive->addressing == 1) /* 48-bit LBA */ return lba_48_rw_disk(drive, rq, (unsigned long long) block); if (drive->select.b.lba) /* 28-bit LBA */ @@ -702,6 +694,17 @@ #endif /* CONFIG_IDE_TASKFILE_IO */ +static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + ide_hwif_t *hwif = HWIF(drive); + if (hwif->rw_disk) + return hwif->rw_disk(drive, rq, block); + else + return __ide_do_rw_disk(drive, rq, block); +} + +EXPORT_SYMBOL_GPL(__ide_do_rw_disk); + static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive) { MOD_INC_USE_COUNT; @@ -1614,11 +1617,6 @@ struct hd_driveid *id = drive->id; unsigned long capacity; -#if 0 - if (IS_PDC4030_DRIVE) - DRIVER(drive)->do_request = promise_rw_disk; -#endif - idedisk_add_settings(drive); if (drive->id_read == 0) @@ -1707,18 +1705,10 @@ drive->mult_count = 0; if (id->max_multsect) { -#ifdef CONFIG_IDEDISK_MULTI_MODE id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0; id->multsect_valid = id->multsect ? 1 : 0; drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT; drive->special.b.set_multmode = drive->mult_req ? 1 : 0; -#else /* original, pre IDE-NFG, per request of AC */ - drive->mult_req = INITIAL_MULT_COUNT; - if (drive->mult_req > id->max_multsect) - drive->mult_req = id->max_multsect; - if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect)) - drive->special.b.set_multmode = 1; -#endif /* CONFIG_IDEDISK_MULTI_MODE */ } drive->no_io_32bit = id->dword_io ? 1 : 0; if (drive->id->cfs_enable_2 & 0x3000) @@ -1749,7 +1739,7 @@ suspend: do_idedisk_suspend, resume: do_idedisk_resume, flushcache: do_idedisk_flushcache, - do_request: do_rw_disk, + do_request: ide_do_rw_disk, end_request: idedisk_end_request, sense: idedisk_dump_status, error: idedisk_error, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/ide-taskfile.c linux.21rc1-ac2/drivers/ide/ide-taskfile.c --- linux.21rc1/drivers/ide/ide-taskfile.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/ide-taskfile.c 2003-04-22 16:44:37.000000000 +0100 @@ -854,11 +854,6 @@ rq->errors = 0; return ide_started; #else /* ! ALTERNATE_STATE_DIAGRAM_MULTI_OUT */ - -#if 0 - if (wait_for_ready(drive, 100)) - IDE_DEBUG(__LINE__); //BUG(); -#else if (!(drive_is_ready(drive))) { int i; for (i=0; i<100; i++) { @@ -866,7 +861,7 @@ break; } } -#endif + /* * WARNING :: if the drive as not acked good status we may not * move the DATA-TRANSFER T-Bar as BSY != 0. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/legacy/pdc4030.c linux.21rc1-ac2/drivers/ide/legacy/pdc4030.c --- linux.21rc1/drivers/ide/legacy/pdc4030.c 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/legacy/pdc4030.c 2003-04-25 14:51:20.000000000 +0100 @@ -94,6 +94,8 @@ #include "pdc4030.h" +static ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block); + /* * promise_selectproc() is invoked by ide.c * in preparation for access to the specified drive. @@ -256,6 +258,11 @@ if (!ident.current_tm[i+2].cyl) hwif2->drives[i].noprobe = 1; } + + /* Now override the normal ide disk read/write */ + hwif->rw_disk = promise_rw_disk; + hwif2->rw_disk = promise_rw_disk; + #ifndef HWIF_PROBE_CLASSIC_METHOD probe_hwif_init(&ide_hwifs[hwif->index]); probe_hwif_init(&ide_hwifs[hwif2->index]); @@ -796,7 +803,7 @@ } } -ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { /* The four drives on the two logical (one physical) interfaces are distinguished by writing the drive number (0-3) to the diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/Makefile linux.21rc1-ac2/drivers/ide/Makefile --- linux.21rc1/drivers/ide/Makefile 2003-04-22 16:39:34.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/Makefile 2003-04-25 16:12:35.000000000 +0100 @@ -10,7 +10,7 @@ O_TARGET := idedriver.o -export-objs := ide-iops.o ide-taskfile.o ide-proc.o ide.o ide-probe.o ide-dma.o ide-lib.o setup-pci.o ide-io.o +export-objs := ide-iops.o ide-taskfile.o ide-proc.o ide.o ide-probe.o ide-dma.o ide-lib.o setup-pci.o ide-io.o ide-disk.o all-subdirs := arm legacy pci ppc raid mod-subdirs := arm legacy pci ppc raid @@ -35,8 +35,12 @@ obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o -obj-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o -obj-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o +ifeq ($(CONFIG_BLK_DEV_IDEPCI),y) +obj-$(CONFIG_BLK_DEV_IDE) += setup-pci.o +endif +ifeq ($(CONFIG_BLK_DEV_IDEDMA_PCI),y) +obj-$(CONFIG_BLK_DEV_IDE) += ide-dma.o +endif obj-$(CONFIG_BLK_DEV_ISAPNP) += ide-pnp.o @@ -48,6 +52,10 @@ obj-y += legacy/idedriver-legacy.o obj-y += ppc/idedriver-ppc.o obj-y += arm/idedriver-arm.o +else + ifeq ($(CONFIG_BLK_DEV_HD_ONLY),y) + obj-y += legacy/idedriver-legacy.o + endif endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/pci/alim15x3.c linux.21rc1-ac2/drivers/ide/pci/alim15x3.c --- linux.21rc1/drivers/ide/pci/alim15x3.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/pci/alim15x3.c 2003-04-22 16:44:37.000000000 +0100 @@ -368,7 +368,7 @@ * Check the drive and controller revisions. Return 0 if UDMA is * not available, or 1 if UDMA can be used. The actual rules for * the ALi are - * No UDMA on revisions <= 0x20 + * No UDMA on revisions <= 0xC1 * Disk only for revisions < 0xC2 * Not WDC drives for revisions < 0xC2 * diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/pci/hpt366.c linux.21rc1-ac2/drivers/ide/pci/hpt366.c --- linux.21rc1/drivers/ide/pci/hpt366.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/pci/hpt366.c 2003-04-25 16:23:32.000000000 +0100 @@ -1,8 +1,9 @@ /* - * linux/drivers/ide/pci/hpt366.c Version 0.35 March 18, 2003 + * linux/drivers/ide/pci/hpt366.c Version 0.36 April 25, 2003 * * Copyright (C) 1999-2002 Andre Hedrick * Portions Copyright (C) 2001 Sun Microsystems, Inc. + * Portions Copyright (C) 2003 Red Hat Inc * * Thanks to HighPoint Technologies for their assistance, and hardware. * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his @@ -43,6 +44,13 @@ * Reset the hpt366 on error, reset on dma * Fix disabling Fast Interrupt hpt366. * Mike Waychison + * + * Added support for 372N clocking and clock switching. The 372N needs + * different clocks on read/write. This requires overloading rw_disk and + * other deeply crazy things. Thanks to for + * keeping me sane. + * Alan Cox + * */ @@ -671,6 +679,69 @@ return __ide_dma_end(drive); } +/** + * hpt372n_set_clock - perform clock switching dance + * @drive: Drive to switch + * @mode: Switching mode (0x21 for write, 0x23 otherwise) + * + * Switch the DPLL clock on the HPT372N devices. This is a + * right mess. + */ + +static void hpt372n_set_clock(ide_drive_t *drive, int mode) +{ + ide_hwif_t *hwif = HWIF(drive); + + /* FIXME: should we check for DMA active and BUG() */ + /* Tristate the bus */ + outb(0x80, hwif->dma_base+0x73); + outb(0x80, hwif->dma_base+0x77); + + /* Switch clock and reset channels */ + outb(mode, hwif->dma_base+0x7B); + outb(0xC0, hwif->dma_base+0x79); + + /* Reset state machines */ + outb(0x37, hwif->dma_base+0x70); + outb(0x37, hwif->dma_base+0x74); + + /* Complete reset */ + outb(0x00, hwif->dma_base+0x79); + + /* Reconnect channels to bus */ + outb(0x00, hwif->dma_base+0x73); + outb(0x00, hwif->dma_base+0x79); +} + +/** + * hpt372n_rw_disk - wrapper for I/O + * @drive: drive for command + * @rq: block request structure + * @block: block number + * + * This is called when a disk I/O is issued to the 372N instead + * of the default functionality. We need it because of the clock + * switching + * + */ + +static ide_startstop_t hpt372n_rw_disk(ide_drive_t *drive, struct request *rq, unsigned long block) +{ + int wantclock; + + if(rq_data_dir(rq) == READ) + wantclock = 0x21; + else + wantclock = 0x23; + + if(HWIF(drive)->config_data != wantclock) + { + hpt372n_set_clock(drive, wantclock); + HWIF(drive)->config_data = wantclock; + } + return __ide_do_rw_disk(drive, rq, block); +} + /* * Since SUN Cobalt is attempting to do this operation, I should disclose * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date @@ -810,14 +881,14 @@ pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); #endif - did = inb(dmabase + 0x22); - rid = inb(dmabase + 0x28); + if(dmabase) + { + did = inb(dmabase + 0x22); + rid = inb(dmabase + 0x28); - if((did == 4 && rid == 6) || (did == 5 && rid > 1)) - is_372n = 1; - - if(is_372n) - printk(KERN_ERR "HPT372N support is EXPERIMENTAL ONLY.\n"); + if((did == 4 && rid == 6) || (did == 5 && rid > 1)) + is_372n = 1; + } /* * default to pci clock. make sure MA15/16 are set to output @@ -1062,12 +1133,30 @@ { struct pci_dev *dev = hwif->pci_dev; u8 ata66 = 0, regmask = (hwif->channel) ? 0x01 : 0x02; - + u8 did, rid; + unsigned long dmabase = hwif->dma_base; + int is_372n = 0; + + if(dmabase) + { + did = inb(dmabase + 0x22); + rid = inb(dmabase + 0x28); + + if((did == 4 && rid == 6) || (did == 5 && rid > 1)) + is_372n = 1; + } + + if(is_372n) + printk(KERN_ERR "HPT372N support is EXPERIMENTAL ONLY.\n"); + hwif->tuneproc = &hpt3xx_tune_drive; hwif->speedproc = &hpt3xx_tune_chipset; hwif->quirkproc = &hpt3xx_quirkproc; hwif->intrproc = &hpt3xx_intrproc; hwif->maskproc = &hpt3xx_maskproc; + + if(is_372n) + hwif->rw_disk = &hpt372n_rw_disk; pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/pci/serverworks.c linux.21rc1-ac2/drivers/ide/pci/serverworks.c --- linux.21rc1/drivers/ide/pci/serverworks.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/pci/serverworks.c 2003-04-25 14:12:03.000000000 +0100 @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/pci/serverworks.c Version 0.7 10 Sept 2002 + * linux/drivers/ide/pci/serverworks.c Version 0.8 25 Ebr 2003 * * Copyright (C) 1998-2000 Michel Aubry * Copyright (C) 1998-2000 Andrzej Krzysztofowicz @@ -203,11 +203,22 @@ } #endif /* defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) */ +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + while (*list) + if (!strcmp(*list++, drive->id->model)) + return 1; + return 0; +} + static u8 svwks_ratemask (ide_drive_t *drive) { struct pci_dev *dev = HWIF(drive)->pci_dev; u8 mode; + if (!svwks_revision) + pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision); + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { u32 reg = 0; if (isa_dev) @@ -225,9 +236,13 @@ } else if (svwks_revision >= SVWKS_CSB5_REVISION_NEW) { u8 btr = 0; pci_read_config_byte(dev, 0x5A, &btr); - mode = btr; + mode = btr & 0x3; if (!eighty_ninty_three(drive)) mode = min(mode, (u8)1); + /* If someone decides to do UDMA133 on CSB5 the same + issue will bite so be inclusive */ + if (mode > 2 && check_in_drive_lists(drive, svwks_bad_ata100)) + mode = 2; } if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) && @@ -419,7 +434,10 @@ static void svwks_tune_drive (ide_drive_t *drive, u8 pio) { - (void) svwks_tune_chipset(drive, (XFER_PIO_0 + pio)); + if(pio == 255) + (void) svwks_tune_chipset(drive, 255); + else + (void) svwks_tune_chipset(drive, (XFER_PIO_0 + pio)); } static int config_chipset_for_dma (ide_drive_t *drive) @@ -477,28 +495,10 @@ return hwif->ide_dma_on(drive); } +/* This can go soon */ + static int svwks_ide_dma_end (ide_drive_t *drive) { - /* - * We never place the OSB4 into a UDMA mode with a disk - * medium, that means the UDMA "all my data is 4 byte shifted" - * problem cannot occur. - */ -#if 0 - ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); - - if ((dma_stat & 1) && drive->media == ide_disk) - { - printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n"); - printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n"); - printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n"); - /* Panic might sys_sync -> death by corrupt disk */ - printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n"); - while(1) - cpu_relax(); - } -#endif return __ide_dma_end(drive); } @@ -624,8 +624,6 @@ static unsigned int __init ata66_svwks_svwks (ide_hwif_t *hwif) { -// struct pci_dev *dev = hwif->pci_dev; -// return 0; return 1; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/pci/serverworks.h linux.21rc1-ac2/drivers/ide/pci/serverworks.h --- linux.21rc1/drivers/ide/pci/serverworks.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/pci/serverworks.h 2003-04-25 16:10:30.000000000 +0100 @@ -11,6 +11,16 @@ #define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ #define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ +/* Seagate Barracuda ATA IV Family drives in UDMA mode 5 + * can overrun their FIFOs when used with the CSB5 */ +const char *svwks_bad_ata100[] = { + "ST320011A", + "ST340016A", + "ST360021A", + "ST380021A", + NULL +}; + #define DISPLAY_SVWKS_TIMINGS 1 #if defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ide/raid/hptraid.c linux.21rc1-ac2/drivers/ide/raid/hptraid.c --- linux.21rc1/drivers/ide/raid/hptraid.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ide/raid/hptraid.c 2003-04-22 16:44:37.000000000 +0100 @@ -38,7 +38,9 @@ static int hptraid_open(struct inode * inode, struct file * filp); static int hptraid_release(struct inode * inode, struct file * filp); static int hptraid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -static int hptraid_make_request (request_queue_t *q, int rw, struct buffer_head * bh); +static int hptraidspan_make_request (request_queue_t *q, int rw, struct buffer_head * bh); +static int hptraid0_make_request (request_queue_t *q, int rw, struct buffer_head * bh); +static int hptraid1_make_request (request_queue_t *q, int rw, struct buffer_head * bh); @@ -46,28 +48,68 @@ kdev_t device; unsigned long sectors; struct block_device *bdev; + unsigned long last_pos; }; struct hptraid { unsigned int stride; unsigned int disks; unsigned long sectors; + u_int32_t magic_0; struct geom geom; struct hptdisk disk[8]; - unsigned long cutoff[8]; unsigned int cutoff_disks[8]; }; -static struct raid_device_operations hptraid_ops = { +struct hptraid_dev { + int major; + int minor; + int device; +}; + +static struct hptraid_dev devlist[]= +{ + + {IDE0_MAJOR, 0, -1}, + {IDE0_MAJOR, 64, -1}, + {IDE1_MAJOR, 0, -1}, + {IDE1_MAJOR, 64, -1}, + {IDE2_MAJOR, 0, -1}, + {IDE2_MAJOR, 64, -1}, + {IDE3_MAJOR, 0, -1}, + {IDE3_MAJOR, 64, -1}, + {IDE4_MAJOR, 0, -1}, + {IDE4_MAJOR, 64, -1}, + {IDE5_MAJOR, 0, -1}, + {IDE5_MAJOR, 64, -1}, + {IDE6_MAJOR, 0, -1}, + {IDE6_MAJOR, 64, -1} +}; + +static struct raid_device_operations hptraidspan_ops = { + open: hptraid_open, + release: hptraid_release, + ioctl: hptraid_ioctl, + make_request: hptraidspan_make_request +}; + +static struct raid_device_operations hptraid0_ops = { + open: hptraid_open, + release: hptraid_release, + ioctl: hptraid_ioctl, + make_request: hptraid0_make_request +}; + +static struct raid_device_operations hptraid1_ops = { open: hptraid_open, release: hptraid_release, ioctl: hptraid_ioctl, - make_request: hptraid_make_request + make_request: hptraid1_make_request }; -static struct hptraid raid[16]; +static struct hptraid raid[14]; static int hptraid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -131,7 +173,50 @@ } -static int hptraid_make_request (request_queue_t *q, int rw, struct buffer_head * bh) +static int hptraidspan_make_request (request_queue_t *q, int rw, struct buffer_head * bh) +{ + unsigned long rsect; + unsigned int disk; + int device; + struct hptraid *thisraid; + + rsect = bh->b_rsector; + + device = (bh->b_rdev >> SHIFT)&MAJOR_MASK; + thisraid = &raid[device]; + + /* Partitions need adding of the start sector of the partition to the requested sector */ + + rsect += ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect; + + for (disk=0;diskdisks;disk++) { + if (disk==1) + rsect+=10; + // the "on next disk" contition check is a bit odd + if (thisraid->disk[disk].sectors > rsect+1) + break; + rsect-=thisraid->disk[disk].sectors-(disk?11:1); + } + + // request spans over 2 disks => request must be split + if(rsect+bh->b_size/512 >= thisraid->disk[disk].sectors) + return -1; + + /* + * The new BH_Lock semantics in ll_rw_blk.c guarantee that this + * is the only IO operation happening on this bh. + */ + + bh->b_rdev = thisraid->disk[disk].device; + bh->b_rsector = rsect; + + /* + * Let the main block layer submit the IO and resolve recursion: + */ + return 1; +} + +static int hptraid0_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { unsigned long rsect; unsigned long rsect_left,rsect_accum = 0; @@ -218,6 +303,106 @@ return 1; } +static int hptraid1_read_request (request_queue_t *q, int rw, struct buffer_head * bh) +{ + int device; + int dist; + int bestsofar,bestdist,i; + static int previous; + + /* Reads are simple in principle. Pick a disk and go. + Initially I cheat by just picking the one which the last known + head position is closest by. + Later on, online/offline checking and performance needs adding */ + + device = (bh->b_rdev >> SHIFT)&MAJOR_MASK; + bh->b_rsector += ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect; + + bestsofar = 0; + bestdist = raid[device].disk[0].last_pos - bh->b_rsector; + if (bestdist<0) + bestdist=-bestdist; + if (bestdist>4095) + bestdist=4095; + + for (i=1 ; ib_rsector; + if (dist<0) + dist = -dist; + if (dist>4095) + dist=4095; + + if (bestdist==dist) { /* it's a tie; try to do some read balancing */ + if ((previous>bestsofar)&&(previous<=i)) + bestsofar = i; + previous = (previous + 1) % raid[device].disks; + } else if (bestdist>dist) { + bestdist = dist; + bestsofar = i; + } + + } + + bh->b_rsector += bestsofar?10:0; + bh->b_rdev = raid[device].disk[bestsofar].device; + raid[device].disk[bestsofar].last_pos = bh->b_rsector+(bh->b_size>>9); + + /* + * Let the main block layer submit the IO and resolve recursion: + */ + + return 1; +} + +static int hptraid1_write_request(request_queue_t *q, int rw, struct buffer_head * bh) +{ + struct buffer_head *bh1; + struct ataraid_bh_private *private; + int device; + int i; + + device = (bh->b_rdev >> SHIFT)&MAJOR_MASK; + private = ataraid_get_private(); + if (private==NULL) + BUG(); + + private->parent = bh; + + atomic_set(&private->count,raid[device].disks); + + + for (i = 0; i< raid[device].disks; i++) { + bh1=ataraid_get_bhead(); + /* If this ever fails we're doomed */ + if (!bh1) + BUG(); + + /* dupe the bufferhead and update the parts that need to be different */ + memcpy(bh1, bh, sizeof(*bh)); + + bh1->b_end_io = ataraid_end_request; + bh1->b_private = private; + bh1->b_rsector += ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect+(i==0?0:10); /* partition offset */ + bh1->b_rdev = raid[device].disk[i].device; + + /* update the last known head position for the drive */ + raid[device].disk[i].last_pos = bh1->b_rsector+(bh1->b_size>>9); + + generic_make_request(rw,bh1); + } + return 0; +} + +static int hptraid1_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { + /* Read and Write are totally different cases; split them totally here */ + if (rw==READA) + rw = READ; + + if (rw==READ) + return hptraid1_read_request(q,rw,bh); + else + return hptraid1_write_request(q,rw,bh); +} #include "hptraid.h" @@ -271,35 +456,49 @@ return lba; } -static void __init probedisk(int major, int minor,int device) +static void __init probedisk(struct hptraid_dev *disk, int device, u_int8_t type) { int i; struct highpoint_raid_conf *prom; static unsigned char block[4096]; struct block_device *bdev; - if (maxsectors(major,minor)==0) + if (disk->device != -1) /* disk is occupied? */ + return; + + if (maxsectors(disk->major,disk->minor)==0) return; - if (read_disk_sb(major,minor,(unsigned char*)&block,sizeof(block))) + if (read_disk_sb(disk->major,disk->minor,(unsigned char*)&block,sizeof(block))) return; prom = (struct highpoint_raid_conf*)&block[512]; if (prom->magic!= 0x5a7816f0) return; - if (prom->type) { - printk(KERN_INFO "hptraid: only RAID0 is supported currently\n"); - return; + switch (prom->type) { + case HPT_T_SPAN: + case HPT_T_RAID_0: + case HPT_T_RAID_1: + if(prom->type != type) + return; + break; + default: + printk(KERN_INFO "hptraid: only SPAN, RAID0 and RAID1 is currently supported \n"); + return; } + /* disk from another array? */ + if(raid[device].disks && prom->magic_0 != raid[device].magic_0) + return; + i = prom->disk_number; if (i<0) return; if (i>8) return; - bdev = bdget(MKDEV(major,minor)); + bdev = bdget(MKDEV(disk->major,disk->minor)); if (bdev && blkdev_get(bdev,FMODE_READ|FMODE_WRITE,0,BDEV_RAW) == 0) { int j=0; struct gendisk *gd; @@ -308,17 +507,22 @@ /* now blank the /proc/partitions table for the wrong partition table, so that scripts don't accidentally mount it and crash the kernel */ /* XXX: the 0 is an utter hack --hch */ - gd=get_gendisk(MKDEV(major, 0)); + gd=get_gendisk(MKDEV(disk->major, 0)); if (gd!=NULL) { - for (j=1+(minor<minor_shift);j<((minor+1)<minor_shift);j++) - gd->part[j].nr_sects=0; + if (gd->major==disk->major) + for (j=1+(disk->minor<minor_shift); + j<((disk->minor+1)<minor_shift); + j++) gd->part[j].nr_sects=0; } } - raid[device].disk[i].device = MKDEV(major,minor); - raid[device].disk[i].sectors = maxsectors(major,minor); + raid[device].disk[i].device = MKDEV(disk->major,disk->minor); + raid[device].disk[i].sectors = maxsectors(disk->major,disk->minor); raid[device].stride = (1<raid0_shift); raid[device].disks = prom->raid_disks; - raid[device].sectors = prom->total_secs; + raid[device].sectors = prom->total_secs-(prom->total_secs%(255*63)); + raid[device].sectors += raid[device].sectors&1?1:0; + raid[device].magic_0=prom->magic_0; + disk->device=device; } @@ -349,31 +553,23 @@ } -static __init int hptraid_init_one(int device) +static __init int hptraid_init_one(int device, u_int8_t type) { int i,count; - probedisk(IDE0_MAJOR, 0, device); - probedisk(IDE0_MAJOR, 64, device); - probedisk(IDE1_MAJOR, 0, device); - probedisk(IDE1_MAJOR, 64, device); - probedisk(IDE2_MAJOR, 0, device); - probedisk(IDE2_MAJOR, 64, device); - probedisk(IDE3_MAJOR, 0, device); - probedisk(IDE3_MAJOR, 64, device); - probedisk(IDE4_MAJOR, 0, device); - probedisk(IDE4_MAJOR, 64, device); - probedisk(IDE5_MAJOR, 0, device); - probedisk(IDE5_MAJOR, 64, device); + memset(raid+device, 0, sizeof(struct hptraid)); + for(i=0; i < 14; i++) { + probedisk(devlist+i, device, type); + } - fill_cutoff(device); + if(type == HPT_T_RAID_0) + fill_cutoff(device); /* Initialize the gendisk structure */ ataraid_register_disk(device,raid[device].sectors); count=0; - printk(KERN_INFO "Highpoint HPT370 Softwareraid driver for linux version 0.01\n"); for (i=0;i<8;i++) { if (raid[device].disk[i].device!=0) { @@ -394,15 +590,44 @@ static __init int hptraid_init(void) { - int retval,device; - - device=ataraid_get_device(&hptraid_ops); - if (device<0) - return -ENODEV; - retval = hptraid_init_one(device); - if (retval) - ataraid_release_device(device); - return retval; + int retval,device,count=0; + + printk(KERN_INFO "Highpoint HPT370 Softwareraid driver for linux version 0.01-ww1\n"); + + do + { + device=ataraid_get_device(&hptraid0_ops); + if (device<0) + return (count?0:-ENODEV); + retval = hptraid_init_one(device, HPT_T_RAID_0); + if (retval) + ataraid_release_device(device); + else + count++; + } while(!retval); + do + { + device=ataraid_get_device(&hptraid1_ops); + if (device<0) + return (count?0:-ENODEV); + retval = hptraid_init_one(device, HPT_T_RAID_1); + if (retval) + ataraid_release_device(device); + else + count++; + } while(!retval); + do + { + device=ataraid_get_device(&hptraidspan_ops); + if (device<0) + return (count?0:-ENODEV); + retval = hptraid_init_one(device, HPT_T_SPAN); + if (retval) + ataraid_release_device(device); + else + count++; + } while(!retval); + return (count?0:retval); } static void __exit hptraid_exit (void) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/amdtp.c linux.21rc1-ac2/drivers/ieee1394/amdtp.c --- linux.21rc1/drivers/ieee1394/amdtp.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/amdtp.c 2003-04-25 03:38:30.000000000 +0100 @@ -228,7 +228,7 @@ /* The cycle_count and cycle_offset fields are used for the * synchronization timestamps (syt) in the cip header. They * are incremented by at least a cycle every time we put a - * time stamp in a packet. As we dont time stamp all + * time stamp in a packet. As we don't time stamp all * packages, cycle_count isn't updated in every cycle, and * sometimes it's incremented by 2. Thus, we have * cycle_count2, which is simply incremented by one with each @@ -263,13 +263,13 @@ struct hpsb_host *host; struct ti_ohci *ohci; struct list_head stream_list; + devfs_handle_t devfs; spinlock_t stream_list_lock; - struct list_head link; }; -static struct hpsb_highlevel *amdtp_highlevel; -static LIST_HEAD(host_list); -static spinlock_t host_list_lock = SPIN_LOCK_UNLOCKED; +static devfs_handle_t devfs_handle; + +static struct hpsb_highlevel amdtp_highlevel; /* FIXME: This doesn't belong here... */ @@ -345,7 +345,7 @@ { u32 syt_cycle, cycle_count, start_cycle; - cycle_count = reg_read(s->host->host->hostdata, + cycle_count = reg_read(s->host->ohci, OHCI1394_IsochronousCycleTimer) >> 12; syt_cycle = (pl->last_cycle_count - PACKET_LIST_SIZE + 1) & 0x0f; @@ -690,7 +690,7 @@ return get_iec958_header_bits(s, sub_frame, sample); case AMDTP_FORMAT_RAW: - return 0x40000000; + return 0x40; default: return 0; @@ -748,7 +748,7 @@ /* This next addition should be modulo 8000 (0x1f40), * but we only use the lower 4 bits of cycle_count, so - * we dont need the modulo. */ + * we don't need the modulo. */ atomic_add(s->cycle_offset.integer / 3072, &s->cycle_count); s->cycle_offset.integer %= 3072; } @@ -1111,7 +1111,7 @@ */ for (i = 0; i < count; i += length) { - p = buffer_put_bytes(s->input, count, &length); + p = buffer_put_bytes(s->input, count - i, &length); copy_from_user(p, buffer + i, length); if (s->input->length < s->input->size) continue; @@ -1167,15 +1167,9 @@ static int amdtp_open(struct inode *inode, struct file *file) { struct amdtp_host *host; + int i = ieee1394_file_to_instance(file); - /* FIXME: We just grab the first registered host */ - spin_lock(&host_list_lock); - if (!list_empty(&host_list)) - host = list_entry(host_list.next, struct amdtp_host, link); - else - host = NULL; - spin_unlock(&host_list_lock); - + host = hpsb_get_hostinfo_bykey(&amdtp_highlevel, i); if (host == NULL) return -ENODEV; @@ -1210,44 +1204,49 @@ static void amdtp_add_host(struct hpsb_host *host) { struct amdtp_host *ah; + int minor; + char name[16]; if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME) != 0) return; - ah = kmalloc(sizeof *ah, SLAB_KERNEL); + ah = hpsb_create_hostinfo(&amdtp_highlevel, host, sizeof(*ah)); + if (!ah) { + HPSB_ERR("amdtp: Unable able to alloc hostinfo"); + return; + } + ah->host = host; ah->ohci = host->hostdata; + + hpsb_set_hostinfo_key(&amdtp_highlevel, host, ah->ohci->id); + + minor = IEEE1394_MINOR_BLOCK_AMDTP * 16 + ah->ohci->id; + + sprintf(name, "%d", ah->ohci->id); + INIT_LIST_HEAD(&ah->stream_list); spin_lock_init(&ah->stream_list_lock); - spin_lock_irq(&host_list_lock); - list_add_tail(&ah->link, &host_list); - spin_unlock_irq(&host_list_lock); + ah->devfs = devfs_register(devfs_handle, name, + DEVFS_FL_AUTO_OWNER, + IEEE1394_MAJOR, minor, + S_IFCHR | S_IRUSR | S_IWUSR, + &amdtp_fops, NULL); } static void amdtp_remove_host(struct hpsb_host *host) { - struct list_head *lh; - struct amdtp_host *ah; + struct amdtp_host *ah = hpsb_get_hostinfo(&amdtp_highlevel, host); - spin_lock_irq(&host_list_lock); - list_for_each(lh, &host_list) { - if (list_entry(lh, struct amdtp_host, link)->host == host) { - list_del(lh); - break; - } - } - spin_unlock_irq(&host_list_lock); - - if (lh != &host_list) { - ah = list_entry(lh, struct amdtp_host, link); - kfree(ah); - } - else - HPSB_ERR("remove_host: bogus ohci host: %p", host); + if (ah) + devfs_unregister(ah->devfs); + + return; } -static struct hpsb_highlevel_ops amdtp_highlevel_ops = { +static struct hpsb_highlevel amdtp_highlevel = { + .name = "amdtp", .add_host = amdtp_add_host, .remove_host = amdtp_remove_host, }; @@ -1268,13 +1267,9 @@ return -EIO; } - amdtp_highlevel = hpsb_register_highlevel ("amdtp", - &amdtp_highlevel_ops); - if (amdtp_highlevel == NULL) { - HPSB_ERR("amdtp: unable to register highlevel ops"); - ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_AMDTP); - return -EIO; - } + devfs_handle = devfs_mk_dir(NULL, "amdtp", NULL); + + hpsb_register_highlevel(&amdtp_highlevel); HPSB_INFO("Loaded AMDTP driver"); @@ -1283,7 +1278,8 @@ static void __exit amdtp_exit_module (void) { - hpsb_unregister_highlevel(amdtp_highlevel); + hpsb_unregister_highlevel(&amdtp_highlevel); + devfs_unregister(devfs_handle); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_AMDTP); HPSB_INFO("Unloaded AMDTP driver"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/cmp.c linux.21rc1-ac2/drivers/ieee1394/cmp.c --- linux.21rc1/drivers/ieee1394/cmp.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/cmp.c 2003-04-25 03:38:30.000000000 +0100 @@ -64,8 +64,6 @@ quadlet_t impr_quadlet; } v; struct plug ipcr[2]; - - struct list_head link; }; enum { @@ -76,32 +74,7 @@ #define CSR_PCR_MAP 0x900 #define CSR_PCR_MAP_END 0x9fc -static struct hpsb_highlevel *cmp_highlevel; - -static LIST_HEAD(host_list); -static spinlock_t host_list_lock = SPIN_LOCK_UNLOCKED; - -static struct cmp_host * -lookup_cmp_host(struct hpsb_host *host) -{ - struct cmp_host *ch; - struct list_head *lh; - unsigned long flags; - - ch = NULL; - spin_lock_irqsave(&host_list_lock, flags); - list_for_each(lh, &host_list) { - ch = list_entry(lh, struct cmp_host, link); - if (ch->host == host) - break; - } - spin_unlock_irqrestore(&host_list_lock, flags); - - if (lh == &host_list) - return NULL; - else - return ch; -} +static struct hpsb_highlevel cmp_highlevel; struct cmp_pcr * cmp_register_opcr(struct hpsb_host *host, int opcr_number, int payload, @@ -111,7 +84,7 @@ struct cmp_host *ch; struct plug *plug; - ch = lookup_cmp_host(host); + ch = hpsb_get_hostinfo(&cmp_highlevel, host); if (opcr_number >= ch->u.ompr.nplugs || ch->opcr[opcr_number].update != NULL) @@ -134,7 +107,7 @@ struct cmp_host *ch; struct plug *plug; - ch = lookup_cmp_host(host); + ch = hpsb_get_hostinfo(&cmp_highlevel, host); plug = (struct plug *)opcr; if (plug - ch->opcr >= ch->u.ompr.nplugs) BUG(); @@ -156,46 +129,32 @@ static void cmp_add_host(struct hpsb_host *host) { - struct cmp_host *ch; + struct cmp_host *ch = hpsb_create_hostinfo(&cmp_highlevel, host, sizeof (*ch)); - ch = kmalloc(sizeof *ch, SLAB_KERNEL); if (ch == NULL) { HPSB_ERR("Failed to allocate cmp_host"); return; } - memset(ch, 0, sizeof *ch); + ch->host = host; ch->u.ompr.rate = SPEED_100; ch->u.ompr.bcast_channel_base = 63; ch->u.ompr.nplugs = 2; - reset_plugs(ch); - spin_lock_irq(&host_list_lock); - list_add_tail(&ch->link, &host_list); - spin_unlock_irq(&host_list_lock); -} - -static void cmp_host_reset(struct hpsb_host *host) -{ - struct cmp_host *ch; - - ch = lookup_cmp_host(host); - if (ch == NULL) BUG(); reset_plugs(ch); } -static void cmp_remove_host(struct hpsb_host *host) +static void cmp_host_reset(struct hpsb_host *host) { struct cmp_host *ch; - ch = lookup_cmp_host(host); - if (ch == NULL) BUG(); - - spin_lock_irq(&host_list_lock); - list_del(&ch->link); - spin_unlock_irq(&host_list_lock); + ch = hpsb_get_hostinfo(&cmp_highlevel, host); + if (ch == NULL) { + HPSB_ERR("cmp: Tried to reset unknown host"); + return; + } - kfree(ch); + reset_plugs(ch); } static int pcr_read(struct hpsb_host *host, int nodeid, quadlet_t *buf, @@ -208,7 +167,7 @@ if (length != 4) return RCODE_TYPE_ERROR; - ch = lookup_cmp_host(host); + ch = hpsb_get_hostinfo(&cmp_highlevel, host); if (csraddr == 0x900) { *buf = cpu_to_be32(ch->u.ompr_quadlet); return RCODE_COMPLETE; @@ -241,7 +200,7 @@ int plug; struct cmp_host *ch; - ch = lookup_cmp_host(host); + ch = hpsb_get_hostinfo(&cmp_highlevel, host); if (extcode != EXTCODE_COMPARE_SWAP) return RCODE_TYPE_ERROR; @@ -298,9 +257,9 @@ } -static struct hpsb_highlevel_ops cmp_highlevel_ops = { +static struct hpsb_highlevel cmp_highlevel = { + .name = "cmp", .add_host = cmp_add_host, - .remove_host = cmp_remove_host, .host_reset = cmp_host_reset, }; @@ -321,14 +280,9 @@ static int __init cmp_init_module (void) { - cmp_highlevel = hpsb_register_highlevel ("cmp", - &cmp_highlevel_ops); - if (cmp_highlevel == NULL) { - HPSB_ERR("cmp: unable to register highlevel ops"); - return -EIO; - } + hpsb_register_highlevel (&cmp_highlevel); - hpsb_register_addrspace(cmp_highlevel, &pcr_ops, + hpsb_register_addrspace(&cmp_highlevel, &pcr_ops, CSR_REGISTER_BASE + CSR_PCR_MAP, CSR_REGISTER_BASE + CSR_PCR_MAP_END); @@ -339,7 +293,7 @@ static void __exit cmp_exit_module (void) { - hpsb_unregister_highlevel(cmp_highlevel); + hpsb_unregister_highlevel(&cmp_highlevel); HPSB_INFO("Unloaded CMP driver"); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/Config.in linux.21rc1-ac2/drivers/ieee1394/Config.in --- linux.21rc1/drivers/ieee1394/Config.in 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/Config.in 2003-04-23 16:49:51.000000000 +0100 @@ -40,6 +40,7 @@ fi bool 'Excessive debugging output' CONFIG_IEEE1394_VERBOSEDEBUG + bool "OUI Database built-in" CONFIG_IEEE1394_OUI_DB fi endmenu fi diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/csr.c linux.21rc1-ac2/drivers/ieee1394/csr.c --- linux.21rc1/drivers/ieee1394/csr.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/csr.c 2003-04-25 03:38:30.000000000 +0100 @@ -116,7 +116,9 @@ int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom, size_t size, unsigned char rom_version) { - int ret,flags; + unsigned long flags; + int ret; + spin_lock_irqsave(&host->csr.lock, flags); if (rom_version != host->csr.rom_version) ret = -1; @@ -135,7 +137,9 @@ int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer, size_t buffersize, size_t *rom_size, unsigned char *rom_version) { - int ret,flags; + unsigned long flags; + int ret; + spin_lock_irqsave(&host->csr.lock, flags); *rom_version=host->csr.rom_version; *rom_size=host->csr.rom_size; @@ -154,9 +158,9 @@ static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer, u64 addr, unsigned int length, u16 fl) { + unsigned long flags; int csraddr = addr - CSR_REGISTER_BASE; const char *src; - int flags; spin_lock_irqsave(&host->csr.lock, flags); @@ -442,7 +446,7 @@ /* bandwidth available algorithm adapted from IEEE 1394a-2000 spec */ if (arg > 0x1fff) { *store = cpu_to_be32(old); /* change nothing */ - break; + break; } data &= 0x1fff; if (arg >= data) { @@ -641,7 +645,8 @@ } -static struct hpsb_highlevel_ops csr_ops = { +static struct hpsb_highlevel csr_highlevel = { + .name = "standard registers", .add_host = add_host, .host_reset = host_reset, }; @@ -662,35 +667,29 @@ .lock64 = lock64_regs, }; -static struct hpsb_highlevel *hl; - void init_csr(void) { - hl = hpsb_register_highlevel("standard registers", &csr_ops); - if (hl == NULL) { - HPSB_ERR("out of memory during ieee1394 initialization"); - return; - } + hpsb_register_highlevel(&csr_highlevel); - hpsb_register_addrspace(hl, ®_ops, CSR_REGISTER_BASE, + hpsb_register_addrspace(&csr_highlevel, ®_ops, CSR_REGISTER_BASE, CSR_REGISTER_BASE + CSR_CONFIG_ROM); - hpsb_register_addrspace(hl, &map_ops, + hpsb_register_addrspace(&csr_highlevel, &map_ops, CSR_REGISTER_BASE + CSR_CONFIG_ROM, CSR_REGISTER_BASE + CSR_CONFIG_ROM_END); if (fcp) { - hpsb_register_addrspace(hl, &fcp_ops, + hpsb_register_addrspace(&csr_highlevel, &fcp_ops, CSR_REGISTER_BASE + CSR_FCP_COMMAND, CSR_REGISTER_BASE + CSR_FCP_END); } - hpsb_register_addrspace(hl, &map_ops, + hpsb_register_addrspace(&csr_highlevel, &map_ops, CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP, CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP_END); - hpsb_register_addrspace(hl, &map_ops, + hpsb_register_addrspace(&csr_highlevel, &map_ops, CSR_REGISTER_BASE + CSR_SPEED_MAP, CSR_REGISTER_BASE + CSR_SPEED_MAP_END); } void cleanup_csr(void) { - hpsb_unregister_highlevel(hl); + hpsb_unregister_highlevel(&csr_highlevel); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/dv1394.c linux.21rc1-ac2/drivers/ieee1394/dv1394.c --- linux.21rc1/drivers/ieee1394/dv1394.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/dv1394.c 2003-04-25 03:38:30.000000000 +0100 @@ -114,10 +114,10 @@ #include "ieee1394.h" #include "ieee1394_types.h" +#include "ieee1394_hotplug.h" #include "hosts.h" #include "ieee1394_core.h" #include "highlevel.h" -#include "ieee1394_hotplug.h" #include "dv1394.h" #include "dv1394-private.h" @@ -174,8 +174,6 @@ static LIST_HEAD(dv1394_cards); static spinlock_t dv1394_cards_lock = SPIN_LOCK_UNLOCKED; -static struct hpsb_highlevel *hl_handle; /* = NULL; */ - static LIST_HEAD(dv1394_devfs); struct dv1394_devfs_entry { struct list_head list; @@ -2572,10 +2570,9 @@ */ static struct ieee1394_device_id dv1394_id_table[] = { { - .match_flags =IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = AVC_SW_VERSION_ENTRY & 0xffffff }, { } }; @@ -2906,7 +2903,8 @@ wake_up_interruptible(&video->waitq); } -static struct hpsb_highlevel_ops hl_ops = { +static struct hpsb_highlevel dv1394_highlevel = { + .name = "dv1394", .add_host = dv1394_add_host, .remove_host = dv1394_remove_host, .host_reset = dv1394_host_reset, @@ -2924,7 +2922,7 @@ { hpsb_unregister_protocol(&dv1394_driver); - hpsb_unregister_highlevel (hl_handle); + hpsb_unregister_highlevel(&dv1394_highlevel); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394); #ifdef CONFIG_DEVFS_FS @@ -2962,18 +2960,7 @@ } #endif - hl_handle = hpsb_register_highlevel ("dv1394", &hl_ops); - if (hl_handle == NULL) { - printk(KERN_ERR "dv1394: hpsb_register_highlevel failed\n"); - ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394); -#ifdef CONFIG_DEVFS_FS - dv1394_devfs_del("dv"); -#endif -#ifdef CONFIG_PROC_FS - dv1394_procfs_del("dv"); -#endif - return -ENOMEM; - } + hpsb_register_highlevel (&dv1394_highlevel); hpsb_register_protocol(&dv1394_driver); @@ -2982,4 +2969,3 @@ module_init(dv1394_init_module); module_exit(dv1394_exit_module); - diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/dv1394-private.h linux.21rc1-ac2/drivers/ieee1394/dv1394-private.h --- linux.21rc1/drivers/ieee1394/dv1394-private.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/dv1394-private.h 2003-04-25 16:05:08.000000000 +0100 @@ -492,7 +492,7 @@ struct frame *frames[DV1394_MAX_FRAMES]; /* n_frames also serves as an indicator that this struct video_card is - intialized and ready to run DMA buffers */ + initialized and ready to run DMA buffers */ int n_frames; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/eth1394.c linux.21rc1-ac2/drivers/ieee1394/eth1394.c --- linux.21rc1/drivers/ieee1394/eth1394.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/eth1394.c 2003-04-25 03:38:30.000000000 +0100 @@ -55,9 +55,9 @@ #include #include #include +#include #include #include -#include #include #include "ieee1394_types.h" @@ -78,17 +78,14 @@ printk(KERN_ERR fmt, ## args) static char version[] __devinitdata = - "$Rev: 827 $ Ben Collins "; + "$Rev$ Ben Collins "; /* Our ieee1394 highlevel driver */ #define ETHER1394_DRIVER_NAME "ether1394" static kmem_cache_t *packet_task_cache; -static struct hpsb_highlevel *hl_handle = NULL; -/* Card handling */ -static LIST_HEAD (host_info_list); -static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; +static struct hpsb_highlevel eth1394_highlevel; /* Use common.lf to determine header len */ static int hdr_type_len[] = { @@ -105,40 +102,6 @@ static void ether1394_iso(struct hpsb_iso *iso); -/* Find our host_info struct for a given host pointer. Must be called - * under spinlock. */ -static inline struct host_info *find_host_info (struct hpsb_host *host) -{ - struct list_head *lh; - struct host_info *hi; - - lh = host_info_list.next; - while (lh != &host_info_list) { - hi = list_entry (lh, struct host_info, list); - - if (hi->host == host) - return hi; - - lh = lh->next; - } - return NULL; -} - -/* Find the network device for our host */ -static inline struct net_device *ether1394_find_dev (struct hpsb_host *host) -{ - struct host_info *hi; - - spin_lock_irq (&host_info_lock); - hi = find_host_info (host); - spin_unlock_irq (&host_info_lock); - - if (hi == NULL) - return NULL; - - return hi->dev; -} - /* This is called after an "ifup" */ static int ether1394_open (struct net_device *dev) { @@ -377,31 +340,24 @@ priv = (struct eth1394_priv *)dev->priv; priv->host = host; + spin_lock_init(&priv->lock); - hi = (struct host_info *)kmalloc (sizeof (struct host_info), - in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); if (hi == NULL) goto out; if (register_netdev (dev)) { ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n"); - kfree (dev); - kfree (hi); - return; + goto out; } ETH1394_PRINT (KERN_ERR, dev->name, "IEEE-1394 IPv4 over 1394 Ethernet (%s)\n", host->driver->name); - INIT_LIST_HEAD (&hi->list); hi->host = host; hi->dev = dev; - spin_lock_irq (&host_info_lock); - list_add_tail (&hi->list, &host_info_list); - spin_unlock_irq (&host_info_lock); - /* Ignore validity in hopes that it will be set in the future. It'll * check it on transmit. */ priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; @@ -416,6 +372,9 @@ out: if (dev != NULL) kfree (dev); + if (hi) + hpsb_destroy_hostinfo(ð1394_highlevel, host); + ETH1394_PRINT_G (KERN_ERR, "Out of memory\n"); return; @@ -424,20 +383,17 @@ /* Remove a card from our list */ static void ether1394_remove_host (struct hpsb_host *host) { - struct eth1394_priv *priv; - struct host_info *hi; + struct host_info *hi = hpsb_get_hostinfo(ð1394_highlevel, host); - spin_lock_irq (&host_info_lock); - hi = find_host_info (host); if (hi != NULL) { - priv = (struct eth1394_priv *)hi->dev->priv; + struct eth1394_priv *priv = (struct eth1394_priv *)hi->dev->priv; + priv->bc_state = ETHER1394_BC_CLOSED; unregister_netdev (hi->dev); + hpsb_iso_shutdown(priv->iso); + kfree (hi->dev); - list_del (&hi->list); - kfree (hi); } - spin_unlock_irq (&host_info_lock); return; } @@ -445,12 +401,15 @@ /* A reset has just arisen */ static void ether1394_host_reset (struct hpsb_host *host) { - struct net_device *dev = ether1394_find_dev(host); + struct host_info *hi = hpsb_get_hostinfo(ð1394_highlevel, host); + struct net_device *dev; /* This can happen for hosts that we don't use */ - if (dev == NULL) + if (hi == NULL) return; + dev = hi->dev; + /* Reset our private host data, but not our mtu */ netif_stop_queue (dev); ether1394_reset_priv (dev, 0); @@ -559,15 +518,18 @@ struct sk_buff *skb; char *buf = (char *)data; unsigned long flags; - struct net_device *dev = ether1394_find_dev (host); + struct host_info *hi = hpsb_get_hostinfo(ð1394_highlevel, host); + struct net_device *dev; struct eth1394_priv *priv; - if (dev == NULL) { + if (hi == NULL) { ETH1394_PRINT_G (KERN_ERR, "Could not find net device for host %p\n", host); return RCODE_ADDRESS_ERROR; } + dev = hi->dev; + priv = (struct eth1394_priv *)dev->priv; /* A packet has been received by the ieee1394 bus. Build an skbuff @@ -627,8 +589,9 @@ struct sk_buff *skb; quadlet_t *data; char *buf; - int flags; - struct net_device *dev = ether1394_find_dev(iso->host); + unsigned long flags; + struct host_info *hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); + struct net_device *dev; struct eth1394_priv *priv; unsigned int len; u32 specifier_id; @@ -636,12 +599,14 @@ int i; int nready; - if (dev == NULL) { + if (hi == NULL) { ETH1394_PRINT_G (KERN_ERR, "Could not find net device for host %s\n", iso->host->driver->name); return; } + dev = hi->dev; + nready = hpsb_iso_n_ready(iso); for(i = 0; i < nready; i++) { struct hpsb_iso_packet_info *info = &iso->infos[iso->first_packet + i]; @@ -727,7 +692,7 @@ struct sk_buff *skb = ptask->skb; struct net_device *dev = ptask->skb->dev; struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv; - unsigned long flags; + unsigned long flags; int status; if (ptask->tx_type == ETH1394_GASP) { @@ -914,7 +879,8 @@ }; /* Ieee1394 highlevel driver functions */ -static struct hpsb_highlevel_ops hl_ops = { +static struct hpsb_highlevel eth1394_highlevel = { + .name = ETHER1394_DRIVER_NAME, .add_host = ether1394_add_host, .remove_host = ether1394_remove_host, .host_reset = ether1394_host_reset, @@ -926,14 +892,9 @@ 0, 0, NULL, NULL); /* Register ourselves as a highlevel driver */ - hl_handle = hpsb_register_highlevel (ETHER1394_DRIVER_NAME, &hl_ops); + hpsb_register_highlevel(ð1394_highlevel); - if (hl_handle == NULL) { - ETH1394_PRINT_G (KERN_ERR, "No more memory for driver\n"); - return -ENOMEM; - } - - hpsb_register_addrspace (hl_handle, &addr_ops, ETHER1394_REGION_ADDR, + hpsb_register_addrspace(ð1394_highlevel, &addr_ops, ETHER1394_REGION_ADDR, ETHER1394_REGION_ADDR_END); return 0; @@ -941,20 +902,7 @@ static void __exit ether1394_exit_module (void) { - struct list_head *lh; - struct host_info *hi; - struct eth1394_priv *priv; - - lh = host_info_list.next; - while (lh != &host_info_list) { - hi = list_entry (lh, struct host_info, list); - priv = (struct eth1394_priv*)hi->dev->priv; - if (priv->bc_state != ETHER1394_BC_CLOSED) { - hpsb_iso_shutdown(priv->iso); - } - lh = lh->next; - } - hpsb_unregister_highlevel (hl_handle); + hpsb_unregister_highlevel(ð1394_highlevel); kmem_cache_destroy(packet_task_cache); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/eth1394.h linux.21rc1-ac2/drivers/ieee1394/eth1394.h --- linux.21rc1/drivers/ieee1394/eth1394.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/eth1394.h 2003-04-25 16:05:08.000000000 +0100 @@ -56,7 +56,6 @@ }; struct host_info { - struct list_head list; struct hpsb_host *host; struct net_device *dev; }; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/highlevel.c linux.21rc1-ac2/drivers/ieee1394/highlevel.c --- linux.21rc1/drivers/ieee1394/highlevel.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/highlevel.c 2003-04-25 03:38:30.000000000 +0100 @@ -19,6 +19,7 @@ #include #include +#include #include "ieee1394.h" #include "ieee1394_types.h" @@ -27,69 +28,237 @@ #include "highlevel.h" -LIST_HEAD(hl_drivers); -rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED; +struct hl_host_info { + struct list_head list; + struct hpsb_host *host; + size_t size; + unsigned long key; + void *data; +}; -LIST_HEAD(addr_space); -rwlock_t addr_space_lock = RW_LOCK_UNLOCKED; + +static LIST_HEAD(hl_drivers); +static rwlock_t hl_drivers_lock = RW_LOCK_UNLOCKED; + +static LIST_HEAD(addr_space); +static rwlock_t addr_space_lock = RW_LOCK_UNLOCKED; /* addr_space list will have zero and max already included as bounds */ static struct hpsb_address_ops dummy_ops = { NULL, NULL, NULL, NULL }; static struct hpsb_address_serve dummy_zero_addr, dummy_max_addr; -struct hpsb_highlevel *hpsb_register_highlevel(const char *name, - struct hpsb_highlevel_ops *ops) + +static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl, + struct hpsb_host *host) { - struct hpsb_highlevel *hl; + struct hl_host_info *hi = NULL; + struct list_head *lh; + + if (!hl || !host) + return NULL; + + read_lock(&hl->host_info_lock); + list_for_each (lh, &hl->host_info_list) { + hi = list_entry(lh, struct hl_host_info, list); + if (hi->host == host) + break; + hi = NULL; + } + read_unlock(&hl->host_info_lock); + + return hi; +} + + +/* Returns a per host/driver data structure that was previously stored by + * hpsb_create_hostinfo. */ +void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) +{ + struct hl_host_info *hi = hl_get_hostinfo(hl, host); + + if (hi) + return hi->data; + + return NULL; +} + + +/* If size is zero, then the return here is only valid for error checking */ +void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + size_t data_size) +{ + struct hl_host_info *hi; + void *data; + unsigned long flags; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + HPSB_ERR("%s called hpsb_create_hostinfo when hostinfo already exists", + hl->name); + return NULL; + } + + hi = kmalloc(sizeof(*hi) + data_size, GFP_KERNEL); + if (!hi) + return NULL; + + memset(hi, 0, sizeof(*hi) + data_size); + + if (data_size) { + data = hi->data = hi + 1; + hi->size = data_size; + } else + data = hi; + + hi->host = host; + + write_lock_irqsave(&hl->host_info_lock, flags); + list_add_tail(&hi->list, &hl->host_info_list); + write_unlock_irqrestore(&hl->host_info_lock, flags); + + return data; +} + + +int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + void *data) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + if (!hi->size && !hi->data) { + hi->data = data; + return 0; + } else + HPSB_ERR("%s called hpsb_set_hostinfo when hostinfo already has data", + hl->name); + } else + HPSB_ERR("%s called hpsb_set_hostinfo when no hostinfo exists", + hl->name); + + return -EINVAL; +} + + +void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) { + unsigned long flags; + write_lock_irqsave(&hl->host_info_lock, flags); + list_del(&hi->list); + write_unlock_irqrestore(&hl->host_info_lock, flags); + kfree(hi); + } + + return; +} - hl = (struct hpsb_highlevel *)kmalloc(sizeof(struct hpsb_highlevel), - GFP_KERNEL); - if (hl == NULL) { - return NULL; - } - INIT_LIST_HEAD(&hl->hl_list); +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) + hi->key = key; + + return; +} + + +unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host) +{ + struct hl_host_info *hi; + + hi = hl_get_hostinfo(hl, host); + if (hi) + return hi->key; + + return 0; +} + + +void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) +{ + struct list_head *lh; + struct hl_host_info *hi; + void *data = NULL; + + if (!hl) + return NULL; + + read_lock(&hl->host_info_lock); + list_for_each (lh, &hl->host_info_list) { + hi = list_entry(lh, struct hl_host_info, list); + if (hi->key == key) { + data = hi->data; + break; + } + } + read_unlock(&hl->host_info_lock); + + return data; +} + + +void hpsb_register_highlevel(struct hpsb_highlevel *hl) +{ + struct list_head *lh; + unsigned long flags; + INIT_LIST_HEAD(&hl->addr_list); - hl->name = name; - hl->op = ops; + INIT_LIST_HEAD(&hl->host_info_list); + + rwlock_init(&hl->host_info_lock); - write_lock_irq(&hl_drivers_lock); + write_lock_irqsave(&hl_drivers_lock, flags); list_add_tail(&hl->hl_list, &hl_drivers); - write_unlock_irq(&hl_drivers_lock); + write_unlock_irqrestore(&hl_drivers_lock, flags); - hl_all_hosts(hl->op->add_host); + if (hl->add_host) { + down(&hpsb_hosts_lock); + list_for_each (lh, &hpsb_hosts) { + struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list); + hl->add_host(host); + } + up(&hpsb_hosts_lock); + } - return hl; + return; } void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) { - struct list_head *entry; + struct list_head *lh, *next; struct hpsb_address_serve *as; + unsigned long flags; - if (hl == NULL) { - return; - } - - write_lock_irq(&addr_space_lock); - entry = hl->addr_list.next; - - while (entry != &hl->addr_list) { - as = list_entry(entry, struct hpsb_address_serve, addr_list); + write_lock_irqsave(&addr_space_lock, flags); + list_for_each_safe (lh, next, &hl->addr_list) { + as = list_entry(lh, struct hpsb_address_serve, addr_list); list_del(&as->as_list); - entry = entry->next; kfree(as); } - write_unlock_irq(&addr_space_lock); + write_unlock_irqrestore(&addr_space_lock, flags); - write_lock_irq(&hl_drivers_lock); + write_lock_irqsave(&hl_drivers_lock, flags); list_del(&hl->hl_list); - write_unlock_irq(&hl_drivers_lock); + write_unlock_irqrestore(&hl_drivers_lock, flags); - if (hl->op->remove_host) - hl_all_hosts(hl->op->remove_host); - - kfree(hl); + if (hl->remove_host) { + down(&hpsb_hosts_lock); + list_for_each(lh, &hpsb_hosts) { + struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list); + + hl->remove_host(host); + hpsb_destroy_hostinfo(hl, host); + } + up(&hpsb_hosts_lock); + } } int hpsb_register_addrspace(struct hpsb_highlevel *hl, @@ -168,17 +337,19 @@ return retval; } -void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, +int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel) { if (channel > 63) { HPSB_ERR("%s called with invalid channel", __FUNCTION__); - return; + return -EINVAL; } if (host->iso_listen_count[channel]++ == 0) { - host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel); + return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel); } + + return 0; } void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, @@ -203,8 +374,8 @@ read_lock(&hl_drivers_lock); list_for_each(entry, &hl_drivers) { hl = list_entry(entry, struct hpsb_highlevel, hl_list); - - hl->op->add_host(host); + if (hl->add_host) + hl->add_host(host); } read_unlock(&hl_drivers_lock); } @@ -214,14 +385,16 @@ struct list_head *entry; struct hpsb_highlevel *hl; - write_lock_irq(&hl_drivers_lock); + read_lock(&hl_drivers_lock); list_for_each(entry, &hl_drivers) { hl = list_entry(entry, struct hpsb_highlevel, hl_list); - if (hl->op->remove_host) - hl->op->remove_host(host); + if (hl->remove_host) { + hl->remove_host(host); + hpsb_destroy_hostinfo(hl, host); + } } - write_unlock_irq(&hl_drivers_lock); + read_unlock(&hl_drivers_lock); } void highlevel_host_reset(struct hpsb_host *host) @@ -233,8 +406,8 @@ list_for_each(entry, &hl_drivers) { hl = list_entry(entry, struct hpsb_highlevel, hl_list); - if (hl->op->host_reset) - hl->op->host_reset(host); + if (hl->host_reset) + hl->host_reset(host); } read_unlock(&hl_drivers_lock); } @@ -251,8 +424,8 @@ while (entry != &hl_drivers) { hl = list_entry(entry, struct hpsb_highlevel, hl_list); - if (hl->op->iso_receive) { - hl->op->iso_receive(host, channel, data, length); + if (hl->iso_receive) { + hl->iso_receive(host, channel, data, length); } entry = entry->next; } @@ -271,8 +444,8 @@ while (entry != &hl_drivers) { hl = list_entry(entry, struct hpsb_highlevel, hl_list); - if (hl->op->fcp_request) { - hl->op->fcp_request(host, nodeid, direction, cts, data, + if (hl->fcp_request) { + hl->fcp_request(host, nodeid, direction, cts, data, length); } entry = entry->next; @@ -297,7 +470,7 @@ if (as->end > addr) { partlength = min(as->end - addr, (u64) length); - if (as->op->read != NULL) { + if (as->op->read) { rcode = as->op->read(host, nodeid, data, addr, partlength, flags); } else { @@ -343,7 +516,7 @@ if (as->end > addr) { partlength = min(as->end - addr, (u64) length); - if (as->op->write != NULL) { + if (as->op->write) { rcode = as->op->write(host, nodeid, destid, data, addr, partlength, flags); } else { @@ -387,7 +560,7 @@ while (as->start <= addr) { if (as->end > addr) { - if (as->op->lock != NULL) { + if (as->op->lock) { rcode = as->op->lock(host, nodeid, store, addr, data, arg, ext_tcode, flags); } else { @@ -420,7 +593,7 @@ while (as->start <= addr) { if (as->end > addr) { - if (as->op->lock64 != NULL) { + if (as->op->lock64) { rcode = as->op->lock64(host, nodeid, store, addr, data, arg, ext_tcode, flags); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/highlevel.h linux.21rc1-ac2/drivers/ieee1394/highlevel.h --- linux.21rc1/drivers/ieee1394/highlevel.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/highlevel.h 2003-04-25 03:38:30.000000000 +0100 @@ -3,17 +3,6 @@ #define IEEE1394_HIGHLEVEL_H -struct hpsb_highlevel { - struct list_head hl_list; - - /* List of hpsb_address_serve. */ - struct list_head addr_list; - - const char *name; - struct hpsb_highlevel_ops *op; -}; - - struct hpsb_address_serve { struct list_head as_list; /* global list */ @@ -31,7 +20,9 @@ * following structures are of interest to actual highlevel drivers. */ -struct hpsb_highlevel_ops { +struct hpsb_highlevel { + const char *name; + /* Any of the following pointers can legally be NULL, except for * iso_receive which can only be NULL when you don't request * channels. */ @@ -62,6 +53,13 @@ */ void (*fcp_request) (struct hpsb_host *host, int nodeid, int direction, int cts, u8 *data, unsigned int length); + + + struct list_head hl_list; + struct list_head addr_list; + + struct list_head host_info_list; + rwlock_t host_info_lock; }; struct hpsb_address_ops { @@ -127,8 +125,7 @@ * Register highlevel driver. The name pointer has to stay valid at all times * because the string is not copied. */ -struct hpsb_highlevel *hpsb_register_highlevel(const char *name, - struct hpsb_highlevel_ops *ops); +void hpsb_register_highlevel(struct hpsb_highlevel *hl); void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); /* @@ -150,9 +147,27 @@ * Enable or disable receving a certain isochronous channel through the * iso_receive op. */ -void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, +int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel); void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel); + +/* Retrieve a hostinfo pointer bound to this driver/host */ +void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); +/* Allocate a hostinfo pointer of data_size bound to this driver/host */ +void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, + size_t data_size); +/* Free and remove the hostinfo pointer bound to this driver/host */ +void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); +/* Set an alternate lookup key for the hostinfo bound to this driver/host */ +void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key); +/* Retrieve the alternate lookup key for the hostinfo bound to this driver/host */ +unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host); +/* Retrive a hostinfo pointer bound to this driver using its alternate key */ +void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); +/* Set the hostinfo pointer to something useful. Usually follows a call to + * hpsb_create_hostinfo, where the size is 0. */ +int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data); + #endif /* IEEE1394_HIGHLEVEL_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/hosts.c linux.21rc1-ac2/drivers/ieee1394/hosts.c --- linux.21rc1/drivers/ieee1394/hosts.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/hosts.c 2003-04-25 03:58:53.000000000 +0100 @@ -21,12 +21,8 @@ #include "ieee1394_core.h" #include "highlevel.h" -static struct list_head hosts = LIST_HEAD_INIT(hosts); -static struct list_head host_drivers = LIST_HEAD_INIT(host_drivers); - -spinlock_t hosts_lock = SPIN_LOCK_UNLOCKED; -spinlock_t host_drivers_lock = SPIN_LOCK_UNLOCKED; - +LIST_HEAD(hpsb_hosts); +DECLARE_MUTEX(hpsb_hosts_lock); static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p) { @@ -38,9 +34,15 @@ return -1; } +static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg) +{ + return -1; +} + static struct hpsb_host_driver dummy_driver = { .transmit_packet = dummy_transmit_packet, - .devctl = dummy_devctl + .devctl = dummy_devctl, + .isoctl = dummy_isoctl }; /** @@ -57,11 +59,10 @@ int hpsb_ref_host(struct hpsb_host *host) { struct list_head *lh; - unsigned long flags; int retval = 0; - spin_lock_irqsave(&hosts_lock, flags); - list_for_each(lh, &hosts) { + down(&hpsb_hosts_lock); + list_for_each(lh, &hpsb_hosts) { if (host == list_entry(lh, struct hpsb_host, host_list)) { host->driver->devctl(host, MODIFY_USAGE, 1); host->refcount++; @@ -69,7 +70,7 @@ break; } } - spin_unlock_irqrestore(&hosts_lock, flags); + up(&hpsb_hosts_lock); return retval; } @@ -85,16 +86,14 @@ void hpsb_unref_host(struct hpsb_host *host) { - unsigned long flags; - host->driver->devctl(host, MODIFY_USAGE, 0); - spin_lock_irqsave(&hosts_lock, flags); + down(&hpsb_hosts_lock); host->refcount--; if (!host->refcount && host->is_shutdown) kfree(host); - spin_unlock_irqrestore(&hosts_lock, flags); + up(&hpsb_hosts_lock); } /** @@ -121,6 +120,7 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra) { struct hpsb_host *h; + int i; h = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL); if (!h) return NULL; @@ -133,8 +133,8 @@ INIT_LIST_HEAD(&h->pending_packets); spin_lock_init(&h->pending_pkt_lock); - sema_init(&h->tlabel_count, 64); - spin_lock_init(&h->tlabel_lock); + for (i = 0; i < ARRAY_SIZE(h->tpool); i++) + HPSB_TPOOL_INIT(&h->tpool[i]); atomic_set(&h->generation, 0); @@ -146,13 +146,38 @@ return h; } -void hpsb_add_host(struct hpsb_host *host) +static int alloc_hostnum(void) { - unsigned long flags; + int hostnum = 0; - spin_lock_irqsave(&hosts_lock, flags); - list_add_tail(&host->host_list, &hosts); - spin_unlock_irqrestore(&hosts_lock, flags); + while (1) { + struct list_head *lh; + int found = 0; + + list_for_each(lh, &hpsb_hosts) { + struct hpsb_host *host = list_entry(lh, struct hpsb_host, host_list); + + if (host->id == hostnum) { + found = 1; + break; + } + } + + if (!found) + return hostnum; + + hostnum++; + } + + return 0; +} + +void hpsb_add_host(struct hpsb_host *host) +{ + down(&hpsb_hosts_lock); + host->id = alloc_hostnum(); + list_add_tail(&host->host_list, &hpsb_hosts); + up(&hpsb_hosts_lock); highlevel_add_host(host); host->driver->devctl(host, RESET_BUS, LONG_RESET); @@ -160,29 +185,11 @@ void hpsb_remove_host(struct hpsb_host *host) { - unsigned long flags; - + down(&hpsb_hosts_lock); host->is_shutdown = 1; host->driver = &dummy_driver; - highlevel_remove_host(host); - - spin_lock_irqsave(&hosts_lock, flags); - list_del(&host->host_list); - spin_unlock_irqrestore(&hosts_lock, flags); -} + list_del(&host->host_list); + up(&hpsb_hosts_lock); -/* - * This function calls the given function for every host currently registered. - */ -void hl_all_hosts(void (*function)(struct hpsb_host*)) -{ - struct list_head *lh; - struct hpsb_host *host; - - spin_lock_irq(&hosts_lock); - list_for_each (lh, &hosts) { - host = list_entry(lh, struct hpsb_host, host_list); - function(host); - } - spin_unlock_irq(&hosts_lock); + highlevel_remove_host(host); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/hosts.h linux.21rc1-ac2/drivers/ieee1394/hosts.h --- linux.21rc1/drivers/ieee1394/hosts.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/hosts.h 2003-04-25 16:05:08.000000000 +0100 @@ -34,13 +34,6 @@ spinlock_t pending_pkt_lock; struct tq_struct timeout_tq; - /* A bitmask where a set bit means that this tlabel is in use. - * FIXME - should be handled per node instead of per bus. */ - u32 tlabel_pool[2]; - struct semaphore tlabel_count; - spinlock_t tlabel_lock; - u32 tlabel_current; - unsigned char iso_listen_count[64]; int node_count; /* number of identified nodes on this bus */ @@ -66,9 +59,14 @@ u8 *speed_map; struct csr_control csr; + /* Per node tlabel pool allocation */ + struct hpsb_tlabel_pool tpool[64]; + struct hpsb_host_driver *driver; struct pci_dev *pdev; + + int id; }; @@ -178,9 +176,9 @@ */ int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg); - /* rawiso transmission/reception functions. Return 0 on success, -1 + /* ISO transmission/reception functions. Return 0 on success, -1 * (or -EXXX errno code) on failure. If the low-level driver does not - * support the rawiso API, set isoctl to NULL. + * support the new ISO API, set isoctl to NULL. */ int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg); @@ -195,12 +193,9 @@ quadlet_t data, quadlet_t compare); }; -/* core internal use */ -void register_builtin_lowlevels(void); -/* high level internal use */ -struct hpsb_highlevel; -void hl_all_hosts(void (*function)(struct hpsb_host*)); +extern struct list_head hpsb_hosts; +extern struct semaphore hpsb_hosts_lock; /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_core.c linux.21rc1-ac2/drivers/ieee1394/ieee1394_core.c --- linux.21rc1/drivers/ieee1394/ieee1394_core.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_core.c 2003-04-24 20:17:50.000000000 +0100 @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include @@ -52,17 +52,14 @@ MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality."); static int disable_nodemgr = 0; -MODULE_PARM(disable_hotplug, "i"); -MODULE_PARM_DESC(disable_hotplug, "Disable hotplug for detected nodes."); -static int disable_hotplug = 0; - /* We are GPL, so treat us special */ MODULE_LICENSE("GPL"); static kmem_cache_t *hpsb_packet_cache; /* Some globals used */ -const char *hpsb_speedto_str[] = { "S100", "S200", "S400" }; +const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" }; +const u8 hpsb_speedto_maxrec[] = { 0x7, 0x8, 0x9, 0x10, 0x11, 0x12 }; static void dump_packet(const char *text, quadlet_t *data, int size) { @@ -78,28 +75,31 @@ printk("\n"); } -static void process_complete_tasks(struct hpsb_packet *packet) +static void run_packet_complete(struct hpsb_packet *packet) { - struct list_head *lh, *next; - - list_for_each_safe(lh, next, &packet->complete_tq) { - struct tq_struct *tq = - list_entry(lh, struct tq_struct, list); - list_del(&tq->list); - schedule_task(tq); + if (packet->complete_routine != NULL) { + packet->complete_routine(packet->complete_data); + packet->complete_routine = NULL; + packet->complete_data = NULL; } - return; } /** - * hpsb_add_packet_complete_task - add a new task for when a packet completes + * hpsb_set_packet_complete_task - set the task that runs when a packet + * completes. You cannot call this more than once on a single packet + * before it is sent. + * * @packet: the packet whose completion we want the task added to - * @tq: the tq_struct describing the task to add + * @routine: function to call + * @data: data (if any) to pass to the above function */ -void hpsb_add_packet_complete_task(struct hpsb_packet *packet, struct tq_struct *tq) +void hpsb_set_packet_complete_task(struct hpsb_packet *packet, + void (*routine)(void *), void *data) { - list_add_tail(&tq->list, &packet->complete_tq); + BUG_ON(packet->complete_routine != NULL); + packet->complete_routine = routine; + packet->complete_data = data; return; } @@ -146,9 +146,10 @@ packet->data_size = data_size; } - INIT_LIST_HEAD(&packet->complete_tq); INIT_LIST_HEAD(&packet->list); sema_init(&packet->state_change, 0); + packet->complete_routine = NULL; + packet->complete_data = NULL; packet->state = hpsb_unused; packet->generation = -1; packet->data_be = 1; @@ -284,7 +285,7 @@ for (i = 0; i < (nodecount * 64); i += 64) { for (j = 0; j < nodecount; j++) { - map[i+j] = SPEED_400; + map[i+j] = SPEED_MAX; } } @@ -356,7 +357,7 @@ host->topology_map[host->selfid_count++] = sid; } else { HPSB_NOTICE("Spurious SelfID packet (0x%08x) received from bus %d", - sid, (host->node_id & BUS_MASK) >> 6); + sid, NODEID_TO_BUS(host->node_id)); } } @@ -373,6 +374,7 @@ /* selfid stage did not complete without error */ HPSB_NOTICE("Error in SelfID stage, resetting"); host->in_bus_reset = 0; + /* this should work from ohci1394 now... */ hpsb_reset_bus(host, LONG_RESET); return; } else { @@ -390,15 +392,11 @@ /* irm_id is kept up to date by check_selfids() */ if (host->irm_id == host->node_id) { host->is_irm = 1; - host->is_busmgr = 1; - host->busmgr_id = host->node_id; - host->csr.bus_manager_id = host->node_id; } else { host->is_busmgr = 0; host->is_irm = 0; } - host->reset_retries = 0; if (isroot) { host->driver->devctl(host, ACT_CYCLE_MASTER, 1); host->is_cycmst = 1; @@ -426,7 +424,7 @@ packet->state = hpsb_complete; up(&packet->state_change); up(&packet->state_change); - process_complete_tasks(packet); + run_packet_complete(packet); return; } @@ -442,6 +440,63 @@ } /** + * hpsb_send_phy_config - transmit a PHY configuration packet on the bus + * @host: host that PHY config packet gets sent through + * @rootid: root whose force_root bit should get set (-1 = don't set force_root) + * @gapcnt: gap count value to set (-1 = don't set gap count) + * + * This function sends a PHY config packet on the bus through the specified host. + * + * Return value: 0 for success or error number otherwise. + */ +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) +{ + struct hpsb_packet *packet; + int retval = 0; + + if(rootid >= ALL_NODES || rootid < -1 || gapcnt > 0x3f || gapcnt < -1 || + (rootid == -1 && gapcnt == -1)) { + HPSB_DEBUG("Invalid Parameter: rootid = %d gapcnt = %d", + rootid, gapcnt); + return -EINVAL; + } + + packet = alloc_hpsb_packet(0); + if (!packet) + return -ENOMEM; + + packet->host = host; + packet->header_size = 16; + packet->data_size = 0; + packet->expect_response = 0; + packet->no_waiter = 0; + packet->type = hpsb_raw; + packet->header[0] = 0; + if(rootid != -1) + packet->header[0] |= rootid << 24 | 1 << 23; + if(gapcnt != -1) + packet->header[0] |= gapcnt << 16 | 1 << 22; + + packet->header[1] = ~packet->header[0]; + + packet->generation = get_hpsb_generation(host); + + HPSB_DEBUG("Sending PHY configuration packet (I hope)..."); + if (!hpsb_send_packet(packet)) { + retval = -EINVAL; + goto fail; + } + + down(&packet->state_change); + down(&packet->state_change); + +fail: + free_hpsb_packet(packet); + + return retval; +} + +/** * hpsb_send_packet - transmit a packet on the bus * @packet: packet to send * @@ -506,8 +561,8 @@ if (packet->type == hpsb_async && packet->node_id != ALL_NODES) { packet->speed_code = - host->speed_map[(host->node_id & NODE_MASK) * 64 - + (packet->node_id & NODE_MASK)]; + host->speed_map[NODEID_TO_NODE(host->node_id) * 64 + + NODEID_TO_NODE(packet->node_id)]; } #ifdef CONFIG_IEEE1394_VERBOSEDEBUG @@ -614,7 +669,7 @@ packet->state = hpsb_complete; up(&packet->state_change); - process_complete_tasks(packet); + run_packet_complete(packet); } @@ -647,6 +702,54 @@ return p; } +#define PREP_ASYNC_HEAD_RCODE(tc) \ + packet->tcode = tc; \ + packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ + | (1 << 8) | (tc << 4); \ + packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \ + packet->header[2] = 0 + +static void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, + quadlet_t data) +{ + PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE); + packet->header[3] = data; + packet->header_size = 16; + packet->data_size = 0; +} + +static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, + int length) +{ + if (rcode != RCODE_COMPLETE) + length = 0; + + PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE); + packet->header[3] = length << 16; + packet->header_size = 16; + packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); +} + +static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) +{ + PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); + packet->header[2] = 0; + packet->header_size = 12; + packet->data_size = 0; +} + +static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, + int length) +{ + if (rcode != RCODE_COMPLETE) + length = 0; + + PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE); + packet->header[3] = (length << 16) | extcode; + packet->header_size = 16; + packet->data_size = length; +} + #define PREP_REPLY_PACKET(length) \ packet = create_reply_packet(host, data, length); \ if (packet == NULL) break @@ -671,7 +774,7 @@ addr, 4, flags); if (!write_acked - && (((data[0] >> 16) & NODE_MASK) != NODE_MASK) + && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) && (rcode >= 0)) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); @@ -686,7 +789,7 @@ addr, data[3]>>16, flags); if (!write_acked - && (((data[0] >> 16) & NODE_MASK) != NODE_MASK) + && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) && (rcode >= 0)) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); @@ -848,7 +951,7 @@ packet->state = hpsb_complete; packet->ack_code = ACKX_ABORTED; up(&packet->state_change); - process_complete_tasks(packet); + run_packet_complete(packet); } } @@ -890,7 +993,7 @@ packet->state = hpsb_complete; packet->ack_code = ACKX_TIMEOUT; up(&packet->state_change); - process_complete_tasks(packet); + run_packet_complete(packet); } } @@ -923,7 +1026,7 @@ struct file_operations *file_ops) { int retval; - + if( (blocknum < 0) || (blocknum > 15) ) return -EINVAL; @@ -939,7 +1042,7 @@ /* block already taken */ retval = -EBUSY; } - + write_unlock(&ieee1394_chardevs_lock); return retval; @@ -950,14 +1053,14 @@ { if( (blocknum < 0) || (blocknum > 15) ) return; - + write_lock(&ieee1394_chardevs_lock); - + if(ieee1394_chardevs[blocknum].file_ops) { ieee1394_chardevs[blocknum].file_ops = NULL; ieee1394_chardevs[blocknum].module = NULL; } - + write_unlock(&ieee1394_chardevs_lock); } @@ -978,7 +1081,7 @@ { int ret = 0; - if( (blocknum < 0) || (blocknum > 15) ) + if ((blocknum < 0) || (blocknum > 15)) return ret; read_lock(&ieee1394_chardevs_lock); @@ -986,7 +1089,7 @@ *module = ieee1394_chardevs[blocknum].module; *file_ops = ieee1394_chardevs[blocknum].file_ops; - if(*file_ops == NULL) + if (*file_ops == NULL) goto out; /* don't need try_inc_mod_count if the driver is non-modular */ @@ -995,7 +1098,7 @@ /* success! */ ret = 1; - + out: read_unlock(&ieee1394_chardevs_lock); return ret; @@ -1025,11 +1128,11 @@ reference count of whatever module file->f_op->owner points to, immediately after this function returns. */ - + /* shift away lower four bits of the minor to get the index of the ieee1394_driver we want */ - + blocknum = (MINOR(inode->i_rdev) >> 4) & 0xF; /* look up the driver */ @@ -1116,7 +1219,7 @@ init_hpsb_highlevel(); init_csr(); if (!disable_nodemgr) - init_ieee1394_nodemgr(disable_hotplug); + init_ieee1394_nodemgr(); else HPSB_INFO("nodemgr functionality disabled"); @@ -1153,9 +1256,11 @@ /** ieee1394_core.c **/ EXPORT_SYMBOL(hpsb_speedto_str); -EXPORT_SYMBOL(hpsb_add_packet_complete_task); +EXPORT_SYMBOL(hpsb_speedto_maxrec); +EXPORT_SYMBOL(hpsb_set_packet_complete_task); EXPORT_SYMBOL(alloc_hpsb_packet); EXPORT_SYMBOL(free_hpsb_packet); +EXPORT_SYMBOL(hpsb_send_phy_config); EXPORT_SYMBOL(hpsb_send_packet); EXPORT_SYMBOL(hpsb_reset_bus); EXPORT_SYMBOL(hpsb_bus_reset); @@ -1169,33 +1274,20 @@ EXPORT_SYMBOL(ieee1394_procfs_entry); /** ieee1394_transactions.c **/ -EXPORT_SYMBOL(get_tlabel); -EXPORT_SYMBOL(free_tlabel); -EXPORT_SYMBOL(fill_async_readquad); -EXPORT_SYMBOL(fill_async_readquad_resp); -EXPORT_SYMBOL(fill_async_readblock); -EXPORT_SYMBOL(fill_async_readblock_resp); -EXPORT_SYMBOL(fill_async_writequad); -EXPORT_SYMBOL(fill_async_writeblock); -EXPORT_SYMBOL(fill_async_write_resp); -EXPORT_SYMBOL(fill_async_lock); -EXPORT_SYMBOL(fill_async_lock_resp); -EXPORT_SYMBOL(fill_iso_packet); -EXPORT_SYMBOL(fill_phy_packet); -EXPORT_SYMBOL(hpsb_make_readqpacket); -EXPORT_SYMBOL(hpsb_make_readbpacket); -EXPORT_SYMBOL(hpsb_make_writeqpacket); -EXPORT_SYMBOL(hpsb_make_writebpacket); +EXPORT_SYMBOL(hpsb_get_tlabel); +EXPORT_SYMBOL(hpsb_free_tlabel); +EXPORT_SYMBOL(hpsb_make_readpacket); +EXPORT_SYMBOL(hpsb_make_writepacket); EXPORT_SYMBOL(hpsb_make_lockpacket); EXPORT_SYMBOL(hpsb_make_lock64packet); EXPORT_SYMBOL(hpsb_make_phypacket); -EXPORT_SYMBOL(hpsb_packet_success); -EXPORT_SYMBOL(hpsb_make_packet); +EXPORT_SYMBOL(hpsb_make_isopacket); EXPORT_SYMBOL(hpsb_read); EXPORT_SYMBOL(hpsb_write); EXPORT_SYMBOL(hpsb_lock); EXPORT_SYMBOL(hpsb_lock64); EXPORT_SYMBOL(hpsb_send_gasp); +EXPORT_SYMBOL(hpsb_packet_success); /** highlevel.c **/ EXPORT_SYMBOL(hpsb_register_highlevel); @@ -1204,6 +1296,13 @@ EXPORT_SYMBOL(hpsb_unregister_addrspace); EXPORT_SYMBOL(hpsb_listen_channel); EXPORT_SYMBOL(hpsb_unlisten_channel); +EXPORT_SYMBOL(hpsb_get_hostinfo); +EXPORT_SYMBOL(hpsb_create_hostinfo); +EXPORT_SYMBOL(hpsb_destroy_hostinfo); +EXPORT_SYMBOL(hpsb_set_hostinfo_key); +EXPORT_SYMBOL(hpsb_get_hostinfo_key); +EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); +EXPORT_SYMBOL(hpsb_set_hostinfo); EXPORT_SYMBOL(highlevel_read); EXPORT_SYMBOL(highlevel_write); EXPORT_SYMBOL(highlevel_lock); @@ -1219,12 +1318,14 @@ EXPORT_SYMBOL(hpsb_node_read); EXPORT_SYMBOL(hpsb_node_write); EXPORT_SYMBOL(hpsb_node_lock); -EXPORT_SYMBOL(hpsb_update_config_rom); -EXPORT_SYMBOL(hpsb_get_config_rom); EXPORT_SYMBOL(hpsb_register_protocol); EXPORT_SYMBOL(hpsb_unregister_protocol); EXPORT_SYMBOL(hpsb_release_unit_directory); +/** csr.c **/ +EXPORT_SYMBOL(hpsb_update_config_rom); +EXPORT_SYMBOL(hpsb_get_config_rom); + /** dma.c **/ EXPORT_SYMBOL(dma_prog_region_init); EXPORT_SYMBOL(dma_prog_region_alloc); @@ -1253,4 +1354,3 @@ EXPORT_SYMBOL(hpsb_iso_packet_sent); EXPORT_SYMBOL(hpsb_iso_packet_received); EXPORT_SYMBOL(hpsb_iso_wake); - diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_core.h linux.21rc1-ac2/drivers/ieee1394/ieee1394_core.h --- linux.21rc1/drivers/ieee1394/ieee1394_core.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_core.h 2003-04-25 16:05:08.000000000 +0100 @@ -68,7 +68,10 @@ /* Very core internal, don't care. */ struct semaphore state_change; - struct list_head complete_tq; + /* Function (and possible data to pass to it) to call when this + * packet is completed. */ + void (*complete_routine)(void *); + void *complete_data; /* Store jiffies for implementing bus timeouts. */ unsigned long sendtime; @@ -76,8 +79,9 @@ quadlet_t embedded_header[5]; }; -/* add a new task for when a packet completes */ -void hpsb_add_packet_complete_task(struct hpsb_packet *packet, struct tq_struct *tq); +/* Set a task for when a packet completes */ +void hpsb_set_packet_complete_task(struct hpsb_packet *packet, + void (*routine)(void *), void *data); static inline struct hpsb_packet *driver_packet(struct list_head *l) { @@ -105,6 +109,11 @@ } /* + * Send a PHY configuration packet. + */ +int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); + +/* * Queue packet for transmitting, return 0 for failure. */ int hpsb_send_packet(struct hpsb_packet *packet); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394.h linux.21rc1-ac2/drivers/ieee1394/ieee1394.h --- linux.21rc1/drivers/ieee1394/ieee1394.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394.h 2003-04-24 20:17:50.000000000 +0100 @@ -46,12 +46,19 @@ #define ACKX_TIMEOUT (-4) -#define SPEED_100 0x0 -#define SPEED_200 0x1 -#define SPEED_400 0x2 +#define SPEED_100 0x00 +#define SPEED_200 0x01 +#define SPEED_400 0x02 +#define SPEED_800 0x03 +#define SPEED_1600 0x04 +#define SPEED_3200 0x05 +/* The current highest tested speed supported by the subsystem */ +#define SPEED_MAX SPEED_800 /* Maps speed values above to a string representation */ extern const char *hpsb_speedto_str[]; +extern const u8 hpsb_speedto_maxrec[]; + #define SELFID_PWRCL_NO_POWER 0x0 #define SELFID_PWRCL_PROVIDE_15W 0x1 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_hotplug.h linux.21rc1-ac2/drivers/ieee1394/ieee1394_hotplug.h --- linux.21rc1/drivers/ieee1394/ieee1394_hotplug.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_hotplug.h 2003-04-25 16:05:08.000000000 +0100 @@ -77,8 +77,6 @@ int hpsb_register_protocol(struct hpsb_protocol_driver *driver); void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver); -int hpsb_claim_unit_directory(struct unit_directory *ud, - struct hpsb_protocol_driver *driver); void hpsb_release_unit_directory(struct unit_directory *ud); #endif /* _IEEE1394_HOTPLUG_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394-ioctl.h linux.21rc1-ac2/drivers/ieee1394/ieee1394-ioctl.h --- linux.21rc1/drivers/ieee1394/ieee1394-ioctl.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394-ioctl.h 2003-04-25 16:05:08.000000000 +0100 @@ -4,8 +4,8 @@ #ifndef __IEEE1394_IOCTL_H #define __IEEE1394_IOCTL_H -#include -#include +#include +#include /* AMDTP Gets 6 */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_transactions.c linux.21rc1-ac2/drivers/ieee1394/ieee1394_transactions.c --- linux.21rc1/drivers/ieee1394/ieee1394_transactions.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_transactions.c 2003-04-21 17:30:49.000000000 +0100 @@ -10,8 +10,8 @@ */ #include +#include #include -#include #include #include "ieee1394.h" @@ -19,6 +19,7 @@ #include "hosts.h" #include "ieee1394_core.h" #include "highlevel.h" +#include "nodemgr.h" #define PREP_ASYNC_HEAD_ADDRESS(tc) \ @@ -28,15 +29,8 @@ packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ packet->header[2] = addr & 0xffffffff -#define PREP_ASYNC_HEAD_RCODE(tc) \ - packet->tcode = tc; \ - packet->header[0] = (packet->node_id << 16) | (packet->tlabel << 10) \ - | (1 << 8) | (tc << 4); \ - packet->header[1] = (packet->host->node_id << 16) | (rcode << 12); \ - packet->header[2] = 0 - -void fill_async_readquad(struct hpsb_packet *packet, u64 addr) +static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) { PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); packet->header_size = 12; @@ -44,16 +38,7 @@ packet->expect_response = 1; } -void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, - quadlet_t data) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_READQ_RESPONSE); - packet->header[3] = data; - packet->header_size = 16; - packet->data_size = 0; -} - -void fill_async_readblock(struct hpsb_packet *packet, u64 addr, int length) +static void fill_async_readblock(struct hpsb_packet *packet, u64 addr, int length) { PREP_ASYNC_HEAD_ADDRESS(TCODE_READB); packet->header[3] = length << 16; @@ -62,20 +47,7 @@ packet->expect_response = 1; } -void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, - int length) -{ - if (rcode != RCODE_COMPLETE) { - length = 0; - } - - PREP_ASYNC_HEAD_RCODE(TCODE_READB_RESPONSE); - packet->header[3] = length << 16; - packet->header_size = 16; - packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); -} - -void fill_async_writequad(struct hpsb_packet *packet, u64 addr, quadlet_t data) +static void fill_async_writequad(struct hpsb_packet *packet, u64 addr, quadlet_t data) { PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEQ); packet->header[3] = data; @@ -84,7 +56,7 @@ packet->expect_response = 1; } -void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, int length) +static void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, int length) { PREP_ASYNC_HEAD_ADDRESS(TCODE_WRITEB); packet->header[3] = length << 16; @@ -93,15 +65,7 @@ packet->data_size = length + (length % 4 ? 4 - (length % 4) : 0); } -void fill_async_write_resp(struct hpsb_packet *packet, int rcode) -{ - PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); - packet->header[2] = 0; - packet->header_size = 12; - packet->data_size = 0; -} - -void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, +static void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, int length) { PREP_ASYNC_HEAD_ADDRESS(TCODE_LOCK_REQUEST); @@ -111,20 +75,7 @@ packet->expect_response = 1; } -void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, - int length) -{ - if (rcode != RCODE_COMPLETE) { - length = 0; - } - - PREP_ASYNC_HEAD_RCODE(TCODE_LOCK_RESPONSE); - packet->header[3] = (length << 16) | extcode; - packet->header_size = 16; - packet->data_size = length; -} - -void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, +static void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, int tag, int sync) { packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) @@ -136,7 +87,7 @@ packet->tcode = TCODE_ISO_DATA; } -void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data) +static void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data) { packet->header[0] = data; packet->header[1] = ~data; @@ -147,7 +98,7 @@ packet->speed_code = SPEED_100; /* Force speed to be 100Mbps */ } -void fill_async_stream_packet(struct hpsb_packet *packet, int length, +static void fill_async_stream_packet(struct hpsb_packet *packet, int length, int channel, int tag, int sync) { packet->header[0] = (length << 16) | (tag << 14) | (channel << 8) @@ -160,9 +111,8 @@ } /** - * get_tlabel - allocate a transaction label - * @host: host to be used for transmission - * @nodeid: the node ID of the transmission target + * hpsb_get_tlabel - allocate a transaction label + * @packet: the packet who's tlabel/tpool we set * @wait: whether to sleep if no tlabel is available * * Every asynchronous transaction on the 1394 bus needs a transaction label to @@ -171,72 +121,66 @@ * matching possible without ambiguity. * * There are 64 different tlabels, so an allocated tlabel has to be freed with - * free_tlabel() after the transaction is complete (unless it's reused again for + * hpsb_free_tlabel() after the transaction is complete (unless it's reused again for * the same target node). * - * @wait must not be set to true if you are calling from interrupt context. + * @wait cannot be set if in_interrupt() * - * Return value: The allocated transaction label or -1 if there was no free - * tlabel and @wait is false. + * Return value: Zero on success, otherwise non-zero. A non-zero return + * generally means there are no available tlabels. */ -int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait) +int hpsb_get_tlabel(struct hpsb_packet *packet, int wait) { - int tlabel = 0; unsigned long flags; - int found_tlabel = 0; + struct hpsb_tlabel_pool *tp; + + tp = &packet->host->tpool[packet->node_id & NODE_MASK]; if (wait) { - down(&host->tlabel_count); + BUG_ON(in_interrupt()); + down(&tp->count); } else { - if (down_trylock(&host->tlabel_count)) return -1; + if (down_trylock(&tp->count)) + return 1; } - spin_lock_irqsave(&host->tlabel_lock, flags); - - while (!found_tlabel) { - tlabel = host->tlabel_current; - if (tlabel < 32 && !(host->tlabel_pool[0] & 1 << tlabel)) { - host->tlabel_pool[0] |= 1 << tlabel; - found_tlabel = 1; - } else if (!(host->tlabel_pool[1] & 1 << (tlabel - 32))) { - host->tlabel_pool[1] |= 1 << (tlabel - 32); - found_tlabel = 1; - } - host->tlabel_current = (host->tlabel_current + 1) % 64; - } + spin_lock_irqsave(&tp->lock, flags); - spin_unlock_irqrestore(&host->tlabel_lock, flags); - - return tlabel; + packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next); + tp->next = (packet->tlabel + 1) % 64; + /* Should _never_ happen */ + BUG_ON(test_and_set_bit(packet->tlabel, tp->pool)); + tp->allocations++; + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; } -/** - * free_tlabel - free an allocated transaction label - * @host: host to be used for transmission - * @nodeid: the node ID of the transmission target - * @tlabel: the transaction label to free +/** + * hpsb_free_tlabel - free an allocated transaction label + * @packet: packet whos tlabel/tpool needs to be cleared * - * Frees the transaction label allocated with get_tlabel(). The tlabel has to - * be freed after the transaction is complete (i.e. response was received for a - * split transaction or packet was sent for a unified transaction). + * Frees the transaction label allocated with hpsb_get_tlabel(). The + * tlabel has to be freed after the transaction is complete (i.e. response + * was received for a split transaction or packet was sent for a unified + * transaction). * * A tlabel must not be freed twice. */ -void free_tlabel(struct hpsb_host *host, nodeid_t nodeid, int tlabel) +void hpsb_free_tlabel(struct hpsb_packet *packet) { unsigned long flags; + struct hpsb_tlabel_pool *tp; + + tp = &packet->host->tpool[packet->node_id & NODE_MASK]; - spin_lock_irqsave(&host->tlabel_lock, flags); + BUG_ON(packet->tlabel > 63 || packet->tlabel < 0); - if (tlabel < 32) { - host->tlabel_pool[0] &= ~(1 << tlabel); - } else { - host->tlabel_pool[1] &= ~(1 << (tlabel-32)); - } + spin_lock_irqsave(&tp->lock, flags); + BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool)); + spin_unlock_irqrestore(&tp->lock, flags); - spin_unlock_irqrestore(&host->tlabel_lock, flags); - - up(&host->tlabel_count); + up(&tp->count); } @@ -309,124 +253,144 @@ HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__); } -struct hpsb_packet *hpsb_make_readqpacket(struct hpsb_host *host, nodeid_t node, - u64 addr) +struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, size_t length) { - struct hpsb_packet *p; - - p = alloc_hpsb_packet(0); - if (!p) return NULL; + struct hpsb_packet *packet; - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - fill_async_readquad(p, addr); + if (length == 0) + return NULL; - return p; -} + packet = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0)); + if (!packet) + return NULL; -struct hpsb_packet *hpsb_make_readbpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length) -{ - struct hpsb_packet *p; + packet->host = host; + packet->node_id = node; - p = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0)); - if (!p) return NULL; + if (hpsb_get_tlabel(packet, in_interrupt() ? 0 : 1)) { + free_hpsb_packet(packet); + return NULL; + } - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - fill_async_readblock(p, addr, length); + if (length == 4) + fill_async_readquad(packet, addr); + else + fill_async_readblock(packet, addr, length); - return p; + return packet; } -struct hpsb_packet *hpsb_make_writeqpacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - quadlet_t data) +struct hpsb_packet *hpsb_make_writepacket (struct hpsb_host *host, nodeid_t node, + u64 addr, quadlet_t *buffer, size_t length) { - struct hpsb_packet *p; - - p = alloc_hpsb_packet(0); - if (!p) return NULL; - - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - fill_async_writequad(p, addr, data); + struct hpsb_packet *packet; - return p; -} + if (length == 0) + return NULL; -struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - size_t length) -{ - struct hpsb_packet *p; + packet = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0)); + if (!packet) + return NULL; - p = alloc_hpsb_packet(length + (length % 4 ? 4 - (length % 4) : 0)); - if (!p) return NULL; + if (length % 4) { /* zero padding bytes */ + packet->data[length >> 2] = 0; + } + packet->host = host; + packet->node_id = node; - if (length % 4) { - p->data[length / 4] = 0; - } + if (hpsb_get_tlabel(packet, in_interrupt() ? 0 : 1)) { + free_hpsb_packet(packet); + return NULL; + } - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - fill_async_writeblock(p, addr, length); + if (length == 4) { + fill_async_writequad(packet, addr, buffer ? *buffer : 0); + } else { + fill_async_writeblock(packet, addr, length); + if (buffer) + memcpy(packet->data, buffer, length); + } - return p; + return packet; } struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode) + u64 addr, int extcode, quadlet_t *data, + quadlet_t arg) { - struct hpsb_packet *p; + struct hpsb_packet *p; + u32 length; - p = alloc_hpsb_packet(8); - if (!p) return NULL; + p = alloc_hpsb_packet(8); + if (!p) return NULL; - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - fill_async_lock(p, addr, extcode, 4); - break; - default: - fill_async_lock(p, addr, extcode, 8); - break; - } + p->host = host; + p->node_id = node; + if (hpsb_get_tlabel(p, in_interrupt() ? 0 : 1)) { + free_hpsb_packet(p); + return NULL; + } + + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + length = 4; + if (data) + p->data[0] = *data; + break; + default: + length = 8; + if (data) { + p->data[0] = arg; + p->data[1] = *data; + } + break; + } + fill_async_lock(p, addr, extcode, length); - return p; + return p; } struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode) + u64 addr, int extcode, octlet_t *data, + octlet_t arg) { - struct hpsb_packet *p; + struct hpsb_packet *p; + u32 length; - p = alloc_hpsb_packet(16); - if (!p) return NULL; + p = alloc_hpsb_packet(16); + if (!p) return NULL; - p->host = host; - p->tlabel = get_tlabel(host, node, 1); - p->node_id = node; - - switch (extcode) { - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - fill_async_lock(p, addr, extcode, 8); - break; - default: - fill_async_lock(p, addr, extcode, 16); - break; - } + p->host = host; + p->node_id = node; + if (hpsb_get_tlabel(p, in_interrupt() ? 0 : 1)) { + free_hpsb_packet(p); + return NULL; + } - return p; + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + length = 8; + if (data) { + p->data[0] = *data >> 32; + p->data[1] = *data & 0xffffffff; + } + break; + default: + length = 16; + if (data) { + p->data[0] = arg >> 32; + p->data[1] = arg & 0xffffffff; + p->data[2] = *data >> 32; + p->data[3] = *data & 0xffffffff; + } + break; + } + fill_async_lock(p, addr, extcode, length); + + return p; } struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, @@ -443,6 +407,23 @@ return p; } +struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, + int length, int channel, + int tag, int sync) +{ + struct hpsb_packet *p; + + p = alloc_hpsb_packet(length); + if (!p) return NULL; + + p->host = host; + fill_iso_packet(p, length, channel, tag, sync); + + p->generation = get_hpsb_generation(host); + + return p; +} + /* * FIXME - these functions should probably read from / write to user space to * avoid in kernel buffers for user space callers @@ -454,15 +435,12 @@ struct hpsb_packet *packet; int retval = 0; - if (length == 0) { + if (length == 0) return -EINVAL; - } - if (length == 4) { - packet = hpsb_make_readqpacket(host, node, addr); - } else { - packet = hpsb_make_readbpacket(host, node, addr, length); - } + BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet + + packet = hpsb_make_readpacket(host, node, addr, length); if (!packet) { return -ENOMEM; @@ -487,35 +465,12 @@ } hpsb_read_fail: - free_tlabel(host, node, packet->tlabel); + hpsb_free_tlabel(packet); free_hpsb_packet(packet); return retval; } -struct hpsb_packet *hpsb_make_packet (struct hpsb_host *host, nodeid_t node, - u64 addr, quadlet_t *buffer, size_t length) -{ - struct hpsb_packet *packet; - - if (length == 0) - return NULL; - - if (length == 4) - packet = hpsb_make_writeqpacket(host, node, addr, *buffer); - else - packet = hpsb_make_writebpacket(host, node, addr, length); - - if (!packet) - return NULL; - - /* Sometimes this may be called without data, just to allocate the - * packet. */ - if (length != 4 && buffer) - memcpy(packet->data, buffer, length); - - return packet; -} int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length) @@ -526,7 +481,9 @@ if (length == 0) return -EINVAL; - packet = hpsb_make_packet (host, node, addr, buffer, length); + BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet + + packet = hpsb_make_writepacket (host, node, addr, buffer, length); if (!packet) return -ENOMEM; @@ -542,7 +499,7 @@ retval = hpsb_packet_success(packet); hpsb_write_fail: - free_tlabel(host, node, packet->tlabel); + hpsb_free_tlabel(packet); free_hpsb_packet(packet); return retval; @@ -553,35 +510,13 @@ u64 addr, int extcode, quadlet_t *data, quadlet_t arg) { struct hpsb_packet *packet; - int retval = 0, length; - - packet = alloc_hpsb_packet(8); - if (!packet) { - return -ENOMEM; - } + int retval = 0; - packet->host = host; - packet->tlabel = get_tlabel(host, node, 1); - packet->node_id = node; - - switch (extcode) { - case EXTCODE_MASK_SWAP: - case EXTCODE_COMPARE_SWAP: - case EXTCODE_BOUNDED_ADD: - case EXTCODE_WRAP_ADD: - length = 8; - packet->data[0] = arg; - packet->data[1] = *data; - break; - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - length = 4; - packet->data[0] = *data; - break; - default: - return -EINVAL; - } - fill_async_lock(packet, addr, extcode, length); + BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet + + packet = hpsb_make_lockpacket(host, node, addr, extcode, data, arg); + if (!packet) + return -ENOMEM; packet->generation = generation; if (!hpsb_send_packet(packet)) { @@ -597,7 +532,7 @@ } hpsb_lock_fail: - free_tlabel(host, node, packet->tlabel); + hpsb_free_tlabel(packet); free_hpsb_packet(packet); return retval; @@ -611,27 +546,7 @@ BUG_ON(in_interrupt()); // We can't be called in an interrupt, yet - packet = hpsb_make_lock64packet(host, node, addr, extcode); - - switch (extcode) { - case EXTCODE_MASK_SWAP: - case EXTCODE_COMPARE_SWAP: - case EXTCODE_BOUNDED_ADD: - case EXTCODE_WRAP_ADD: - packet->data[0] = (arg >> 32); - packet->data[1] = (arg & 0xffffffff); - packet->data[2] = (*data >> 32); - packet->data[3] = (*data & 0xffffffff); - break; - case EXTCODE_FETCH_ADD: - case EXTCODE_LITTLE_ADD: - packet->data[0] = (*data >> 32); - packet->data[1] = (*data & 0xffffffff); - break; - default: - return -EINVAL; - } - + packet = hpsb_make_lock64packet(host, node, addr, extcode, data, arg); if (!packet) return -ENOMEM; @@ -648,7 +563,7 @@ *data = (u64)packet->data[1] << 32 | packet->data[0]; hpsb_lock64_fail: - free_tlabel(host, node, packet->tlabel); + hpsb_free_tlabel(packet); free_hpsb_packet(packet); return retval; @@ -702,9 +617,9 @@ packet->no_waiter = 1; if (!hpsb_send_packet(packet)) { - free_hpsb_packet(packet); + free_hpsb_packet(packet); retval = -EINVAL; } - return retval; + return retval; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_transactions.h linux.21rc1-ac2/drivers/ieee1394/ieee1394_transactions.h --- linux.21rc1/drivers/ieee1394/ieee1394_transactions.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_transactions.h 2003-04-25 16:05:08.000000000 +0100 @@ -5,48 +5,26 @@ /* - * Utility functions to fill out packet headers. - */ -void fill_async_readquad(struct hpsb_packet *packet, u64 addr); -void fill_async_readquad_resp(struct hpsb_packet *packet, int rcode, - quadlet_t data); -void fill_async_readblock(struct hpsb_packet *packet, u64 addr, int length); -void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, - int length); -void fill_async_writequad(struct hpsb_packet *packet, u64 addr, quadlet_t data); -void fill_async_writeblock(struct hpsb_packet *packet, u64 addr, int length); -void fill_async_write_resp(struct hpsb_packet *packet, int rcode); -void fill_async_lock(struct hpsb_packet *packet, u64 addr, int extcode, - int length); -void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extcode, - int length); -void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, - int tag, int sync); -void fill_phy_packet(struct hpsb_packet *packet, quadlet_t data); - -/* * Get and free transaction labels. */ -int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait); -void free_tlabel(struct hpsb_host *host, nodeid_t nodeid, int tlabel); +int hpsb_get_tlabel(struct hpsb_packet *packet, int wait); +void hpsb_free_tlabel(struct hpsb_packet *packet); -struct hpsb_packet *hpsb_make_readqpacket(struct hpsb_host *host, nodeid_t node, - u64 addr); -struct hpsb_packet *hpsb_make_readbpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, size_t length); -struct hpsb_packet *hpsb_make_writeqpacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - quadlet_t data); -struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, - nodeid_t node, u64 addr, - size_t length); +struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, size_t length); struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode); + u64 addr, int extcode, quadlet_t *data, + quadlet_t arg); struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, nodeid_t node, - u64 addr, int extcode); + u64 addr, int extcode, octlet_t *data, + octlet_t arg); struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data) ; - +struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, + int length, int channel, + int tag, int sync); +struct hpsb_packet *hpsb_make_writepacket (struct hpsb_host *host, nodeid_t node, + u64 addr, quadlet_t *buffer, size_t length); /* * hpsb_packet_success - Make sense of the ack and reply codes and @@ -78,12 +56,7 @@ int hpsb_lock64(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, int extcode, octlet_t *data, octlet_t arg); int hpsb_send_gasp(struct hpsb_host *host, int channel, unsigned int generation, - quadlet_t *buffer, size_t length, u32 specifier_id, - unsigned int version); - -/* Generic packet creation. Used by hpsb_write. Also useful for protocol - * drivers that want to implement their own hpsb_write replacement. */ -struct hpsb_packet *hpsb_make_packet (struct hpsb_host *host, nodeid_t node, - u64 addr, quadlet_t *buffer, size_t length); + quadlet_t *buffer, size_t length, u32 specifier_id, + unsigned int version); #endif /* _IEEE1394_TRANSACTIONS_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ieee1394_types.h linux.21rc1-ac2/drivers/ieee1394/ieee1394_types.h --- linux.21rc1/drivers/ieee1394/ieee1394_types.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ieee1394_types.h 2003-04-25 16:05:08.000000000 +0100 @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -19,6 +20,25 @@ #endif +/* Transaction Label handling */ +struct hpsb_tlabel_pool { + DECLARE_BITMAP(pool, 64); + spinlock_t lock; + u8 next; + u32 allocations; + struct semaphore count; +}; + +#define HPSB_TPOOL_INIT(_tp) \ +do { \ + CLEAR_BITMAP((_tp)->pool, 64); \ + spin_lock_init(&(_tp)->lock); \ + (_tp)->next = 0; \ + (_tp)->allocations = 0; \ + sema_init(&(_tp)->count, 63); \ +} while(0) + + typedef u32 quadlet_t; typedef u64 octlet_t; typedef u16 nodeid_t; @@ -28,6 +48,7 @@ typedef u16 arm_length_t; #define BUS_MASK 0xffc0 +#define BUS_SHIFT 6 #define NODE_MASK 0x003f #define LOCAL_BUS 0xffc0 #define ALL_NODES 0x003f @@ -36,9 +57,8 @@ #define NODEID_TO_NODE(nodeid) (nodeid & NODE_MASK) /* Can be used to consistently print a node/bus ID. */ -#define NODE_BUS_FMT "%02d:%04d" -#define NODE_BUS_ARGS(nodeid) \ - (nodeid & NODE_MASK), ((nodeid & BUS_MASK) >> 6) +#define NODE_BUS_FMT "%02d:%04d" +#define NODE_BUS_ARGS(nodeid) NODEID_TO_NODE(nodeid), NODEID_TO_BUS(nodeid) #define HPSB_PRINT(level, fmt, args...) printk(level "ieee1394: " fmt "\n" , ## args) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/Makefile linux.21rc1-ac2/drivers/ieee1394/Makefile --- linux.21rc1/drivers/ieee1394/Makefile 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/Makefile 2003-04-23 16:49:51.000000000 +0100 @@ -8,7 +8,7 @@ list-multi := ieee1394.o ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ - highlevel.o csr.o nodemgr.o dma.o iso.o + highlevel.o csr.o nodemgr.o oui.o dma.o iso.o obj-$(CONFIG_IEEE1394) += ieee1394.o obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o @@ -25,3 +25,7 @@ ieee1394.o: $(ieee1394-objs) $(LD) $(LDFLAGS) -r -o $@ $(ieee1394-objs) + +oui.o: oui.c +oui.c: oui.db oui2c.sh + $(CONFIG_SHELL) oui2c.sh < oui.db > $@ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/nodemgr.c linux.21rc1-ac2/drivers/ieee1394/nodemgr.c --- linux.21rc1/drivers/ieee1394/nodemgr.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/nodemgr.c 2003-04-25 03:58:53.000000000 +0100 @@ -1,17 +1,17 @@ /* * Node information (ConfigROM) collection and management. * - * Copyright (C) 2000 Andreas E. Bombe - * 2001 Ben Collins + * Copyright (C) 2000 Andreas E. Bombe + * 2001-2003 Ben Collins * * This code is licensed under the GPL. See the file COPYING in the root * directory of the kernel sources for details. */ #include +#include #include #include -#include #include #include #include @@ -31,6 +31,24 @@ #include "nodemgr.h" + +static char *nodemgr_find_oui_name(int oui) +{ +#ifdef CONFIG_IEEE1394_OUI_DB + extern struct oui_list_struct { + int oui; + char *name; + } oui_list[]; + int i; + + for (i = 0; oui_list[i].name; i++) + if (oui_list[i].oui == oui) + return oui_list[i].name; +#endif + return NULL; +} + + /* * Basically what we do here is start off retrieving the bus_info block. * From there will fill in some info about the node, verify it is of IEEE @@ -40,9 +58,9 @@ * complete directory entry (be it a leaf or a directory). We then process * it and add the info to our structure for that particular node. * - * We verify CRC's along the way for each directory/block/leaf. The - * entire node structure is generic, and simply stores the information in - * a way that's easy to parse by the protocol interface. + * We verify CRC's along the way for each directory/block/leaf. The entire + * node structure is generic, and simply stores the information in a way + * that's easy to parse by the protocol interface. */ /* The nodemgr maintains a number of data structures: the node list, @@ -61,20 +79,17 @@ static LIST_HEAD(driver_list); static LIST_HEAD(unit_directory_list); -static LIST_HEAD(host_info_list); -static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; - -/* Disables use of the hotplug calls. */ -static int nodemgr_disable_hotplug = 0; struct host_info { struct hpsb_host *host; - struct list_head list; struct completion exited; struct semaphore reset_sem; int pid; + char daemon_name[15]; }; +static struct hpsb_highlevel nodemgr_highlevel; + #ifdef CONFIG_PROC_FS #define PUTF(fmt, args...) out += sprintf(out, fmt, ## args) @@ -160,7 +175,7 @@ PUTF(" Software Version: %06x\n", ud->version); if (ud->driver) PUTF(" Driver: %s\n", ud->driver->name); - PUTF(" Length (in quads): %d\n", ud->count); + PUTF(" Length (in quads): %d\n", ud->length); } } @@ -261,7 +276,7 @@ ret = -ENXIO; for (; size > 0; size--, address += 4, quadp++) { for (i = 0; i < 3; i++) { - ret = hpsb_read(ne->host, ne->nodeid, ne->generation, address, quadp, 4); + ret = hpsb_node_read(ne, address, quadp, 4); if (ret != -EAGAIN) break; } @@ -312,30 +327,32 @@ address += 4; length--; total_size += (size + 1) * sizeof (quadlet_t); - } - else if (size < 0) + } else if (size < 0) return NULL; } } - ne = kmalloc(total_size, SLAB_ATOMIC); - if (ne != NULL) { - memset(ne, 0, total_size); - if (size != 0) { - ne->vendor_name - = (const char *) &(ne->quadlets[2]); - ne->quadlets[size] = 0; - } - else { - ne->vendor_name = NULL; - } + ne = kmalloc(total_size, GFP_KERNEL); + + if (!ne) + return NULL; + + memset(ne, 0, total_size); + + if (size != 0) { + ne->vendor_name = (const char *) &(ne->quadlets[2]); + ne->quadlets[size] = 0; + } else { + ne->vendor_name = NULL; } + return ne; } static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions, - struct hpsb_host *host, + struct host_info *hi, nodeid_t nodeid, unsigned int generation) { + struct hpsb_host *host = hi->host; struct node_entry *ne; ne = nodemgr_scan_root_directory (host, nodeid, generation); @@ -345,8 +362,10 @@ INIT_LIST_HEAD(&ne->unit_directories); ne->host = host; ne->nodeid = nodeid; - ne->guid = guid; ne->generation = generation; + ne->guid = guid; + ne->guid_vendor_id = (guid >> 40) & 0xffffff; + ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id); list_add_tail(&ne->list, &node_list); @@ -373,14 +392,15 @@ return NULL; } -static struct node_entry *find_entry_by_nodeid(nodeid_t nodeid) +static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid) { struct list_head *lh; struct node_entry *ne; list_for_each(lh, &node_list) { ne = list_entry(lh, struct node_entry, list); - if (ne->nodeid == nodeid) return ne; + if (ne->nodeid == nodeid && ne->host == host) + return ne; } return NULL; @@ -468,26 +488,25 @@ return NULL; } } + total_size += count * sizeof (quadlet_t); ud = kmalloc (total_size, GFP_KERNEL); + if (ud != NULL) { - memset (ud, 0, sizeof *ud); + memset (ud, 0, total_size); ud->flags = flags; - ud->count = count; + ud->length = count; ud->vendor_name_size = vendor_name_size; ud->model_name_size = model_name_size; - /* If there is no vendor name in the unit directory, - use the one in the root directory. */ - ud->vendor_name = ne->vendor_name; } + return ud; } + /* This implementation currently only scans the config rom and its * immediate unit directories looking for software_id and - * software_version entries, in order to get driver autoloading working. - */ - + * software_version entries, in order to get driver autoloading working. */ static struct unit_directory * nodemgr_process_unit_directory (struct node_entry *ne, octlet_t address, struct unit_directory *parent) { @@ -496,13 +515,13 @@ quadlet_t *infop; int length; struct unit_directory *ud_temp = NULL; - + if (!(ud = nodemgr_scan_unit_directory(ne, address))) goto unit_directory_error; ud->ne = ne; ud->address = address; - + if (parent != NULL) ud->parent = parent; @@ -513,7 +532,7 @@ address += 4; infop = (quadlet_t *) ud->quadlets; - for (; length > 0; length--, address += 4, infop++) { + for (; length > 0; length--, address += 4) { int code; quadlet_t value; quadlet_t *quadp; @@ -528,14 +547,16 @@ case CONFIG_ROM_VENDOR_ID: ud->vendor_id = value; ud->flags |= UNIT_DIRECTORY_VENDOR_ID; + + if (ud->vendor_id) + ud->vendor_oui = nodemgr_find_oui_name(ud->vendor_id); + if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) { length--; address += 4; - quadp = &(ud->quadlets[ud->count]); - if (nodemgr_read_text_leaf(ne, address, - quadp) == 0 - && quadp[0] == 0 - && quadp[1] == 0) { + quadp = &(ud->quadlets[ud->length]); + if (nodemgr_read_text_leaf(ne, address, quadp) == 0 + && quadp[0] == 0 && quadp[1] == 0) { /* We only support minimal ASCII and English. */ quadp[ud->vendor_name_size] = 0; @@ -551,11 +572,9 @@ if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) { length--; address += 4; - quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]); - if (nodemgr_read_text_leaf(ne, address, - quadp) == 0 - && quadp[0] == 0 - && quadp[1] == 0) { + quadp = &(ud->quadlets[ud->length + ud->vendor_name_size + 1]); + if (nodemgr_read_text_leaf(ne, address, quadp) == 0 + && quadp[0] == 0 && quadp[1] == 0) { /* We only support minimal ASCII and English. */ quadp[ud->model_name_size] = 0; @@ -581,13 +600,12 @@ break; case CONFIG_ROM_LOGICAL_UNIT_DIRECTORY: - { ud_temp = nodemgr_process_unit_directory(ne, address + value * 4, ud); + /* inherit unspecified values */ - if (ud_temp != NULL ) + if (ud_temp != NULL) { - ud->n_children++; - if ((ud->flags & UNIT_DIRECTORY_VENDOR_ID) && + if ((ud->flags & UNIT_DIRECTORY_VENDOR_ID) && !(ud_temp->flags & UNIT_DIRECTORY_VENDOR_ID)) { ud_temp->flags |= UNIT_DIRECTORY_VENDOR_ID; @@ -605,15 +623,14 @@ ud_temp->flags |= UNIT_DIRECTORY_SPECIFIER_ID; ud_temp->specifier_id = ud->specifier_id; } - if ((ud->flags & UNIT_DIRECTORY_VERSION) && + if ((ud->flags & UNIT_DIRECTORY_VERSION) && !(ud_temp->flags & UNIT_DIRECTORY_VERSION)) { ud_temp->flags |= UNIT_DIRECTORY_VERSION; ud_temp->version = ud->version; } } - - } + break; default: @@ -622,7 +639,7 @@ CSR offsets for now. */ code &= CONFIG_ROM_KEY_TYPE_MASK; if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0) - *infop = quad; + *infop++ = quad; break; } } @@ -638,33 +655,6 @@ return NULL; } -static void dump_directories (struct node_entry *ne) -{ -#ifdef CONFIG_IEEE1394_VERBOSEDEBUG - struct list_head *l; - - HPSB_DEBUG("vendor_id=0x%06x [%s], capabilities=0x%06x", - ne->vendor_id, ne->vendor_name ?: "Unknown", - ne->capabilities); - list_for_each (l, &ne->unit_directories) { - struct unit_directory *ud = list_entry (l, struct unit_directory, node_list); - HPSB_DEBUG("unit directory:"); - if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) - HPSB_DEBUG(" vendor_id=0x%06x [%s]", - ud->vendor_id, - ud->vendor_name ?: "Unknown"); - if (ud->flags & UNIT_DIRECTORY_MODEL_ID) - HPSB_DEBUG(" model_id=0x%06x [%s]", - ud->model_id, - ud->model_name ?: "Unknown"); - if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) - HPSB_DEBUG(" sw_specifier_id=0x%06x ", ud->specifier_id); - if (ud->flags & UNIT_DIRECTORY_VERSION) - HPSB_DEBUG(" sw_version=0x%06x ", ud->version); - } -#endif - return; -} static void nodemgr_process_root_directory(struct node_entry *ne) { @@ -697,16 +687,17 @@ switch (code) { case CONFIG_ROM_VENDOR_ID: ne->vendor_id = value; + + if (ne->vendor_id) + ne->vendor_oui = nodemgr_find_oui_name(ne->vendor_id); + /* Now check if there is a vendor name text string. */ if (ne->vendor_name != NULL) { length--; address += 4; - if (nodemgr_read_text_leaf(ne, address, - ne->quadlets) - != 0 - || ne->quadlets [0] != 0 - || ne->quadlets [1] != 0) + if (nodemgr_read_text_leaf(ne, address, ne->quadlets) != 0 + || ne->quadlets[0] != 0 || ne->quadlets[1] != 0) /* We only support minimal ASCII and English. */ ne->vendor_name = NULL; @@ -727,8 +718,6 @@ break; } } - - dump_directories(ne); } #ifdef CONFIG_HOTPLUG @@ -738,10 +727,6 @@ char *argv [3], **envp, *buf, *scratch; int i = 0, value; - /* User requested to disable hotplug when module was loaded. */ - if (nodemgr_disable_hotplug) - return; - if (!hotplug_path [0]) return; if (!current->fs->root) @@ -917,6 +902,7 @@ } } + int hpsb_register_protocol(struct hpsb_protocol_driver *driver) { struct unit_directory *ud; @@ -1021,8 +1007,8 @@ * the to take whatever actions required. */ static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions, - struct hpsb_host *host, - nodeid_t nodeid, unsigned int generation) + struct host_info *hi, nodeid_t nodeid, + unsigned int generation) { struct list_head *lh; struct unit_directory *ud; @@ -1130,9 +1116,10 @@ /* This is where we probe the nodes for their information and provided * features. */ -static void nodemgr_node_probe_one(struct hpsb_host *host, +static void nodemgr_node_probe_one(struct host_info *hi, nodeid_t nodeid, int generation) { + struct hpsb_host *host = hi->host; struct node_entry *ne; quadlet_t buffer[5]; octlet_t guid; @@ -1160,9 +1147,9 @@ ne = find_entry_by_guid(guid); if (!ne) - nodemgr_create_node(guid, buffer[2], host, nodeid, generation); + nodemgr_create_node(guid, buffer[2], hi, nodeid, generation); else - nodemgr_update_node(ne, buffer[2], host, nodeid, generation); + nodemgr_update_node(ne, buffer[2], hi, nodeid, generation); return; } @@ -1192,9 +1179,10 @@ return; } -static void nodemgr_node_probe(struct hpsb_host *host, int generation) +static void nodemgr_node_probe(struct host_info *hi, int generation) { int count; + struct hpsb_host *host = hi->host; struct selfid *sid = (struct selfid *)host->topology_map; nodeid_t nodeid = LOCAL_BUS; @@ -1207,8 +1195,7 @@ nodeid++; continue; } - - nodemgr_node_probe_one(host, nodeid++, generation); + nodemgr_node_probe_one(hi, nodeid++, generation); } /* If we had a bus reset while we were scanning the bus, it is @@ -1225,7 +1212,7 @@ /* Because we are a 1394a-2000 compliant IRM, we need to inform all the other * nodes of the broadcast channel. (Really we're only setting the validity - * bit). */ + * bit). Other IRM responsibilities go in here as well. */ static void nodemgr_do_irm_duties(struct hpsb_host *host) { quadlet_t bc; @@ -1239,14 +1226,31 @@ hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host), (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL), - &bc, - sizeof(quadlet_t)); + &bc, sizeof(quadlet_t)); + + /* If there is no bus manager then we should set the root node's + * force_root bit to promote bus stability per the 1394 + * spec. (8.4.2.6) */ + if (host->busmgr_id == 0x3f && host->node_count > 1) + { + u16 root_node = host->node_count - 1; + struct node_entry *ne = hpsb_nodeid_get_entry(host, root_node); + + if (ne->busopt.cmc) + hpsb_send_phy_config(host, root_node, -1); + else { + HPSB_DEBUG("The root node is not cycle master capable; " + "selecting a new root node and resetting..."); + hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); + hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); + } + } } /* We need to ensure that if we are not the IRM, that the IRM node is capable of * everything we can do, otherwise issue a bus reset and try to become the IRM * ourselves. */ -static int nodemgr_check_root_capability(struct hpsb_host *host) +static int nodemgr_check_irm_capability(struct hpsb_host *host, int cycles) { quadlet_t bc; int status; @@ -1260,23 +1264,35 @@ &bc, sizeof(quadlet_t)); if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) { - /* The root node does not have a valid BROADCAST_CHANNEL + /* The current irm node does not have a valid BROADCAST_CHANNEL * register and we do, so reset the bus with force_root set */ - HPSB_INFO("Remote root is not IRM capable, resetting..."); + HPSB_DEBUG("Current remote IRM is not 1394a-2000 compliant, resetting..."); + + if (cycles >= 5) { + /* Oh screw it! Just leave the bus as it is */ + HPSB_DEBUG("Stopping reset loop for IRM sanity"); + return 1; + } + + hpsb_send_phy_config(host, NODEID_TO_NODE(host->node_id), -1); hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT); + return 0; } + return 1; } static int nodemgr_host_thread(void *__hi) { struct host_info *hi = (struct host_info *)__hi; + struct hpsb_host *host = hi->host; + int reset_cycles = 0; /* No userlevel access needed */ daemonize(); - strcpy(current->comm, "knodemgrd"); + strcpy(current->comm, hi->daemon_name); /* Sit and wait for a signal to probe the nodes on the bus. This * happens when we get a bus reset. */ @@ -1296,7 +1312,7 @@ * for the read transactions, so that if another reset occurs * during the scan the transactions will fail instead of * returning bogus data. */ - generation = get_hpsb_generation(hi->host); + generation = get_hpsb_generation(host); /* If we get a reset before we are done waiting, then * start the the waiting over again */ @@ -1304,21 +1320,23 @@ i = HZ/4; } - if (!nodemgr_check_root_capability(hi->host)) { + if (!nodemgr_check_irm_capability(host, reset_cycles++)) { /* Do nothing, we are resetting */ up(&nodemgr_serialize); continue; } - nodemgr_node_probe(hi->host, generation); - nodemgr_do_irm_duties(hi->host); + reset_cycles = 0; + + nodemgr_node_probe(hi, generation); + nodemgr_do_irm_duties(host); up(&nodemgr_serialize); } - caught_signal: +caught_signal: #ifdef CONFIG_IEEE1394_VERBOSEDEBUG - HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name); + HPSB_DEBUG ("NodeMgr: Exiting thread"); #endif complete_and_exit(&hi->exited, 0); @@ -1335,12 +1353,12 @@ return ne; } -struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid) +struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid) { struct node_entry *ne; down(&nodemgr_serialize); - ne = find_entry_by_nodeid(nodeid); + ne = find_entry_by_nodeid(host, nodeid); up(&nodemgr_serialize); return ne; @@ -1401,56 +1419,37 @@ static void nodemgr_add_host(struct hpsb_host *host) { - struct host_info *hi = kmalloc (sizeof (struct host_info), GFP_KERNEL); - unsigned long flags; + struct host_info *hi; + + hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi)); if (!hi) { HPSB_ERR ("NodeMgr: out of memory in add host"); return; } - /* Initialize the hostinfo here and start the thread. The - * thread blocks on the reset semaphore until a bus reset - * happens. */ hi->host = host; - INIT_LIST_HEAD(&hi->list); init_completion(&hi->exited); sema_init(&hi->reset_sem, 0); - spin_lock_irqsave (&host_info_lock, flags); + sprintf(hi->daemon_name, "knodemgrd_%d", host->id); hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - + if (hi->pid < 0) { - HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s", - host->driver->name); - kfree(hi); - spin_unlock_irqrestore (&host_info_lock, flags); + HPSB_ERR ("NodeMgr: failed to start %s thread for %s", + hi->daemon_name, host->driver->name); + hpsb_destroy_hostinfo(&nodemgr_highlevel, host); return; } - list_add_tail(&hi->list, &host_info_list); - - spin_unlock_irqrestore (&host_info_lock, flags); - return; } static void nodemgr_host_reset(struct hpsb_host *host) { - struct list_head *lh; - struct host_info *hi = NULL; - unsigned long flags; - - spin_lock_irqsave (&host_info_lock, flags); - list_for_each(lh, &host_info_list) { - struct host_info *myhi = list_entry(lh, struct host_info, list); - if (myhi->host == host) { - hi = myhi; - break; - } - } + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); if (hi != NULL) { #ifdef CONFIG_IEEE1394_VERBOSEDEBUG @@ -1458,9 +1457,7 @@ #endif up(&hi->reset_sem); } else - HPSB_ERR ("NodeMgr: could not process reset of non-existent host"); - - spin_unlock_irqrestore (&host_info_lock, flags); + HPSB_ERR ("NodeMgr: could not process reset of unused host"); return; } @@ -1469,28 +1466,14 @@ { struct list_head *lh, *next; struct node_entry *ne; - unsigned long flags; - struct host_info *hi = NULL; - - spin_lock_irqsave (&host_info_lock, flags); - list_for_each_safe(lh, next, &host_info_list) { - struct host_info *myhi = list_entry(lh, struct host_info, list); - if (myhi->host == host) { - list_del(&myhi->list); - hi = myhi; - break; - } - } - spin_unlock_irqrestore (&host_info_lock, flags); + struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host); if (hi) { if (hi->pid >= 0) { kill_proc(hi->pid, SIGTERM, 1); wait_for_completion(&hi->exited); } - kfree(hi); - } - else + } else HPSB_ERR("NodeMgr: host %s does not exist, cannot remove", host->driver->name); @@ -1510,32 +1493,27 @@ return; } -static struct hpsb_highlevel_ops nodemgr_ops = { +static struct hpsb_highlevel nodemgr_highlevel = { + .name = "Node manager", .add_host = nodemgr_add_host, .host_reset = nodemgr_host_reset, .remove_host = nodemgr_remove_host, }; -static struct hpsb_highlevel *hl; - #define PROC_ENTRY "devices" -void init_ieee1394_nodemgr(int disable_hotplug) +void init_ieee1394_nodemgr(void) { - nodemgr_disable_hotplug = disable_hotplug; #ifdef CONFIG_PROC_FS if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL)) HPSB_ERR("Can't create devices procfs entry"); #endif - hl = hpsb_register_highlevel("Node manager", &nodemgr_ops); - if (!hl) { - HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization"); - } + hpsb_register_highlevel(&nodemgr_highlevel); } void cleanup_ieee1394_nodemgr(void) { - hpsb_unregister_highlevel(hl); + hpsb_unregister_highlevel(&nodemgr_highlevel); #ifdef CONFIG_PROC_FS remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry); #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/nodemgr.h linux.21rc1-ac2/drivers/ieee1394/nodemgr.h --- linux.21rc1/drivers/ieee1394/nodemgr.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/nodemgr.h 2003-04-23 14:32:44.000000000 +0100 @@ -98,8 +98,11 @@ struct node_entry *ne; /* The node which this directory belongs to */ octlet_t address; /* Address of the unit directory on the node */ u8 flags; /* Indicates which entries were read */ + quadlet_t vendor_id; const char *vendor_name; + const char *vendor_oui; + int vendor_name_size; quadlet_t model_id; const char *model_name; @@ -118,15 +121,19 @@ /* for tracking unit versus logical unit */ struct unit_directory *parent; - int n_children; - int count; /* Number of quadlets */ + int length; /* Number of quadlets */ + + /* XXX Must be last in the struct! */ quadlet_t quadlets[0]; }; struct node_entry { struct list_head list; u64 guid; /* GUID of this node */ + u32 guid_vendor_id; /* Top 24bits of guid */ + const char *guid_vendor_oui; /* OUI name of guid vendor id */ + struct hpsb_host *host; /* Host this node is attached to */ nodeid_t nodeid; /* NodeID */ struct bus_options busopt; /* Bus Options */ @@ -134,10 +141,14 @@ /* The following is read from the config rom */ u32 vendor_id; - u32 capabilities; + const char *vendor_name; + const char *vendor_oui; + + u32 capabilities; + struct hpsb_tlabel_pool *tpool; struct list_head unit_directories; - const char *vendor_name; + /* XXX Must be last in the struct! */ quadlet_t quadlets[0]; }; @@ -155,7 +166,7 @@ /* Same as above, but use the nodeid to get an node entry. This is not * fool-proof by itself, since the nodeid can change. */ -struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid); +struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid); /* * If the entry refers to a local host, this function will return the pointer @@ -185,7 +196,7 @@ int extcode, quadlet_t *data, quadlet_t arg); -void init_ieee1394_nodemgr(int disable_hotplug); +void init_ieee1394_nodemgr(void); void cleanup_ieee1394_nodemgr(void); #endif /* _IEEE1394_NODEMGR_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/ohci1394.c linux.21rc1-ac2/drivers/ieee1394/ohci1394.c --- linux.21rc1/drivers/ieee1394/ohci1394.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/ohci1394.c 2003-04-21 17:30:49.000000000 +0100 @@ -26,18 +26,19 @@ * . Async Request Receive * . Async Response Transmit * . Iso Receive - * . Iso Transmit * . DMA mmap for iso receive * . Config ROM generation * * Things implemented, but still in test phase: + * . Iso Transmit * . Async Stream Packets Transmit (Receive done via Iso interface) - * + * * Things not implemented: * . DMA error recovery * * Known bugs: * . devctl BUS_RESET arg confusion (reset type or root holdoff?) + * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk */ /* @@ -76,12 +77,13 @@ * . Removed procfs support since it trashes random mem * . Config ROM generation * - * Dan Maas - * . New isochronous API (rawiso) + * Manfred Weihs + * . Reworked code for initiating bus resets + * (long, short, with or without hold-off) * * Nandu Santhi * . Added support for nVidia nForce2 onboard Firewire chipset - * + * */ #include @@ -162,13 +164,9 @@ printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args) static char version[] __devinitdata = - "$Rev: 866 $ Ben Collins "; + "$Rev$ Ben Collins "; /* Module Parameters */ -MODULE_PARM(attempt_root,"i"); -MODULE_PARM_DESC(attempt_root, "Attempt to make the host root (default = 0)."); -static int attempt_root = 0; - MODULE_PARM(phys_dma,"i"); MODULE_PARM_DESC(phys_dma, "Enable physical dma (default = 1)."); static int phys_dma = 1; @@ -407,7 +405,7 @@ for (i=0; inum_desc; i++) { u32 c; - + c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH; if (generate_irq) c |= DMA_CTL_IRQ; @@ -900,8 +898,6 @@ switch (cmd) { case RESET_BUS: - DBGMSG(ohci->id, "devctl: Bus reset requested%s", - attempt_root ? " and attempting to become root" : ""); switch (arg) { case SHORT_RESET: phy_reg = get_phy_reg(ohci, 5); @@ -910,13 +906,13 @@ break; case LONG_RESET: phy_reg = get_phy_reg(ohci, 1); - phy_reg |= 0x40 | (attempt_root ? 0x80 : 0); + phy_reg |= 0x40; set_phy_reg(ohci, 1, phy_reg); /* set IBR */ break; case SHORT_RESET_NO_FORCE_ROOT: phy_reg = get_phy_reg(ohci, 1); if (phy_reg & 0x80) { - phy_reg &= ~0x80 | (attempt_root ? 0x80 : 0); + phy_reg &= ~0x80; set_phy_reg(ohci, 1, phy_reg); /* clear RHB */ } @@ -927,7 +923,7 @@ case LONG_RESET_NO_FORCE_ROOT: phy_reg = get_phy_reg(ohci, 1); phy_reg &= ~0x80; - phy_reg |= 0x40 | (attempt_root ? 0x80 : 0); + phy_reg |= 0x40; set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */ break; case SHORT_RESET_FORCE_ROOT: @@ -949,8 +945,6 @@ default: retval = -1; } - - break; case GET_CYCLE_COUNTER: @@ -2340,11 +2334,8 @@ } } spin_unlock_irqrestore(&ohci->event_lock, flags); - if (!host->in_bus_reset) { - DBGMSG(ohci->id, "irq_handler: Bus reset requested%s", - (attempt_root) ? " and attempting to become root" - : ""); + DBGMSG(ohci->id, "irq_handler: Bus reset requested"); /* Subsystem call */ hpsb_bus_reset(ohci->host); @@ -3542,7 +3533,7 @@ static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = { { .class = PCI_CLASS_FIREWIRE_OHCI, - .class_mask = ~0, + .class_mask = PCI_ANY_ID, .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/oui2c.sh linux.21rc1-ac2/drivers/ieee1394/oui2c.sh --- linux.21rc1/drivers/ieee1394/oui2c.sh 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/oui2c.sh 2003-04-23 16:49:51.000000000 +0100 @@ -0,0 +1,23 @@ +#!/bin/sh + +cat < + +#ifdef CONFIG_IEEE1394_OUI_DB +struct oui_list_struct { + int oui; + char *name; +} oui_list[] = { +EOF + +while read oui name; do + echo " { 0x$oui, \"$name\" }," +done + +cat <phy_reg0 = -1; set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ break; + case SHORT_RESET_NO_FORCE_ROOT: + if (lynx->phyic.reg_1394a) { + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + if (phy_reg & 0x80) { + phy_reg &= ~0x80; + set_phy_reg(lynx, 1, phy_reg); /* clear RHB */ + } + + phy_reg = get_phy_reg(lynx, 5); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, no force_root) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ + break; + } else { + PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); + /* fall through to long bus reset */ + } + case LONG_RESET_NO_FORCE_ROOT: + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg &= ~0x80; + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, no force_root) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */ + break; + case SHORT_RESET_FORCE_ROOT: + if (lynx->phyic.reg_1394a) { + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + if (!(phy_reg & 0x80)) { + phy_reg |= 0x80; + set_phy_reg(lynx, 1, phy_reg); /* set RHB */ + } + + phy_reg = get_phy_reg(lynx, 5); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0x40; + + PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset, force_root set) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 5, phy_reg); /* set ISBR */ + break; + } else { + PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy"); + /* fall through to long bus reset */ + } + case LONG_RESET_FORCE_ROOT: + phy_reg = get_phy_reg(lynx, 1); + if (phy_reg == -1) { + PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed"); + retval = -1; + break; + } + phy_reg |= 0xc0; + + PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset, force_root set) on request"); + + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; + set_phy_reg(lynx, 1, phy_reg); /* set IBR and RHB */ + break; default: PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg); retval = -1; @@ -1817,6 +1907,7 @@ .get_rom = get_lynx_rom, .transmit_packet = lynx_transmit, .devctl = lynx_devctl, + .isoctl = NULL, }; MODULE_AUTHOR("Andreas E. Bombe "); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/raw1394.c linux.21rc1-ac2/drivers/ieee1394/raw1394.c --- linux.21rc1/drivers/ieee1394/raw1394.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/raw1394.c 2003-04-25 03:38:30.000000000 +0100 @@ -78,11 +78,11 @@ static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; static atomic_t internal_generation = ATOMIC_INIT(0); -static struct hpsb_highlevel *hl_handle; - static atomic_t iso_buffer_size; static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */ +static struct hpsb_highlevel raw1394_highlevel; + static int arm_read (struct hpsb_host *host, int nodeid, quadlet_t *buffer, u64 addr, unsigned int length, u16 flags); static int arm_write (struct hpsb_host *host, int nodeid, int destid, @@ -109,7 +109,6 @@ if (req != NULL) { memset(req, 0, sizeof(struct pending_request)); INIT_LIST_HEAD(&req->list); - INIT_TQUEUE(&req->tq, (void(*)(void*))queue_complete_cb, NULL); } return req; @@ -180,11 +179,11 @@ req->req.length = 0; } - if ((req->req.type == RAW1394_REQ_ASYNC_READ) || + if ((req->req.type == RAW1394_REQ_ASYNC_READ) || (req->req.type == RAW1394_REQ_ASYNC_WRITE) || (req->req.type == RAW1394_REQ_LOCK) || (req->req.type == RAW1394_REQ_LOCK64)) - free_tlabel(packet->host, packet->node_id, packet->tlabel); + hpsb_free_tlabel(packet); queue_complete_req(req); } @@ -195,7 +194,8 @@ struct host_info *hi; unsigned long flags; - hi = (struct host_info *)kmalloc(sizeof(struct host_info), SLAB_KERNEL); + hi = (struct host_info *)kmalloc(sizeof(struct host_info), GFP_KERNEL); + if (hi != NULL) { INIT_LIST_HEAD(&hi->list); hi->host = host; @@ -238,7 +238,7 @@ list_del(&hi->list); host_count--; /* - FIXME: adressranges should be removed + FIXME: address ranges should be removed and fileinfo states should be initialized (including setting generation to internal-generation ...) @@ -281,8 +281,8 @@ req->req.misc = (host->node_id << 16) | host->node_count; if (fi->protocol_version > 3) { - req->req.misc |= ((host->irm_id - & NODE_MASK) << 8); + req->req.misc |= (NODEID_TO_NODE(host->irm_id) + << 8); } queue_complete_req(req); @@ -558,7 +558,7 @@ lh = lh->next; } hi = list_entry(lh, struct host_info, list); - hpsb_ref_host(hi->host); + hpsb_ref_host(hi->host); // XXX Need to handle failure case list_add_tail(&fi->list, &hi->file_info_list); fi->host = hi->host; fi->state = connected; @@ -571,8 +571,7 @@ req->req.misc = (fi->host->node_id << 16) | fi->host->node_count; if (fi->protocol_version > 3) { - req->req.misc |= - (fi->host->irm_id & NODE_MASK) << 8; + req->req.misc |= NODEID_TO_NODE(fi->host->irm_id) << 8; } } else { req->req.error = RAW1394_ERROR_INVALID_ARG; @@ -603,17 +602,20 @@ if (fi->listen_channels & (1ULL << channel)) { req->req.error = RAW1394_ERROR_ALREADY; } else { - fi->listen_channels |= 1ULL << channel; - hpsb_listen_channel(hl_handle, fi->host, channel); - fi->iso_buffer = int2ptr(req->req.recvb); - fi->iso_buffer_length = req->req.length; + if(hpsb_listen_channel(&raw1394_highlevel, fi->host, channel)) { + req->req.error = RAW1394_ERROR_ALREADY; + } else { + fi->listen_channels |= 1ULL << channel; + fi->iso_buffer = int2ptr(req->req.recvb); + fi->iso_buffer_length = req->req.length; + } } } else { /* deallocate channel (one's complement neg) req.misc */ channel = ~channel; if (fi->listen_channels & (1ULL << channel)) { - hpsb_unlisten_channel(hl_handle, fi->host, channel); + hpsb_unlisten_channel(&raw1394_highlevel, fi->host, channel); fi->listen_channels &= ~(1ULL << channel); } else { req->req.error = RAW1394_ERROR_INVALID_ARG; @@ -654,47 +656,39 @@ switch (req->req.type) { case RAW1394_REQ_ASYNC_READ: - if (req->req.length == 4) { - DBGMSG("quadlet_read_request called"); - packet = hpsb_make_readqpacket(fi->host, node, addr); - if (!packet) return -ENOMEM; + DBGMSG("read_request called"); + packet = hpsb_make_readpacket(fi->host, node, addr, req->req.length); - req->data = &packet->header[3]; - } else { - DBGMSG("block_read_request called"); - packet = hpsb_make_readbpacket(fi->host, node, addr, - req->req.length); - if (!packet) return -ENOMEM; + if (!packet) + return -ENOMEM; - req->data = packet->data; - } + if (req->req.length == 4) + req->data = &packet->header[3]; + else + req->data = packet->data; + break; - case RAW1394_REQ_ASYNC_WRITE: - if (req->req.length == 4) { - quadlet_t x; - - DBGMSG("quadlet_write_request called"); - if (copy_from_user(&x, int2ptr(req->req.sendb), 4)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - } + case RAW1394_REQ_ASYNC_WRITE: + DBGMSG("write_request called"); - packet = hpsb_make_writeqpacket(fi->host, node, addr, - x); - if (!packet) return -ENOMEM; - } else { - DBGMSG("block_write_request called"); - packet = hpsb_make_writebpacket(fi->host, node, addr, - req->req.length); - if (!packet) return -ENOMEM; - - if (copy_from_user(packet->data, int2ptr(req->req.sendb), - req->req.length)) { - req->req.error = RAW1394_ERROR_MEMFAULT; - } - } - req->req.length = 0; - break; + packet = hpsb_make_writepacket(fi->host, node, addr, NULL, + req->req.length); + if (!packet) + return -ENOMEM; + + if (req->req.length == 4) { + if (copy_from_user(&packet->header[3], int2ptr(req->req.sendb), + req->req.length)) + req->req.error = RAW1394_ERROR_MEMFAULT; + } else { + if (copy_from_user(packet->data, int2ptr(req->req.sendb), + req->req.length)) + req->req.error = RAW1394_ERROR_MEMFAULT; + } + + req->req.length = 0; + break; case RAW1394_REQ_LOCK: DBGMSG("lock_request called"); @@ -712,7 +706,7 @@ } packet = hpsb_make_lockpacket(fi->host, node, addr, - req->req.misc); + req->req.misc, NULL, 0); if (!packet) return -ENOMEM; if (copy_from_user(packet->data, int2ptr(req->req.sendb), @@ -740,7 +734,7 @@ } } packet = hpsb_make_lock64packet(fi->host, node, addr, - req->req.misc); + req->req.misc, NULL, 0); if (!packet) return -ENOMEM; if (copy_from_user(packet->data, int2ptr(req->req.sendb), @@ -765,8 +759,7 @@ return sizeof(struct raw1394_request); } - req->tq.data = req; - hpsb_add_packet_complete_task(packet, &req->tq); + hpsb_set_packet_complete_task(packet, (void(*)(void*))queue_complete_cb, req); spin_lock_irq(&fi->reqlists_lock); list_add_tail(&req->list, &fi->req_pending); @@ -777,7 +770,7 @@ if (!hpsb_send_packet(packet)) { req->req.error = RAW1394_ERROR_SEND_ERROR; req->req.length = 0; - free_tlabel(packet->host, packet->node_id, packet->tlabel); + hpsb_free_tlabel(packet); queue_complete_req(req); } return sizeof(struct raw1394_request); @@ -788,15 +781,14 @@ { struct hpsb_packet *packet; - packet = alloc_hpsb_packet(req->req.length); - if (!packet) return -ENOMEM; - req->packet = packet; + packet = hpsb_make_isopacket(fi->host, req->req.length, channel & 0x3f, + (req->req.misc >> 16) & 0x3, req->req.misc & 0xf); + if (!packet) + return -ENOMEM; - fill_iso_packet(packet, req->req.length, channel & 0x3f, - (req->req.misc >> 16) & 0x3, req->req.misc & 0xf); - packet->type = hpsb_iso; packet->speed_code = req->req.address & 0x3; - packet->host = fi->host; + + req->packet = packet; if (copy_from_user(packet->data, int2ptr(req->req.sendb), req->req.length)) { @@ -806,16 +798,15 @@ return sizeof(struct raw1394_request); } - PREPARE_TQUEUE(&req->tq, (void (*)(void*))queue_complete_req, req); req->req.length = 0; - hpsb_add_packet_complete_task(packet, &req->tq); + hpsb_set_packet_complete_task(packet, (void (*)(void*))queue_complete_req, req); spin_lock_irq(&fi->reqlists_lock); list_add_tail(&req->list, &fi->req_pending); spin_unlock_irq(&fi->reqlists_lock); /* Update the generation of the packet just before sending. */ - packet->generation = get_hpsb_generation(fi->host); + packet->generation = req->req.generation; if (!hpsb_send_packet(packet)) { req->req.error = RAW1394_ERROR_SEND_ERROR; @@ -869,16 +860,15 @@ packet->header_size=header_length; packet->data_size=req->req.length-header_length; - PREPARE_TQUEUE(&req->tq, (void (*)(void*))queue_complete_req, req); req->req.length = 0; - hpsb_add_packet_complete_task(packet, &req->tq); + hpsb_set_packet_complete_task(packet, (void(*)(void*))queue_complete_cb, req); spin_lock_irq(&fi->reqlists_lock); list_add_tail(&req->list, &fi->req_pending); spin_unlock_irq(&fi->reqlists_lock); /* Update the generation of the packet just before sending. */ - packet->generation = get_hpsb_generation(fi->host); + packet->generation = req->req.generation; if (!hpsb_send_packet(packet)) { req->req.error = RAW1394_ERROR_SEND_ERROR; @@ -1690,7 +1680,7 @@ spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); } - retval = hpsb_register_addrspace(hl_handle, &arm_ops, req->req.address, + retval = hpsb_register_addrspace(&raw1394_highlevel, &arm_ops, req->req.address, req->req.address + req->req.length); if (retval) { /* INSERT ENTRY */ @@ -1777,7 +1767,7 @@ spin_unlock_irqrestore(&host_info_lock, flags); return sizeof(struct raw1394_request); } - retval = hpsb_unregister_addrspace(hl_handle, addr->start); + retval = hpsb_unregister_addrspace(&raw1394_highlevel, addr->start); if (!retval) { printk(KERN_ERR "raw1394: arm_Unregister failed -> EINVAL\n"); spin_unlock_irqrestore(&host_info_lock, flags); @@ -1818,8 +1808,7 @@ if (!packet) return -ENOMEM; req->req.length=0; req->packet=packet; - req->tq.data=req; - hpsb_add_packet_complete_task(packet, &req->tq); + hpsb_set_packet_complete_task(packet, (void(*)(void*))queue_complete_cb, req); spin_lock_irq(&fi->reqlists_lock); list_add_tail(&req->list, &fi->req_pending); spin_unlock_irq(&fi->reqlists_lock); @@ -2391,7 +2380,7 @@ for (i = 0; i < 64; i++) { if (fi->listen_channels & (1ULL << i)) { - hpsb_unlisten_channel(hl_handle, fi->host, i); + hpsb_unlisten_channel(&raw1394_highlevel, fi->host, i); } } @@ -2436,7 +2425,7 @@ } if (!another_host) { DBGMSG("raw1394_release: call hpsb_arm_unregister"); - retval = hpsb_unregister_addrspace(hl_handle, addr->start); + retval = hpsb_unregister_addrspace(&raw1394_highlevel, addr->start); if (!retval) { ++fail; printk(KERN_ERR "raw1394_release arm_Unregister failed\n"); @@ -2485,37 +2474,38 @@ return 0; } + /*** HOTPLUG STUFF **********************************************************/ /* * Export information about protocols/devices supported by this driver. */ static struct ieee1394_device_id raw1394_id_table[] = { { - .match_flags =IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = AVC_SW_VERSION_ENTRY & 0xffffff + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = AVC_SW_VERSION_ENTRY & 0xffffff }, { - .match_flags =IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = CAMERA_SW_VERSION_ENTRY & 0xffffff }, { } }; +MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); + static struct hpsb_protocol_driver raw1394_driver = { .name = "raw1394 Driver", .id_table = raw1394_id_table, }; -MODULE_DEVICE_TABLE(ieee1394, raw1394_id_table); - /******************************************************************************/ -static struct hpsb_highlevel_ops hl_ops = { + +static struct hpsb_highlevel raw1394_highlevel = { + .name = RAW1394_DEVICE_NAME, .add_host = add_host, .remove_host = remove_host, .host_reset = host_reset, @@ -2536,11 +2526,7 @@ static int __init init_raw1394(void) { - hl_handle = hpsb_register_highlevel(RAW1394_DEVICE_NAME, &hl_ops); - if (hl_handle == NULL) { - HPSB_ERR("raw1394 failed to register with ieee1394 highlevel"); - return -ENOMEM; - } + hpsb_register_highlevel(&raw1394_highlevel); devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE, @@ -2553,7 +2539,7 @@ THIS_MODULE, &file_ops)) { HPSB_ERR("raw1394 failed to register minor device block"); devfs_unregister(devfs_handle); - hpsb_unregister_highlevel(hl_handle); + hpsb_unregister_highlevel(&raw1394_highlevel); return -EBUSY; } @@ -2569,7 +2555,7 @@ hpsb_unregister_protocol(&raw1394_driver); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394); devfs_unregister(devfs_handle); - hpsb_unregister_highlevel(hl_handle); + hpsb_unregister_highlevel(&raw1394_highlevel); } module_init(init_raw1394); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/raw1394-private.h linux.21rc1-ac2/drivers/ieee1394/raw1394-private.h --- linux.21rc1/drivers/ieee1394/raw1394-private.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/raw1394-private.h 2003-04-21 17:30:49.000000000 +0100 @@ -63,7 +63,6 @@ struct list_head list; struct file_info *file_info; struct hpsb_packet *packet; - struct tq_struct tq; struct iso_block_store *ibs; quadlet_t *data; int free_data; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/sbp2.c linux.21rc1-ac2/drivers/ieee1394/sbp2.c --- linux.21rc1/drivers/ieee1394/sbp2.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/sbp2.c 2003-04-25 03:38:30.000000000 +0100 @@ -84,8 +84,6 @@ * sbp2_serialize_io - Serialize all I/O coming down from the scsi drivers * (0 = deserialized, 1 = serialized, default = 0) * sbp2_max_sectors, - Change max sectors per I/O supported (default = 255) - * sbp2_max_outstanding_cmds - Change max outstanding concurrent commands (default = 64) - * sbp2_max_cmds_per_lun - Change max concurrent commands per sbp2 device (default = 1) * sbp2_exclusive_login - Set to zero if you'd like to allow multiple hosts the ability * to log in at the same time. Sbp2 device must support this, * and you must know what you're doing (default = 1) @@ -353,7 +351,7 @@ #include "sbp2.h" static char version[] __devinitdata = - "$Rev: 878 $ James Goodwin "; + "$Rev$ James Goodwin "; /* * Module load parameter definitions @@ -366,15 +364,10 @@ * NOTE: On certain OHCI parts I have seen short packets on async transmit * (probably due to PCI latency/throughput issues with the part). You can * bump down the speed if you are running into problems. - * - * Valid values: - * sbp2_max_speed = 2 (default: max speed 400mb) - * sbp2_max_speed = 1 (max speed 200mb) - * sbp2_max_speed = 0 (max speed 100mb) */ MODULE_PARM(sbp2_max_speed,"i"); -MODULE_PARM_DESC(sbp2_max_speed, "Force max speed (2 = 400mb default, 1 = 200mb, 0 = 100mb)"); -static int sbp2_max_speed = SPEED_400; +MODULE_PARM_DESC(sbp2_max_speed, "Force max speed (3 = 800mb, 2 = 400mb default, 1 = 200mb, 0 = 100mb)"); +static int sbp2_max_speed = SPEED_MAX; /* * Set sbp2_serialize_io to 1 if you'd like only one scsi command sent @@ -398,26 +391,6 @@ static int sbp2_max_sectors = SBP2_MAX_SECTORS; /* - * Adjust sbp2_max_outstanding_cmds to tune performance if you have many - * sbp2 devices attached (or if you need to do some debugging). - */ -MODULE_PARM(sbp2_max_outstanding_cmds,"i"); -MODULE_PARM_DESC(sbp2_max_outstanding_cmds, "Change max outstanding concurrent commands (default = 64)"); -static int sbp2_max_outstanding_cmds = SBP2SCSI_MAX_OUTSTANDING_CMDS; - -/* - * Adjust sbp2_max_cmds_per_lun to tune performance. Enabling more than - * one concurrent/linked command per sbp2 device may allow some - * performance gains, but some older sbp2 devices have firmware bugs - * resulting in problems when linking commands... so, enable this with - * care. I can note that the Oxsemi OXFW911 sbp2 chipset works very well - * with large numbers of concurrent/linked commands. =) - */ -MODULE_PARM(sbp2_max_cmds_per_lun,"i"); -MODULE_PARM_DESC(sbp2_max_cmds_per_lun, "Change max concurrent commands per sbp2 device (default = 1)"); -static int sbp2_max_cmds_per_lun = SBP2SCSI_MAX_CMDS_PER_LUN; - -/* * Exclusive login to sbp2 device? In most cases, the sbp2 driver should * do an exclusive login, as it's generally unsafe to have two hosts * talking to a single sbp2 device at the same time (filesystem coherency, @@ -515,21 +488,22 @@ #define SBP2_ERR(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) + /* * Globals */ -static spinlock_t sbp2_host_info_lock = SPIN_LOCK_UNLOCKED; +static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id, + u32 status); +static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id, + u32 scsi_status, Scsi_Cmnd *SCpnt, + void (*done)(Scsi_Cmnd *)); + static Scsi_Host_Template scsi_driver_template; -static u8 sbp2_speedto_maxrec[] = { 0x7, 0x8, 0x9 }; - -static LIST_HEAD(sbp2_host_info_list); - -static struct hpsb_highlevel *sbp2_hl_handle = NULL; - -static struct hpsb_highlevel_ops sbp2_hl_ops = { +static struct hpsb_highlevel sbp2_highlevel = { + .name = SBP2_DEVICE_NAME, .add_host = sbp2_add_host, .remove_host = sbp2_remove_host, }; @@ -651,90 +625,11 @@ return ((i > 0) ? 0:1); } -/* - * This function is called to initially create a packet pool for use in - * sbp2 I/O requests. This packet pool is used when sending out sbp2 - * command and agent reset requests, and allows us to remove all - * kmallocs/kfrees from the critical I/O paths. - */ -static int sbp2util_create_request_packet_pool(struct sbp2scsi_host_info *hi) +/* Free's an allocated packet */ +static void sbp2_free_packet(struct hpsb_packet *packet) { - struct hpsb_packet *packet; - int i; - - hi->request_packet = kmalloc(sizeof(struct sbp2_request_packet) * SBP2_MAX_REQUEST_PACKETS, - GFP_KERNEL); - - if (!hi->request_packet) { - SBP2_ERR("sbp2util_create_request_packet_pool - packet allocation failed!"); - return(-ENOMEM); - } - memset(hi->request_packet, 0, sizeof(struct sbp2_request_packet) * SBP2_MAX_REQUEST_PACKETS); - - /* - * Create a pool of request packets. Just take the max supported - * concurrent commands and multiply by two to be safe... - */ - for (i=0; irequest_packet[i].list); - hi->request_packet[i].packet = packet; - list_add_tail(&hi->request_packet[i].list, &hi->sbp2_req_free); - - } - - return(0); -} - -/* - * This function is called to remove the packet pool. It is called when - * the sbp2 driver is unloaded. - */ -static void sbp2util_remove_request_packet_pool(struct sbp2scsi_host_info *hi) -{ - struct list_head *lh; - struct sbp2_request_packet *request_packet; - unsigned long flags; - - /* - * Go through free list releasing packets - */ - spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags); - while (!list_empty(&hi->sbp2_req_free)) { - - lh = hi->sbp2_req_free.next; - list_del(lh); - - request_packet = list_entry(lh, struct sbp2_request_packet, list); - - /* - * Free the hpsb packets that we allocated for the pool - */ - if (request_packet) { - free_hpsb_packet(request_packet->packet); - } - - } - kfree(hi->request_packet); - spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags); - - return; + hpsb_free_tlabel(packet); + free_hpsb_packet(packet); } /* @@ -744,107 +639,44 @@ * out a free request packet and re-initialize values in it. I'm sure this * can still stand some more optimization. */ -static struct sbp2_request_packet * -sbp2util_allocate_write_request_packet(struct sbp2scsi_host_info *hi, - struct node_entry *ne, u64 addr, - size_t data_size, - quadlet_t data) { - struct list_head *lh; - struct sbp2_request_packet *request_packet = NULL; +static struct hpsb_packet * +sbp2util_allocate_write_packet(struct sbp2scsi_host_info *hi, + struct node_entry *ne, u64 addr, + size_t data_size, + quadlet_t *data) +{ struct hpsb_packet *packet; - unsigned long flags; - spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags); - if (!list_empty(&hi->sbp2_req_free)) { + packet = hpsb_make_writepacket(hi->host, ne->nodeid, + addr, data, data_size); - /* - * Pull out a free request packet - */ - lh = hi->sbp2_req_free.next; - list_del(lh); + if (!packet) + return NULL; - request_packet = list_entry(lh, struct sbp2_request_packet, list); - packet = request_packet->packet; + hpsb_set_packet_complete_task(packet, (void (*)(void*))sbp2_free_packet, + packet); - /* - * Initialize the packet (this is really initialization - * the core 1394 stack should do, but I'm doing it myself - * to avoid the overhead). - */ - packet->data_size = data_size; - INIT_LIST_HEAD(&packet->list); - sema_init(&packet->state_change, 0); - packet->state = hpsb_unused; - packet->data_be = 1; - - hpsb_node_fill_packet(ne, packet); - - packet->tlabel = get_tlabel(hi->host, packet->node_id, 0); - - if (!data_size) { - fill_async_writequad(packet, addr, data); - } else { - fill_async_writeblock(packet, addr, data_size); - } - - /* - * Set up a task queue completion routine, which returns - * the packet to the free list and releases the tlabel. - */ - PREPARE_TQUEUE(&request_packet->tq, - (void (*)(void*))sbp2util_free_request_packet, - request_packet); - request_packet->hi_context = hi; - hpsb_add_packet_complete_task(packet, &request_packet->tq); + hpsb_node_fill_packet(ne, packet); - /* - * Now, put the packet on the in-use list. - */ - list_add_tail(&request_packet->list, &hi->sbp2_req_inuse); - - } else { - SBP2_ERR("sbp2util_allocate_request_packet - no packets available!"); - } - spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags); - - return(request_packet); + return packet; } -/* - * This function is called to return a packet to our packet pool. It is - * also called as a completion routine when a request packet is completed. - */ -static void sbp2util_free_request_packet(struct sbp2_request_packet *request_packet) -{ - unsigned long flags; - struct sbp2scsi_host_info *hi = request_packet->hi_context; - - /* - * Free the tlabel, and return the packet to the free pool. - */ - spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags); - free_tlabel(hi->host, LOCAL_BUS | request_packet->packet->node_id, - request_packet->packet->tlabel); - list_del(&request_packet->list); - list_add_tail(&request_packet->list, &hi->sbp2_req_free); - spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags); - - return; -} /* * This function is called to create a pool of command orbs used for * command processing. It is called when a new sbp2 device is detected. */ -static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id, - struct sbp2scsi_host_info *hi) +static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; int i; - unsigned long flags; + unsigned long flags, orbs; struct sbp2_command_info *command; + + orbs = sbp2_serialize_io ? 2 : SBP2_MAX_COMMAND_ORBS; spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); - for (i = 0; i < scsi_id->sbp2_total_command_orbs; i++) { + for (i = 0; i < orbs; i++) { command = (struct sbp2_command_info *) kmalloc(sizeof(struct sbp2_command_info), GFP_ATOMIC); if (!command) { @@ -872,9 +704,9 @@ /* * This function is called to delete a pool of command orbs. */ -static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id, - struct sbp2scsi_host_info *hi) +static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id) { + struct hpsb_host *host = scsi_id->hi->host; struct list_head *lh, *next; struct sbp2_command_info *command; unsigned long flags; @@ -885,11 +717,11 @@ command = list_entry(lh, struct sbp2_command_info, list); /* Release our generic DMA's */ - pci_unmap_single(hi->host->pdev, command->command_orb_dma, + pci_unmap_single(host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_BIDIRECTIONAL); SBP2_DMA_FREE("single command orb DMA"); - pci_unmap_single(hi->host->pdev, command->sge_dma, + pci_unmap_single(host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); SBP2_DMA_FREE("scatter_gather_element"); @@ -959,8 +791,7 @@ static struct sbp2_command_info *sbp2util_allocate_command_orb( struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *Current_SCpnt, - void (*Current_done)(Scsi_Cmnd *), - struct sbp2scsi_host_info *hi) + void (*Current_done)(Scsi_Cmnd *)) { struct list_head *lh; struct sbp2_command_info *command = NULL; @@ -985,10 +816,10 @@ static void sbp2util_free_command_dma(struct sbp2_command_info *command) { struct sbp2scsi_host_info *hi; - - hi = (struct sbp2scsi_host_info *) command->Current_SCpnt->host->hostdata[0]; - if (hi == NULL) { + hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, + (unsigned long)command->Current_SCpnt->device->host->hostt); + if (!hi) { printk(KERN_ERR "%s: hi == NULL\n", __FUNCTION__); return; } @@ -1039,53 +870,17 @@ * This function is called at SCSI init in order to register our driver * with the IEEE-1394 stack. */ -int sbp2_init(void) +static int sbp2scsi_detect(Scsi_Host_Template *tpnt) { - SBP2_DEBUG("sbp2_init"); - - /* - * Register our high level driver with 1394 stack - */ - sbp2_hl_handle = hpsb_register_highlevel(SBP2_DEVICE_NAME, &sbp2_hl_ops); - - if (sbp2_hl_handle == NULL) { - SBP2_ERR("sbp2 failed to register with ieee1394 highlevel"); - return(-ENOMEM); - } - - /* - * Register our sbp2 status address space... - */ - hpsb_register_addrspace(sbp2_hl_handle, &sbp2_ops, SBP2_STATUS_FIFO_ADDRESS, - SBP2_STATUS_FIFO_ADDRESS + - SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2SCSI_MAX_SCSI_IDS+1)); - - /* - * Handle data movement if physical dma is not enabled/supported on host controller - */ -#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA - hpsb_register_addrspace(sbp2_hl_handle, &sbp2_physdma_ops, 0x0ULL, 0xfffffffcULL); -#endif - - hpsb_register_protocol(&sbp2_driver); - - return 0; -} + SBP2_DEBUG("sbp2scsi_detect"); -/* - * This function is called from cleanup module, or during shut-down, in - * order to unregister our driver. - */ -void sbp2_cleanup(void) -{ - SBP2_DEBUG("sbp2_cleanup"); + /* Register our host with the SCSI stack. */ + if (!scsi_register(tpnt, 0)) + return 0; - hpsb_unregister_protocol(&sbp2_driver); + tpnt->present = 1; - if (sbp2_hl_handle) { - hpsb_unregister_highlevel(sbp2_hl_handle); - sbp2_hl_handle = NULL; - } + return tpnt->present; } static int sbp2_probe(struct unit_directory *ud) @@ -1093,22 +888,20 @@ struct sbp2scsi_host_info *hi; SBP2_DEBUG("sbp2_probe"); - hi = sbp2_find_host_info(ud->ne->host); + hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); return sbp2_start_device(hi, ud); } static void sbp2_disconnect(struct unit_directory *ud) { - struct sbp2scsi_host_info *hi; struct scsi_id_instance_data *scsi_id = ud->driver_data; SBP2_DEBUG("sbp2_disconnect"); - hi = sbp2_find_host_info(ud->ne->host); - if (hi != NULL) { - sbp2_logout_device(hi, scsi_id); - sbp2_remove_device(hi, scsi_id); + if (scsi_id) { + sbp2_logout_device(scsi_id); + sbp2_remove_device(scsi_id); } } @@ -1119,56 +912,52 @@ unsigned long flags; SBP2_DEBUG("sbp2_update"); - hi = sbp2_find_host_info(ud->ne->host); + hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host); - if (sbp2_reconnect_device(hi, scsi_id)) { + if (sbp2_reconnect_device(scsi_id)) { /* * Ok, reconnect has failed. Perhaps we didn't * reconnect fast enough. Try doing a regular login. */ - if (sbp2_login_device(hi, scsi_id)) { - + if (sbp2_login_device(scsi_id)) { /* Login failed too, just remove the device. */ SBP2_ERR("sbp2_reconnect_device failed!"); - sbp2_remove_device(hi, scsi_id); + sbp2_remove_device(scsi_id); hpsb_release_unit_directory(ud); return; } } /* Set max retries to something large on the device. */ - sbp2_set_busy_timeout(hi, scsi_id); + sbp2_set_busy_timeout(scsi_id); /* Do a SBP-2 fetch agent reset. */ - sbp2_agent_reset(hi, scsi_id, 0); + sbp2_agent_reset(scsi_id, 0); /* Get the max speed and packet size that we can use. */ - sbp2_max_speed_and_size(hi, scsi_id); + sbp2_max_speed_and_size(scsi_id); /* Complete any pending commands with busy (so they get * retried) and remove them from our queue */ spin_lock_irqsave(&hi->sbp2_command_lock, flags); - sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY); + sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); spin_unlock_irqrestore(&hi->sbp2_command_lock, flags); } /* - * This function is called after registering our operations in sbp2_init. * We go ahead and allocate some memory for our host info structure, and * init some structures. */ static void sbp2_add_host(struct hpsb_host *host) { struct sbp2scsi_host_info *hi; - unsigned long flags; SBP2_DEBUG("sbp2_add_host"); /* Allocate some memory for our host info structure */ - hi = (struct sbp2scsi_host_info *)kmalloc(sizeof(struct sbp2scsi_host_info), - GFP_KERNEL); + hi = hpsb_create_hostinfo(&sbp2_highlevel, host, sizeof(*hi)); if (hi == NULL) { SBP2_ERR("out of memory in sbp2_add_host"); @@ -1176,70 +965,36 @@ } /* Initialize some host stuff */ - memset(hi, 0, sizeof(struct sbp2scsi_host_info)); - INIT_LIST_HEAD(&hi->list); - INIT_LIST_HEAD(&hi->sbp2_req_inuse); - INIT_LIST_HEAD(&hi->sbp2_req_free); hi->host = host; hi->sbp2_command_lock = SPIN_LOCK_UNLOCKED; - hi->sbp2_request_packet_lock = SPIN_LOCK_UNLOCKED; - /* Create our request packet pool (pool of packets for use in I/O) */ - if (sbp2util_create_request_packet_pool(hi)) { - SBP2_ERR("sbp2util_create_request_packet_pool failed!"); + memcpy(&hi->sht, &scsi_driver_template, sizeof hi->sht); + sprintf(hi->proc_name, "%s_%d", SBP2_DEVICE_NAME, host->id); + hi->sht.proc_name = hi->proc_name; + hpsb_set_hostinfo_key(&sbp2_highlevel, host, (unsigned long)&hi->sht); + + if (SCSI_REGISTER_HOST(&hi->sht)) { + SBP2_ERR("Failed to register scsi template for ieee1394 host"); + hpsb_destroy_hostinfo(&sbp2_highlevel, host); + return; + } + + for (hi->scsi_host = scsi_hostlist; hi->scsi_host; hi->scsi_host = hi->scsi_host->next) + if (hi->scsi_host->hostt == &hi->sht) + break; + + if (!hi->scsi_host) { + SBP2_ERR("Failed to register scsi host for ieee1394 host"); + SCSI_UNREGISTER_HOST(&hi->sht); + hpsb_destroy_hostinfo(&sbp2_highlevel, host); return; } - spin_lock_irqsave(&sbp2_host_info_lock, flags); - list_add_tail(&hi->list, &sbp2_host_info_list); - spin_unlock_irqrestore(&sbp2_host_info_lock, flags); - - /* Register our host with the SCSI stack. */ - hi->scsi_host = scsi_register (&scsi_driver_template, sizeof(void *)); - if (hi->scsi_host) { - hi->scsi_host->hostdata[0] = (unsigned long)hi; - hi->scsi_host->max_id = SBP2SCSI_MAX_SCSI_IDS; - } - scsi_driver_template.present++; + hi->scsi_host->max_id = SBP2SCSI_MAX_SCSI_IDS; return; } -/* - * This fuction returns a host info structure from the host structure, in - * case we have multiple hosts. - */ -static struct sbp2scsi_host_info *sbp2_find_host_info(struct hpsb_host *host) -{ - struct list_head *lh; - struct sbp2scsi_host_info *hi; - - list_for_each (lh, &sbp2_host_info_list) { - hi = list_entry(lh, struct sbp2scsi_host_info, list); - if (hi->host == host) - return hi; - } - - return NULL; -} - -/* - * This function returns a host info structure for a given Scsi_Host - * struct. - */ -static struct sbp2scsi_host_info *sbp2_find_host_info_scsi(struct Scsi_Host *host) -{ - struct list_head *lh; - struct sbp2scsi_host_info *hi; - - list_for_each (lh, &sbp2_host_info_list) { - hi = list_entry(lh, struct sbp2scsi_host_info, list); - if (hi->scsi_host == host) - return hi; - } - - return NULL; -} /* * This function is called when a host is removed. @@ -1247,22 +1002,15 @@ static void sbp2_remove_host(struct hpsb_host *host) { struct sbp2scsi_host_info *hi; - unsigned long flags; SBP2_DEBUG("sbp2_remove_host"); - spin_lock_irqsave(&sbp2_host_info_lock, flags); + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); - hi = sbp2_find_host_info(host); - if (hi != NULL) { - sbp2util_remove_request_packet_pool(hi); - list_del(&hi->list); - kfree(hi); - } + if (hi) + SCSI_UNREGISTER_HOST(&hi->sht); else SBP2_ERR("attempt to remove unknown host %p", host); - - spin_unlock_irqrestore(&sbp2_host_info_lock, flags); } /* @@ -1288,6 +1036,8 @@ goto alloc_fail_first; memset(scsi_id, 0, sizeof(struct scsi_id_instance_data)); + scsi_id->hi = hi; + /* Login FIFO DMA */ scsi_id->login_response = pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_response), @@ -1296,22 +1046,6 @@ goto alloc_fail; SBP2_DMA_ALLOC("consistent DMA region for login FIFO"); - /* Reconnect ORB DMA */ - scsi_id->reconnect_orb = - pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_reconnect_orb), - &scsi_id->reconnect_orb_dma); - if (!scsi_id->reconnect_orb) - goto alloc_fail; - SBP2_DMA_ALLOC("consistent DMA region for reconnect ORB"); - - /* Logout ORB DMA */ - scsi_id->logout_orb = - pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_logout_orb), - &scsi_id->logout_orb_dma); - if (!scsi_id->logout_orb) - goto alloc_fail; - SBP2_DMA_ALLOC("consistent DMA region for logout ORB"); - /* Query logins ORB DMA */ scsi_id->query_logins_orb = pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_query_logins_orb), @@ -1328,28 +1062,44 @@ goto alloc_fail; SBP2_DMA_ALLOC("consistent DMA region for query logins response"); + /* Reconnect ORB DMA */ + scsi_id->reconnect_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_reconnect_orb), + &scsi_id->reconnect_orb_dma); + if (!scsi_id->reconnect_orb) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for reconnect ORB"); + + /* Logout ORB DMA */ + scsi_id->logout_orb = + pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_logout_orb), + &scsi_id->logout_orb_dma); + if (!scsi_id->logout_orb) + goto alloc_fail; + SBP2_DMA_ALLOC("consistent DMA region for logout ORB"); + /* Login ORB DMA */ scsi_id->login_orb = pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_orb), &scsi_id->login_orb_dma); - if (scsi_id->login_orb == NULL) { + if (!scsi_id->login_orb) { alloc_fail: if (scsi_id->query_logins_response) { pci_free_consistent(hi->host->pdev, - sizeof(struct sbp2_query_logins_response), - scsi_id->query_logins_response, - scsi_id->query_logins_response_dma); + sizeof(struct sbp2_query_logins_response), + scsi_id->query_logins_response, + scsi_id->query_logins_response_dma); SBP2_DMA_FREE("query logins response DMA"); } if (scsi_id->query_logins_orb) { pci_free_consistent(hi->host->pdev, - sizeof(struct sbp2_query_logins_orb), - scsi_id->query_logins_orb, - scsi_id->query_logins_orb_dma); + sizeof(struct sbp2_query_logins_orb), + scsi_id->query_logins_orb, + scsi_id->query_logins_orb_dma); SBP2_DMA_FREE("query logins ORB DMA"); } - + if (scsi_id->logout_orb) { pci_free_consistent(hi->host->pdev, sizeof(struct sbp2_logout_orb), @@ -1377,7 +1127,8 @@ kfree(scsi_id); alloc_fail_first: SBP2_ERR ("Could not allocate memory for scsi_id"); - return(-ENOMEM); + + return -ENOMEM; } SBP2_DMA_ALLOC("consistent DMA region for login ORB"); @@ -1387,7 +1138,7 @@ scsi_id->ne = ne; scsi_id->ud = ud; scsi_id->speed_code = SPEED_100; - scsi_id->max_payload_size = sbp2_speedto_maxrec[SPEED_100]; + scsi_id->max_payload_size = hpsb_speedto_maxrec[SPEED_100]; ud->driver_data = scsi_id; atomic_set(&scsi_id->sbp2_login_complete, 0); @@ -1398,7 +1149,6 @@ INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse); INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed); scsi_id->sbp2_command_orb_lock = SPIN_LOCK_UNLOCKED; - scsi_id->sbp2_total_command_orbs = 0; /* * Make sure that we've gotten ahold of the sbp2 management agent @@ -1407,19 +1157,10 @@ */ sbp2_parse_unit_directory(scsi_id); - scsi_id->sbp2_total_command_orbs = SBP2_MAX_COMMAND_ORBS; - - /* - * Knock the total command orbs down if we are serializing I/O - */ - if (sbp2_serialize_io) { - scsi_id->sbp2_total_command_orbs = 2; /* one extra for good measure */ - } - /* * Find an empty spot to stick our scsi id instance data. */ - for (i = 0; i < SBP2SCSI_MAX_SCSI_IDS; i++) { + for (i = 0; i < hi->scsi_host->max_id; i++) { if (!hi->scsi_id[i]) { hi->scsi_id[i] = scsi_id; scsi_id->id = i; @@ -1431,46 +1172,44 @@ /* * Create our command orb pool */ - if (sbp2util_create_command_orb_pool(scsi_id, hi)) { + if (sbp2util_create_command_orb_pool(scsi_id)) { SBP2_ERR("sbp2util_create_command_orb_pool failed!"); - sbp2_remove_device(hi, scsi_id); + sbp2_remove_device(scsi_id); return -ENOMEM; } /* * Make sure we are not out of space */ - if (i == SBP2SCSI_MAX_SCSI_IDS) { + if (i == hi->scsi_host->max_id) { SBP2_ERR("No slots left for SBP-2 device"); - sbp2_remove_device(hi, scsi_id); + sbp2_remove_device(scsi_id); return -EBUSY; } /* * Login to the sbp-2 device */ - if (sbp2_login_device(hi, scsi_id)) { - + if (sbp2_login_device(scsi_id)) { /* Login failed, just remove the device. */ - SBP2_ERR("sbp2_login_device failed"); - sbp2_remove_device(hi, scsi_id); + sbp2_remove_device(scsi_id); return -EBUSY; } /* * Set max retries to something large on the device */ - sbp2_set_busy_timeout(hi, scsi_id); + sbp2_set_busy_timeout(scsi_id); /* * Do a SBP-2 fetch agent reset */ - sbp2_agent_reset(hi, scsi_id, 0); + sbp2_agent_reset(scsi_id, 1); /* * Get the max speed and packet size that we can use */ - sbp2_max_speed_and_size(hi, scsi_id); + sbp2_max_speed_and_size(scsi_id); return 0; } @@ -1478,18 +1217,19 @@ /* * This function removes an sbp2 device from the sbp2scsi_host_info struct. */ -static void sbp2_remove_device(struct sbp2scsi_host_info *hi, - struct scsi_id_instance_data *scsi_id) +static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; + SBP2_DEBUG("sbp2_remove_device"); /* Complete any pending commands with selection timeout */ - sbp2scsi_complete_all_commands(hi, scsi_id, DID_NO_CONNECT); + sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT); /* Clean up any other structures */ - if (scsi_id->sbp2_total_command_orbs) { - sbp2util_remove_command_orb_pool(scsi_id, hi); - } + sbp2util_remove_command_orb_pool(scsi_id); + + hi->scsi_id[scsi_id->id] = NULL; if (scsi_id->login_response) { pci_free_consistent(hi->host->pdev, @@ -1540,7 +1280,7 @@ } SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id); - hi->scsi_id[scsi_id->id] = NULL; + kfree(scsi_id); } @@ -1597,8 +1337,9 @@ * This function queries the device for the maximum concurrent logins it * supports. */ -static int sbp2_query_logins(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) +static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; int max_logins; int active_logins; @@ -1623,7 +1364,7 @@ ORB_SET_QUERY_LOGINS_RESP_LENGTH(sizeof(struct sbp2_query_logins_response)); SBP2_DEBUG("sbp2_query_logins: reserved_resp_length initialized"); - scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + + scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id); scsi_id->query_logins_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); @@ -1633,6 +1374,9 @@ SBP2_DEBUG("sbp2_query_logins: orb byte-swapped"); + sbp2util_packet_dump(scsi_id->query_logins_orb, sizeof(stuct sbp2_query_logins_orb), + "sbp2 query logins orb", scsi_id->query_logins_orb_dma); + memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response)); memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); @@ -1675,14 +1419,14 @@ max_logins = RESPONSE_GET_MAX_LOGINS(scsi_id->query_logins_response->length_max_logins); SBP2_INFO("Maximum concurrent logins supported: %d", max_logins); - + active_logins = RESPONSE_GET_ACTIVE_LOGINS(scsi_id->query_logins_response->length_max_logins); SBP2_INFO("Number of active logins: %d", active_logins); - + if (active_logins >= max_logins) { return(-EIO); } - + return 0; } @@ -1690,8 +1434,9 @@ * This function is called in order to login to a particular SBP-2 device, * after a bus reset. */ -static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) +static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; SBP2_DEBUG("sbp2_login_device"); @@ -1702,7 +1447,7 @@ } if (!sbp2_exclusive_login) { - if (sbp2_query_logins(hi, scsi_id)) { + if (sbp2_query_logins(scsi_id)) { SBP2_ERR("Device does not support any more concurrent logins"); return(-EIO); } @@ -1824,8 +1569,9 @@ * This function is called in order to logout from a particular SBP-2 * device, usually called during driver unload. */ -static int sbp2_logout_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) +static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; SBP2_DEBUG("sbp2_logout_device"); @@ -1882,8 +1628,9 @@ * This function is called in order to reconnect to a particular SBP-2 * device, after a bus reset. */ -static int sbp2_reconnect_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) +static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; SBP2_DEBUG("sbp2_reconnect_device"); @@ -1970,8 +1717,8 @@ * This function is called in order to set the busy timeout (number of * retries to attempt) on the sbp2 device. */ -static int sbp2_set_busy_timeout(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) -{ +static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id) +{ quadlet_t data; SBP2_DEBUG("sbp2_set_busy_timeout"); @@ -1988,13 +1735,30 @@ return(0); } -static void sbp2_parse_logical_unit_directory - (struct scsi_id_instance_data *scsi_id, struct unit_directory *ud) +/* + * This function is called to parse sbp2 device's config rom unit + * directory. Used to determine things like sbp2 management agent offset, + * and command set used (SCSI or RBC). + */ +static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id) { + struct unit_directory *ud; int i; - + + SBP2_DEBUG("sbp2_parse_unit_directory"); + + /* Initialize some fields, in case an entry does not exist */ + scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED; + scsi_id->sbp2_management_agent_addr = 0x0; + scsi_id->sbp2_command_set_spec_id = 0x0; + scsi_id->sbp2_command_set = 0x0; + scsi_id->sbp2_unit_characteristics = 0x0; + scsi_id->sbp2_firmware_revision = 0x0; + + ud = scsi_id->ud; + /* Handle different fields in the unit directory, based on keys */ - for (i = 0; i < ud->count; i++) { + for (i = 0; i < ud->length; i++) { switch (CONFIG_ROM_KEY(ud->quadlets[i])) { case SBP2_CSR_OFFSET_KEY: /* Save off the management agent address */ @@ -2054,44 +1818,11 @@ else SBP2_DEBUG("sbp2_firmware_revision = %x", (unsigned int) scsi_id->sbp2_firmware_revision); break; -#if 0 - case SBP2_LOGICAL_UNIT_DIRECTORY_OFFSET_KEY: - SBP2_DEBUG("sbp2_parse_unit_directory: found logical unit directory"); - break; -#endif + default: break; } } -} - -/* - * This function is called to parse sbp2 device's config rom unit - * directory. Used to determine things like sbp2 management agent offset, - * and command set used (SCSI or RBC). - */ -static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id) -{ - struct unit_directory *ud; - int i; - - SBP2_DEBUG("sbp2_parse_unit_directory"); - - /* Initialize some fields, in case an entry does not exist */ - scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED; - scsi_id->sbp2_management_agent_addr = 0x0; - scsi_id->sbp2_command_set_spec_id = 0x0; - scsi_id->sbp2_command_set = 0x0; - scsi_id->sbp2_unit_characteristics = 0x0; - scsi_id->sbp2_firmware_revision = 0x0; - - ud = scsi_id->ud; - if (ud->parent != NULL) - sbp2_parse_logical_unit_directory(scsi_id, ud->parent); - if (ud->n_children == 0) - sbp2_parse_logical_unit_directory(scsi_id, ud); - else - return; /* This is the start of our broken device checking. We try to hack * around oddities and known defects. */ @@ -2139,13 +1870,15 @@ * the speed that it needs to use, and the max_rec the host supports, and * it takes care of the rest. */ -static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) +static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id) { + struct sbp2scsi_host_info *hi = scsi_id->hi; + SBP2_DEBUG("sbp2_max_speed_and_size"); /* Initial setting comes from the hosts speed map */ - scsi_id->speed_code = hi->host->speed_map[(hi->host->node_id & NODE_MASK) * 64 - + (scsi_id->ne->nodeid & NODE_MASK)]; + scsi_id->speed_code = hi->host->speed_map[NODEID_TO_NODE(hi->host->node_id) * 64 + + NODEID_TO_NODE(scsi_id->ne->nodeid)]; /* Bump down our speed if the user requested it */ if (scsi_id->speed_code > sbp2_max_speed) { @@ -2156,7 +1889,7 @@ /* Payload size is the lesser of what our speed supports and what * our host supports. */ - scsi_id->max_payload_size = min(sbp2_speedto_maxrec[scsi_id->speed_code], + scsi_id->max_payload_size = min(hpsb_speedto_maxrec[scsi_id->speed_code], (u8)(((be32_to_cpu(hi->host->csr.rom[2]) >> 12) & 0xf) - 1)); SBP2_ERR("Node[" NODE_BUS_FMT "]: Max speed [%s] - Max payload [%u]", @@ -2169,35 +1902,37 @@ /* * This function is called in order to perform a SBP-2 agent reset. */ -static int sbp2_agent_reset(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 flags) +static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) { - struct sbp2_request_packet *agent_reset_request_packet; - + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct hpsb_packet *packet; + quadlet_t data; + SBP2_DEBUG("sbp2_agent_reset"); /* * Ok, let's write to the target's management agent register */ - agent_reset_request_packet = - sbp2util_allocate_write_request_packet(hi, scsi_id->ne, - scsi_id->sbp2_command_block_agent_addr + - SBP2_AGENT_RESET_OFFSET, - 0, ntohl(SBP2_AGENT_RESET_DATA)); + data = ntohl(SBP2_AGENT_RESET_DATA); + packet = sbp2util_allocate_write_packet(hi, scsi_id->ne, + scsi_id->sbp2_command_block_agent_addr + + SBP2_AGENT_RESET_OFFSET, + 4, &data); - if (!agent_reset_request_packet) { - SBP2_ERR("sbp2util_allocate_write_request_packet failed"); - return(-EIO); + if (!packet) { + SBP2_ERR("sbp2util_allocate_write_packet failed"); + return(-ENOMEM); } - if (!hpsb_send_packet(agent_reset_request_packet->packet)) { + if (!hpsb_send_packet(packet)) { SBP2_ERR("hpsb_send_packet failed"); - sbp2util_free_request_packet(agent_reset_request_packet); + sbp2_free_packet(packet); return(-EIO); } - if (!(flags & SBP2_SEND_NO_WAIT)) { - down(&agent_reset_request_packet->packet->state_change); - down(&agent_reset_request_packet->packet->state_change); + if (wait) { + down(&packet->state_change); + down(&packet->state_change); } /* @@ -2206,15 +1941,13 @@ scsi_id->last_orb = NULL; return(0); - } /* * This function is called to create the actual command orb and s/g list * out of the scsi command itself. */ -static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi, - struct scsi_id_instance_data *scsi_id, +static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command, unchar *scsi_cmd, unsigned int scsi_use_sg, @@ -2222,6 +1955,7 @@ void *scsi_request_buffer, unsigned char scsi_dir) { + struct sbp2scsi_host_info *hi = scsi_id->hi; struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer; struct sbp2_command_orb *command_orb = &command->command_orb; struct sbp2_unrestricted_page_table *scatter_gather_element = @@ -2464,10 +2198,11 @@ /* * This function is called in order to begin a regular SBP-2 command. */ -static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, +static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command) { - struct sbp2_request_packet *command_request_packet; + struct sbp2scsi_host_info *hi = scsi_id->hi; + struct hpsb_packet *packet; struct sbp2_command_orb *command_orb = &command->command_orb; outstanding_orb_incr; @@ -2490,25 +2225,24 @@ */ if (hpsb_node_entry_valid(scsi_id->ne)) { - command_request_packet = - sbp2util_allocate_write_request_packet(hi, scsi_id->ne, + packet = sbp2util_allocate_write_packet(hi, scsi_id->ne, scsi_id->sbp2_command_block_agent_addr + - SBP2_ORB_POINTER_OFFSET, 8, 0); + SBP2_ORB_POINTER_OFFSET, 8, NULL); - if (!command_request_packet) { - SBP2_ERR("sbp2util_allocate_write_request_packet failed"); - return(-EIO); + if (!packet) { + SBP2_ERR("sbp2util_allocate_write_packet failed"); + return(-ENOMEM); } - command_request_packet->packet->data[0] = ORB_SET_NODE_ID(hi->host->node_id); - command_request_packet->packet->data[1] = command->command_orb_dma; - sbp2util_cpu_to_be32_buffer(command_request_packet->packet->data, 8); + packet->data[0] = ORB_SET_NODE_ID(hi->host->node_id); + packet->data[1] = command->command_orb_dma; + sbp2util_cpu_to_be32_buffer(packet->data, 8); SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb); - if (!hpsb_send_packet(command_request_packet->packet)) { + if (!hpsb_send_packet(packet)) { SBP2_ERR("hpsb_send_packet failed"); - sbp2util_free_request_packet(command_request_packet); + sbp2_free_packet(packet); return(-EIO); } @@ -2539,22 +2273,22 @@ * Ring the doorbell */ if (hpsb_node_entry_valid(scsi_id->ne)) { + quadlet_t data = cpu_to_be32(command->command_orb_dma); - command_request_packet = sbp2util_allocate_write_request_packet(hi, - scsi_id->ne, - scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET, - 0, cpu_to_be32(command->command_orb_dma)); + packet = sbp2util_allocate_write_packet(hi, scsi_id->ne, + scsi_id->sbp2_command_block_agent_addr + + SBP2_DOORBELL_OFFSET, 4, &data); - if (!command_request_packet) { - SBP2_ERR("sbp2util_allocate_write_request_packet failed"); - return(-EIO); + if (!packet) { + SBP2_ERR("sbp2util_allocate_write_packet failed"); + return(-ENOMEM); } SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb); - if (!hpsb_send_packet(command_request_packet->packet)) { + if (!hpsb_send_packet(packet)) { SBP2_ERR("hpsb_send_packet failed"); - sbp2util_free_request_packet(command_request_packet); + sbp2_free_packet(packet); return(-EIO); } } @@ -2569,7 +2303,7 @@ /* * This function is called in order to begin a regular SBP-2 command. */ -static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, +static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { unchar *cmd = (unchar *) SCpnt->cmnd; @@ -2587,7 +2321,7 @@ /* * Allocate a command orb and s/g structure */ - command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done, hi); + command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done); if (!command) { return(-EIO); } @@ -2607,7 +2341,7 @@ /* * Now actually fill in the comamnd orb and sbp2 s/g list */ - sbp2_create_command_orb(hi, scsi_id, command, cmd, SCpnt->use_sg, + sbp2_create_command_orb(scsi_id, command, cmd, SCpnt->use_sg, request_bufflen, SCpnt->request_buffer, SCpnt->sc_data_direction); /* @@ -2627,7 +2361,7 @@ /* * Link up the orb, and ring the doorbell if needed */ - sbp2_link_orb_command(hi, scsi_id, command); + sbp2_link_orb_command(scsi_id, command); return(0); } @@ -2866,9 +2600,7 @@ return(RCODE_ADDRESS_ERROR); } - spin_lock_irqsave(&sbp2_host_info_lock, flags); - hi = sbp2_find_host_info(host); - spin_unlock_irqrestore(&sbp2_host_info_lock, flags); + hi = hpsb_get_hostinfo(&sbp2_highlevel, host); if (!hi) { SBP2_ERR("host info is NULL - this is bad!"); @@ -2946,7 +2678,7 @@ * Initiate a fetch agent reset. */ SBP2_DEBUG("Dead bit set - initiating fetch agent reset"); - sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT); + sbp2_agent_reset(scsi_id, 0); } SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb); @@ -2987,7 +2719,8 @@ * io_request_lock (in sbp2scsi_queuecommand). */ SBP2_DEBUG("Completing SCSI command"); - sbp2scsi_complete_command(hi, scsi_id, scsi_status, SCpnt, command->Current_done); + sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt, + command->Current_done); SBP2_ORB_DEBUG("command orb completed"); } @@ -3014,7 +2747,7 @@ /* * Pull our host info and scsi id instance data from the scsi command */ - hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0]; + hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, (unsigned long)SCpnt->device->host->hostt); if (!hi) { SBP2_ERR("sbp2scsi_host_info is NULL - this is bad!"); @@ -3053,7 +2786,7 @@ SBP2_DEBUG("REQUEST_SENSE"); memcpy(SCpnt->request_buffer, SCpnt->sense_buffer, SCpnt->request_bufflen); memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); - sbp2scsi_complete_command(hi, scsi_id, SBP2_SCSI_STATUS_GOOD, SCpnt, done); + sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_GOOD, SCpnt, done); return(0); } @@ -3071,9 +2804,10 @@ * Try and send our SCSI command */ spin_lock_irqsave(&hi->sbp2_command_lock, flags); - if (sbp2_send_command(hi, scsi_id, SCpnt, done)) { + if (sbp2_send_command(scsi_id, SCpnt, done)) { SBP2_ERR("Error sending SCSI command"); - sbp2scsi_complete_command(hi, scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT, SCpnt, done); + sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT, + SCpnt, done); } spin_unlock_irqrestore(&hi->sbp2_command_lock, flags); @@ -3084,10 +2818,10 @@ * This function is called in order to complete all outstanding SBP-2 * commands (in case of resets, etc.). */ -static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi, - struct scsi_id_instance_data *scsi_id, +static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id, u32 status) { + struct sbp2scsi_host_info *hi = scsi_id->hi; struct list_head *lh; struct sbp2_command_info *command; @@ -3119,8 +2853,9 @@ * * This can be called in interrupt context. */ -static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 scsi_status, - Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id, + u32 scsi_status, Scsi_Cmnd *SCpnt, + void (*done)(Scsi_Cmnd *)) { unsigned long flags; @@ -3235,7 +2970,8 @@ */ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt) { - struct sbp2scsi_host_info *hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0]; + struct sbp2scsi_host_info *hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, + (unsigned long)SCpnt->device->host->hostt); struct scsi_id_instance_data *scsi_id = hi->scsi_id[SCpnt->target]; struct sbp2_command_info *command; unsigned long flags; @@ -3272,8 +3008,8 @@ /* * Initiate a fetch agent reset. */ - sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT); - sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY); + sbp2_agent_reset(scsi_id, 0); + sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY); spin_unlock_irqrestore(&hi->sbp2_command_lock, flags); } @@ -3285,97 +3021,94 @@ */ static int sbp2scsi_reset (Scsi_Cmnd *SCpnt) { - struct sbp2scsi_host_info *hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0]; - struct scsi_id_instance_data *scsi_id = hi->scsi_id[SCpnt->target]; + struct sbp2scsi_host_info *hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, + (unsigned long)SCpnt->device->host->hostt); + struct scsi_id_instance_data *scsi_id = hi->scsi_id[SCpnt->device->id]; SBP2_ERR("reset requested"); if (scsi_id) { SBP2_ERR("Generating sbp2 fetch agent reset"); - sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT); + sbp2_agent_reset(scsi_id, 0); } return(SUCCESS); } -/* - * Called by scsi stack to get bios parameters (used by fdisk, and at boot). - */ -static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]) +static const char *sbp2scsi_info (struct Scsi_Host *host) { - int heads, sectors, cylinders; + return "SCSI emulation for IEEE-1394 SBP-2 Devices"; +} - SBP2_DEBUG("Request for bios parameters"); +/* Called for contents of procfs */ +#define SPRINTF(args...) \ + do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0) - heads = 64; - sectors = 32; - cylinders = (int)disk->capacity / (heads * sectors); +static int sbp2scsi_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +{ + Scsi_Device *scd; + struct Scsi_Host *host; + struct sbp2scsi_host_info *hi; + char *pos = buffer; - if (cylinders > 1024) { - heads = 255; - sectors = 63; - cylinders = (int)disk->capacity / (heads * sectors); - } + /* if someone is sending us data, just throw it away */ + if (inout) + return length; - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; + for (host = scsi_hostlist; host; host = host->next) + if (host->host_no == hostno) + break; - return(0); -} + if (!host) /* if we couldn't find it, we return an error */ + return -ESRCH; -/* - * Called by scsi stack after scsi driver is registered - */ -static int sbp2scsi_detect (Scsi_Host_Template *tpnt) -{ - SBP2_DEBUG("sbp2scsi_detect"); + hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, (unsigned long)host->hostt); + if (!hi) /* shouldn't happen, but... */ + return -ESRCH; - /* - * Call sbp2_init to register with the ieee1394 stack. This - * results in a callback to sbp2_add_host for each ieee1394 - * host controller currently registered, and for each of those - * we register a scsi host with the scsi stack. - */ - - spin_unlock_irq(&io_request_lock); - sbp2_init(); - spin_lock_irq(&io_request_lock); + SPRINTF("Host scsi%d : SBP-2 IEEE-1394 (%s)\n", hostno, + hi->host->driver->name); + SPRINTF("Driver version : %s\n", version); - /* We return the number of hosts registered. */ - return scsi_driver_template.present; -} + SPRINTF("\nModule options :\n"); + SPRINTF(" max_speed : %s\n", hpsb_speedto_str[sbp2_max_speed]); + SPRINTF(" max_sectors : %d\n", sbp2_max_sectors); + SPRINTF(" serialize_io : %s\n", sbp2_serialize_io ? "yes" : "no"); + SPRINTF(" exclusive_login : %s\n", sbp2_exclusive_login ? "yes" : "no"); + SPRINTF("\nAttached devices : %s\n", host->host_queue ? "" : "none"); -/* - * Called for contents of procfs - */ -static const char *sbp2scsi_info (struct Scsi_Host *host) -{ - struct sbp2scsi_host_info *hi = sbp2_find_host_info_scsi(host); - static char info[1024]; + for (scd = host->host_queue; scd; scd = scd->next) { + int i; - if (!hi) /* shouldn't happen, but... */ - return "IEEE-1394 SBP-2 protocol driver"; + SPRINTF(" [Channel: %02d, Id: %02d, Lun: %02d] ", scd->channel, + scd->id, scd->lun); + SPRINTF("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ? + scsi_device_types[(short) scd->type] : "Unknown device"); + + for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++) + SPRINTF("%c", scd->vendor[i]); + + SPRINTF(" "); - sprintf(info, "IEEE-1394 SBP-2 protocol driver (host: %s)\n%s\n" - "SBP-2 module load options:\n" - "- Max speed supported: %s\n" - "- Max sectors per I/O supported: %d\n" - "- Max outstanding commands supported: %d\n" - "- Max outstanding commands per lun supported: %d\n" - "- Serialized I/O (debug): %s\n" - "- Exclusive login: %s", - hi->host->driver->name, - version, - hpsb_speedto_str[sbp2_max_speed], - sbp2_max_sectors, - sbp2_max_outstanding_cmds, - sbp2_max_cmds_per_lun, - sbp2_serialize_io ? "yes" : "no", - sbp2_exclusive_login ? "yes" : "no"); + for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++) + SPRINTF("%c", scd->model[i]); - return info; + SPRINTF("\n"); + } + + SPRINTF("\n"); + + /* Calculate start of next buffer, and return value. */ + *start = buffer + offset; + + if ((pos - buffer) < offset) + return (0); + else if ((pos - buffer - offset) < length) + return (pos - buffer - offset); + else + return (length); } @@ -3386,60 +3119,57 @@ /* SCSI host template */ static Scsi_Host_Template scsi_driver_template = { - .name = "IEEE-1394 SBP-2 protocol driver", - .info = sbp2scsi_info, - .detect = sbp2scsi_detect, - .queuecommand = sbp2scsi_queuecommand, - .eh_abort_handler = sbp2scsi_abort, - .eh_device_reset_handler =sbp2scsi_reset, - .eh_bus_reset_handler = sbp2scsi_reset, - .eh_host_reset_handler =sbp2scsi_reset, - .bios_param = sbp2scsi_biosparam, - .this_id = -1, - .sg_tablesize = SBP2_MAX_SG_ELEMENTS, - .use_clustering = SBP2_CLUSTERING, - .use_new_eh_code = TRUE, - .emulated = 1, - .proc_name = SBP2_DEVICE_NAME, + .module = THIS_MODULE, + .name = "SBP-2 IEEE-1394", + .proc_name = NULL, // Filled in per-host + .info = sbp2scsi_info, + .proc_info = sbp2scsi_proc_info, + .detect = sbp2scsi_detect, + .queuecommand = sbp2scsi_queuecommand, + .eh_abort_handler = sbp2scsi_abort, + .eh_device_reset_handler = sbp2scsi_reset, + .eh_bus_reset_handler = sbp2scsi_reset, + .eh_host_reset_handler = sbp2scsi_reset, + .use_new_eh_code = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .cmd_per_lun = SBP2_MAX_CMDS_PER_LUN, + .can_queue = SBP2_MAX_SCSI_QUEUE, + .emulated = 1, + .highmem_io = 1, }; static int sbp2_module_init(void) { SBP2_DEBUG("sbp2_module_init"); - /* - * Module load debug option to force one command at a time (serializing I/O) - */ + /* Module load debug option to force one command at a time (serializing I/O) */ if (sbp2_serialize_io) { SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)"); scsi_driver_template.can_queue = 1; scsi_driver_template.cmd_per_lun = 1; - } else { - scsi_driver_template.can_queue = sbp2_max_outstanding_cmds; - scsi_driver_template.cmd_per_lun = sbp2_max_cmds_per_lun; } - /* - * Set max sectors (module load option). Default is 255 sectors. - */ + /* Set max sectors (module load option). Default is 255 sectors. */ scsi_driver_template.max_sectors = sbp2_max_sectors; - /* - * Ideally we would register our scsi_driver_template with the - * scsi stack and after that register with the ieee1394 stack - * and process the add_host callbacks. However, the detect - * function in the scsi host template requires that we find at - * least one host, so we "nest" the registrations by calling - * sbp2_init from the detect function. - */ - scsi_driver_template.module = THIS_MODULE; - if (SCSI_REGISTER_HOST(&scsi_driver_template) || - !scsi_driver_template.present) { - SBP2_ERR("Please load the lower level IEEE-1394 driver " - "(e.g. ohci1394) before sbp2..."); - sbp2_cleanup(); - return -ENODEV; - } + + /* Register our high level driver with 1394 stack */ + hpsb_register_highlevel(&sbp2_highlevel); + + /* Register our sbp2 status address space... */ + hpsb_register_addrspace(&sbp2_highlevel, &sbp2_ops, SBP2_STATUS_FIFO_ADDRESS, + SBP2_STATUS_FIFO_ADDRESS + + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2SCSI_MAX_SCSI_IDS+1)); + + /* Handle data movement if physical dma is not enabled/supported + * on host controller */ +#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA + hpsb_register_addrspace(&sbp2_highlevel, &sbp2_physdma_ops, 0x0ULL, 0xfffffffcULL); +#endif + + hpsb_register_protocol(&sbp2_driver); return 0; } @@ -3448,17 +3178,9 @@ { SBP2_DEBUG("sbp2_module_exit"); - /* - * On module unload we unregister with the ieee1394 stack - * which results in remove_host callbacks for all ieee1394 - * host controllers. In the callbacks we unregister the - * corresponding scsi hosts. - */ - sbp2_cleanup(); - - if (SCSI_UNREGISTER_HOST(&scsi_driver_template)) - SBP2_ERR("sbp2_module_exit: couldn't unregister scsi driver"); + hpsb_unregister_protocol(&sbp2_driver); + hpsb_unregister_highlevel(&sbp2_highlevel); } module_init(sbp2_module_init); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/sbp2.h linux.21rc1-ac2/drivers/ieee1394/sbp2.h --- linux.21rc1/drivers/ieee1394/sbp2.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/sbp2.h 2003-04-25 03:38:30.000000000 +0100 @@ -27,33 +27,19 @@ #define SCSI_UNREGISTER_HOST(tmpl) scsi_unregister_module(MODULE_SCSI_HA, tmpl) #define SBP2_DEVICE_NAME "sbp2" -#define SBP2_DEVICE_NAME_SIZE 4 /* * SBP2 specific structures and defines */ -#define ORB_FMT_CMD 0x0 -#define ORB_FMT_DUMMY 0x3 - #define ORB_DIRECTION_WRITE_TO_MEDIA 0x0 #define ORB_DIRECTION_READ_FROM_MEDIA 0x1 #define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 #define ORB_SET_NULL_PTR(value) ((value & 0x1) << 31) #define ORB_SET_NOTIFY(value) ((value & 0x1) << 31) -#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) +#define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) /* unused ? */ #define ORB_SET_NODE_ID(value) ((value & 0xffff) << 16) - -struct sbp2_dummy_orb { - volatile u32 next_ORB_hi; - volatile u32 next_ORB_lo; - u32 reserved1; - u32 reserved2; - u32 notify_rq_fmt; - u8 command_block[12]; -}; - #define ORB_SET_DATA_SIZE(value) (value & 0xffff) #define ORB_SET_PAGE_SIZE(value) ((value & 0x7) << 16) #define ORB_SET_PAGE_TABLE_PRESENT(value) ((value & 0x1) << 19) @@ -271,25 +257,15 @@ * Other misc defines */ #define SBP2_128KB_BROKEN_FIRMWARE 0xa0b800 -#define SBP2_BROKEN_FIRMWARE_MAX_TRANSFER 0x20000 #define SBP2_DEVICE_TYPE_LUN_UNINITIALIZED 0xffffffff /* - * Flags for SBP-2 functions - */ -#define SBP2_SEND_NO_WAIT 0x00000001 - -/* * SCSI specific stuff */ -#define SBP2_MAX_SG_ELEMENTS SG_ALL -#define SBP2_CLUSTERING ENABLE_CLUSTERING #define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 -#define SBP2SCSI_MAX_SCSI_IDS 16 /* Max sbp2 device instances supported */ -#define SBP2SCSI_MAX_OUTSTANDING_CMDS 64 /* Max total outstanding sbp2 commands allowed at a time! */ -#define SBP2SCSI_MAX_CMDS_PER_LUN 1 /* Max outstanding sbp2 commands per device - tune as needed */ +#define SBP2SCSI_MAX_SCSI_IDS 32 /* Max sbp2 device instances supported */ #define SBP2_MAX_SECTORS 255 /* Max sectors supported */ #ifndef TYPE_SDAD @@ -332,26 +308,18 @@ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN }; -#define SBP2_MAX_REQUEST_PACKETS (sbp2_max_outstanding_cmds * 2) -#define SBP2_MAX_COMMAND_ORBS (sbp2_max_cmds_per_lun * 2) - -/* - * Request packets structure (used for sending command and agent reset packets) - */ -struct sbp2_request_packet { - - struct list_head list; - struct hpsb_packet *packet; - struct tq_struct tq; - void *hi_context; - -}; - +/* This should be safe. If there's more than one LUN per node, we could + * saturate the tlabel's though. */ +#define SBP2_MAX_CMDS_PER_LUN 8 +#define SBP2_MAX_SCSI_QUEUE (SBP2_MAX_CMDS_PER_LUN * SBP2SCSI_MAX_SCSI_IDS) +#define SBP2_MAX_COMMAND_ORBS SBP2_MAX_SCSI_QUEUE /* This is the two dma types we use for cmd_dma below */ -#define CMD_DMA_NONE 0x0 -#define CMD_DMA_PAGE 0x1 -#define CMD_DMA_SINGLE 0x2 +enum cmd_dma_types { + CMD_DMA_NONE, + CMD_DMA_PAGE, + CMD_DMA_SINGLE +}; /* * Encapsulates all the info necessary for an outstanding command. @@ -365,11 +333,11 @@ void (*Current_done)(Scsi_Cmnd *); /* Also need s/g structure for each sbp2 command */ - struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS] ____cacheline_aligned; + struct sbp2_unrestricted_page_table scatter_gather_element[SG_ALL] ____cacheline_aligned; dma_addr_t sge_dma ____cacheline_aligned; void *sge_buffer; dma_addr_t cmd_dma; - int dma_type; + enum cmd_dma_types dma_type; unsigned long dma_size; int dma_dir; @@ -379,6 +347,10 @@ #define SBP2_BREAKAGE_128K_MAX_TRANSFER 0x1 #define SBP2_BREAKAGE_INQUIRY_HACK 0x2 + +struct sbp2scsi_host_info; + + /* * Information needed on a per scsi id basis (one for each sbp2 device) */ @@ -434,11 +406,13 @@ spinlock_t sbp2_command_orb_lock; struct list_head sbp2_command_orb_inuse; struct list_head sbp2_command_orb_completed; - u32 sbp2_total_command_orbs; /* Node entry, as retrieved from NodeMgr entries */ struct node_entry *ne; + /* A backlink to our host_info */ + struct sbp2scsi_host_info *hi; + /* Device specific workarounds/brokeness */ u32 workarounds; }; @@ -447,46 +421,24 @@ * Sbp2 host data structure (one per sbp2 host) */ struct sbp2scsi_host_info { - - /* - * For use in keeping track of hosts - */ - struct list_head list; struct hpsb_host *host; - /* - * Spin locks for command processing and packet pool management - */ + /* Spin locks for command processing */ spinlock_t sbp2_command_lock; - spinlock_t sbp2_request_packet_lock; - /* - * This is the scsi host we register with the scsi mid level. - * We keep a reference to it here, so we can unregister it - * when the hpsb_host is removed. - */ + /* This is the scsi host we register with the scsi mid level. */ struct Scsi_Host *scsi_host; - /* - * Lists keeping track of inuse/free sbp2_request_packets. These structures are - * used for sending out sbp2 command and agent reset packets. We initially create - * a pool of request packets so that we don't have to do any kmallocs while in critical - * I/O paths. - */ - struct list_head sbp2_req_inuse; - struct list_head sbp2_req_free; + /* Our allocated scsi_host_template */ + Scsi_Host_Template sht; - /* - * Here is the pool of request packets. All the hpsb packets (for 1394 bus transactions) - * are allocated at init and simply re-initialized when needed. - */ - struct sbp2_request_packet *request_packet; + /* The proc_name used with the above sht */ + char proc_name[32]; - /* - * SCSI ID instance data (one for each sbp2 device instance possible) - */ - struct scsi_id_instance_data *scsi_id[SBP2SCSI_MAX_SCSI_IDS]; + struct hpsb_highlevel *hl; + /* SCSI ID instance data (one for each sbp2 device instance possible) */ + struct scsi_id_instance_data *scsi_id[SBP2SCSI_MAX_SCSI_IDS]; }; /* @@ -496,21 +448,13 @@ /* * Various utility prototypes */ -static int sbp2util_create_request_packet_pool(struct sbp2scsi_host_info *hi); -static void sbp2util_remove_request_packet_pool(struct sbp2scsi_host_info *hi); -static struct sbp2_request_packet *sbp2util_allocate_write_request_packet(struct sbp2scsi_host_info *hi, - struct node_entry *ne, u64 addr, - size_t data_size, - quadlet_t data); -static void sbp2util_free_request_packet(struct sbp2_request_packet *request_packet); -static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id, struct sbp2scsi_host_info *hi); -static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id, struct sbp2scsi_host_info *hi); +static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id); +static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id); static struct sbp2_command_info *sbp2util_find_command_for_orb(struct scsi_id_instance_data *scsi_id, dma_addr_t orb); static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt); static struct sbp2_command_info *sbp2util_allocate_command_orb(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *Current_SCpnt, - void (*Current_done)(Scsi_Cmnd *), - struct sbp2scsi_host_info *hi); + void (*Current_done)(Scsi_Cmnd *)); static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command); @@ -518,17 +462,14 @@ * IEEE-1394 core driver related prototypes */ static void sbp2_add_host(struct hpsb_host *host); -static struct sbp2scsi_host_info *sbp2_find_host_info(struct hpsb_host *host); static void sbp2_remove_host(struct hpsb_host *host); -int sbp2_init(void); -void sbp2_cleanup(void); static int sbp2_probe(struct unit_directory *ud); static void sbp2_disconnect(struct unit_directory *ud); static void sbp2_update(struct unit_directory *ud); + static int sbp2_start_device(struct sbp2scsi_host_info *hi, struct unit_directory *ud); -static void sbp2_remove_device(struct sbp2scsi_host_info *hi, - struct scsi_id_instance_data *scsi_id); +static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id); #ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data, @@ -540,45 +481,29 @@ /* * SBP-2 protocol related prototypes */ -static int sbp2_query_logins(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); -static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); -static int sbp2_reconnect_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); -static int sbp2_logout_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); +static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id); +static int sbp2_login_device(struct scsi_id_instance_data *scsi_id); +static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id); +static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id); static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data, u64 addr, unsigned int length, u16 flags); -static int sbp2_agent_reset(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 flags); -static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi, - struct scsi_id_instance_data *scsi_id, +static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait); +static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command, unchar *scsi_cmd, unsigned int scsi_use_sg, unsigned int scsi_request_bufflen, void *scsi_request_buffer, unsigned char scsi_dir); -static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, +static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command); -static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, +static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data); static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd); static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt); static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id); -static int sbp2_set_busy_timeout(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); -static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); - -/* - * Scsi interface related prototypes - */ -static int sbp2scsi_detect (Scsi_Host_Template *tpnt); -static const char *sbp2scsi_info (struct Scsi_Host *host); -void sbp2scsi_setup(char *str, int *ints); -static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]); -static int sbp2scsi_abort (Scsi_Cmnd *SCpnt); -static int sbp2scsi_reset (Scsi_Cmnd *SCpnt); -static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); -static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, - u32 status); -static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, - u32 scsi_status, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id); +static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id); #endif /* SBP2_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/video1394.c linux.21rc1-ac2/drivers/ieee1394/video1394.c --- linux.21rc1/drivers/ieee1394/video1394.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/video1394.c 2003-04-25 03:38:30.000000000 +0100 @@ -37,8 +37,7 @@ #include #include #include - -#include +#include #include #include #include @@ -47,12 +46,12 @@ #include "ieee1394.h" #include "ieee1394_types.h" +#include "ieee1394_hotplug.h" #include "hosts.h" #include "ieee1394_core.h" #include "highlevel.h" #include "video1394.h" #include "dma.h" -#include "ieee1394_hotplug.h" #include "ohci1394.h" @@ -66,9 +65,6 @@ #define vmalloc_32(x) vmalloc(x) #endif -#define remap_page_range_1394(vma, start, addr, size, prot) \ - remap_page_range(start, addr, size, prot) - struct it_dma_prg { struct dma_cmd begin; quadlet_t data[4]; @@ -118,8 +114,6 @@ struct video_card { struct ti_ohci *ohci; - struct list_head list; - int id; devfs_handle_t devfs; }; @@ -156,12 +150,9 @@ void wakeup_dma_ir_ctx(unsigned long l); void wakeup_dma_it_ctx(unsigned long l); -static LIST_HEAD(video1394_cards); -static spinlock_t video1394_cards_lock = SPIN_LOCK_UNLOCKED; - static devfs_handle_t devfs_handle; -static struct hpsb_highlevel *hl_handle = NULL; +static struct hpsb_highlevel video1394_highlevel; static int free_dma_iso_ctx(struct dma_iso_ctx *d) { @@ -1176,21 +1167,10 @@ static int video1394_open(struct inode *inode, struct file *file) { int i = ieee1394_file_to_instance(file); - unsigned long flags; - struct video_card *video = NULL; - struct list_head *lh; + struct video_card *video; struct file_ctx *ctx; - spin_lock_irqsave(&video1394_cards_lock, flags); - list_for_each(lh, &video1394_cards) { - struct video_card *p = list_entry(lh, struct video_card, list); - if (p->id == i) { - video = p; - break; - } - } - spin_unlock_irqrestore(&video1394_cards_lock, flags); - + video = hpsb_get_hostinfo_bykey(&video1394_highlevel, i); if (video == NULL) return -EIO; @@ -1242,124 +1222,83 @@ return 0; } +static struct file_operations video1394_fops= +{ + .owner = THIS_MODULE, + .ioctl = video1394_ioctl, + .mmap = video1394_mmap, + .open = video1394_open, + .release = video1394_release +}; + /*** HOTPLUG STUFF **********************************************************/ /* * Export information about protocols/devices supported by this driver. */ static struct ieee1394_device_id video1394_id_table[] = { { - .match_flags =IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, - .version = CAMERA_SW_VERSION_ENTRY & 0xffffff + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION, + .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff, + .version = CAMERA_SW_VERSION_ENTRY & 0xffffff }, { } }; -static struct hpsb_protocol_driver video1394_driver = { - .name = "1394 Digital Camera Driver", - .id_table = video1394_id_table, -}; - MODULE_DEVICE_TABLE(ieee1394, video1394_id_table); - -/******************************************************************************/ - -static struct file_operations video1394_fops= -{ - .owner = THIS_MODULE, - .ioctl = video1394_ioctl, - .mmap = video1394_mmap, - .open = video1394_open, - .release = video1394_release +static struct hpsb_protocol_driver video1394_driver = { + .name = "1394 Digital Camera Driver", + .id_table = video1394_id_table, }; -static int video1394_init(struct ti_ohci *ohci) + +static void video1394_add_host (struct hpsb_host *host) { + struct ti_ohci *ohci; struct video_card *video; - unsigned long flags; char name[16]; int minor; - video = kmalloc(sizeof(struct video_card), GFP_KERNEL); + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + ohci = (struct ti_ohci *)host->hostdata; + + video = hpsb_create_hostinfo(&video1394_highlevel, host, sizeof(*video)); if (video == NULL) { PRINT(KERN_ERR, ohci->id, "Cannot allocate video_card"); - return -1; + return; } - memset(video, 0, sizeof(struct video_card)); - - spin_lock_irqsave(&video1394_cards_lock, flags); - INIT_LIST_HEAD(&video->list); - list_add_tail(&video->list, &video1394_cards); - spin_unlock_irqrestore(&video1394_cards_lock, flags); - - video->id = ohci->id; + hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->id); video->ohci = ohci; - sprintf(name, "%d", video->id); - minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + video->id; + sprintf(name, "%d", ohci->id); + minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->id; video->devfs = devfs_register(devfs_handle, name, DEVFS_FL_AUTO_OWNER, IEEE1394_MAJOR, minor, S_IFCHR | S_IRUSR | S_IWUSR, &video1394_fops, NULL); - return 0; + return; } -/* Must be called under spinlock */ -static void remove_card(struct video_card *video) -{ - devfs_unregister(video->devfs); - list_del(&video->list); - - kfree(video); -} static void video1394_remove_host (struct hpsb_host *host) { - struct ti_ohci *ohci; - unsigned long flags; - struct list_head *lh, *next; - struct video_card *p; + struct video_card *video = hpsb_get_hostinfo(&video1394_highlevel, host); - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - spin_lock_irqsave(&video1394_cards_lock, flags); - list_for_each_safe(lh, next, &video1394_cards) { - p = list_entry(lh, struct video_card, list); - if (p->ohci == ohci) { - remove_card(p); - break; - } - } - spin_unlock_irqrestore(&video1394_cards_lock, flags); + if (video) + devfs_unregister(video->devfs); return; } -static void video1394_add_host (struct hpsb_host *host) -{ - struct ti_ohci *ohci; - /* We only work with the OHCI-1394 driver */ - if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) - return; - - ohci = (struct ti_ohci *)host->hostdata; - - video1394_init(ohci); - - return; -} - -static struct hpsb_highlevel_ops hl_ops = { +static struct hpsb_highlevel video1394_highlevel = { + .name = VIDEO1394_DRIVER_NAME, .add_host = video1394_add_host, .remove_host = video1394_remove_host, }; @@ -1373,7 +1312,7 @@ { hpsb_unregister_protocol(&video1394_driver); - hpsb_unregister_highlevel (hl_handle); + hpsb_unregister_highlevel(&video1394_highlevel); devfs_unregister(devfs_handle); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); @@ -1391,13 +1330,7 @@ devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME, NULL); - hl_handle = hpsb_register_highlevel (VIDEO1394_DRIVER_NAME, &hl_ops); - if (hl_handle == NULL) { - PRINT_G(KERN_ERR, "No more memory for driver\n"); - devfs_unregister(devfs_handle); - ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); - return -ENOMEM; - } + hpsb_register_highlevel(&video1394_highlevel); hpsb_register_protocol(&video1394_driver); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/ieee1394/video1394.h linux.21rc1-ac2/drivers/ieee1394/video1394.h --- linux.21rc1/drivers/ieee1394/video1394.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/ieee1394/video1394.h 2003-04-25 16:05:08.000000000 +0100 @@ -75,4 +75,5 @@ struct timeval filltime; /* time of buffer full */ }; + #endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/isdn/hisax/st5481.h linux.21rc1-ac2/drivers/isdn/hisax/st5481.h --- linux.21rc1/drivers/isdn/hisax/st5481.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/isdn/hisax/st5481.h 2003-04-25 16:05:08.000000000 +0100 @@ -218,6 +218,19 @@ #define L1_EVENT_COUNT (EV_TIMER3 + 1) +#if (__GNUC__ > 2) + +#define ERR(format, arg...) \ +printk(KERN_ERR __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) + +#define WARN(format, arg...) \ +printk(KERN_WARNING __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) + +#define INFO(format, arg...) \ +printk(KERN_INFO __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) + +#else + #define ERR(format, arg...) \ printk(KERN_ERR __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) @@ -227,6 +240,8 @@ #define INFO(format, arg...) \ printk(KERN_INFO __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) +#endif + #include "isdnhdlc.h" #include "fsm.h" #include "hisax_if.h" diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/isdn/isdn_tty.c linux.21rc1-ac2/drivers/isdn/isdn_tty.c --- linux.21rc1/drivers/isdn/isdn_tty.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/isdn/isdn_tty.c 2003-04-22 16:44:37.000000000 +0100 @@ -1807,7 +1807,7 @@ #endif return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/macintosh/macserial.c linux.21rc1-ac2/drivers/macintosh/macserial.c --- linux.21rc1/drivers/macintosh/macserial.c 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/macintosh/macserial.c 2003-04-22 16:44:37.000000000 +0100 @@ -1957,7 +1957,7 @@ } OPNDBG("rs_close ttyS%d, count = %d\n", info->line, info->count); - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/Config.in linux.21rc1-ac2/drivers/md/Config.in --- linux.21rc1/drivers/md/Config.in 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -14,5 +14,8 @@ dep_tristate ' Multipath I/O support' CONFIG_MD_MULTIPATH $CONFIG_BLK_DEV_MD dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Device-mapper support (EXPERIMENTAL)' CONFIG_BLK_DEV_DM $CONFIG_MD +fi endmenu diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm.c linux.21rc1-ac2/drivers/md/dm.c --- linux.21rc1/drivers/md/dm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,1158 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include + +/* we only need this for the lv_bmap struct definition, not happy */ +#include + +#define DEFAULT_READ_AHEAD 64 + +static const char *_name = DM_NAME; + +static int major = 0; +static int _major = 0; + +struct io_hook { + struct mapped_device *md; + struct target *target; + int rw; + + void (*end_io) (struct buffer_head * bh, int uptodate); + void *context; +}; + +static kmem_cache_t *_io_hook_cache; + +static struct mapped_device *_devs[MAX_DEVICES]; +static struct rw_semaphore _dev_locks[MAX_DEVICES]; + +/* + * This lock is only held by dm_create and dm_set_name to avoid + * race conditions where someone else may create a device with + * the same name. + */ +static spinlock_t _create_lock = SPIN_LOCK_UNLOCKED; + +/* block device arrays */ +static int _block_size[MAX_DEVICES]; +static int _blksize_size[MAX_DEVICES]; +static int _hardsect_size[MAX_DEVICES]; + +static devfs_handle_t _dev_dir; + +static int request(request_queue_t * q, int rw, struct buffer_head *bh); +static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb); + +/* + * Protect the mapped_devices referenced from _dev[] + */ +struct mapped_device *dm_get_r(int minor) +{ + struct mapped_device *md; + + if (minor >= MAX_DEVICES) + return NULL; + + down_read(_dev_locks + minor); + md = _devs[minor]; + if (!md) + up_read(_dev_locks + minor); + return md; +} + +struct mapped_device *dm_get_w(int minor) +{ + struct mapped_device *md; + + if (minor >= MAX_DEVICES) + return NULL; + + down_write(_dev_locks + minor); + md = _devs[minor]; + if (!md) + up_write(_dev_locks + minor); + return md; +} + +static int namecmp(struct mapped_device *md, const char *name, int nametype) +{ + switch (nametype) { + case DM_LOOKUP_BY_NAME: + return strcmp(md->name, name); + break; + + case DM_LOOKUP_BY_UUID: + if (!md->uuid) + return -1; /* never equal */ + + return strcmp(md->uuid, name); + break; + + default: + DMWARN("Unknown comparison type in namecmp: %d", nametype); + BUG(); + } + + return -1; +} + +/* + * The interface (eg, ioctl) will probably access the devices + * through these slow 'by name' locks, this needs improving at + * some point if people start playing with *large* numbers of dm + * devices. + */ +struct mapped_device *dm_get_name_r(const char *name, int nametype) +{ + int i; + struct mapped_device *md; + + for (i = 0; i < MAX_DEVICES; i++) { + md = dm_get_r(i); + if (md) { + if (!namecmp(md, name, nametype)) + return md; + + dm_put_r(md); + } + } + + return NULL; +} + +struct mapped_device *dm_get_name_w(const char *name, int nametype) +{ + int i; + struct mapped_device *md; + + /* + * To avoid getting write locks on all the devices we try + * and promote a read lock to a write lock, this can + * fail, in which case we just start again. + */ + + restart: + for (i = 0; i < MAX_DEVICES; i++) { + md = dm_get_r(i); + if (!md) + continue; + + if (namecmp(md, name, nametype)) { + dm_put_r(md); + continue; + } + + /* found it */ + dm_put_r(md); + + md = dm_get_w(i); + if (!md) + goto restart; + + if (namecmp(md, name, nametype)) { + dm_put_w(md); + goto restart; + } + + return md; + } + + return NULL; +} + +void dm_put_r(struct mapped_device *md) +{ + int minor = MINOR(md->dev); + + if (minor >= MAX_DEVICES) + return; + + up_read(_dev_locks + minor); +} + +void dm_put_w(struct mapped_device *md) +{ + int minor = MINOR(md->dev); + + if (minor >= MAX_DEVICES) + return; + + up_write(_dev_locks + minor); +} + +/* + * Setup and tear down the driver + */ +static __init void init_locks(void) +{ + int i; + + for (i = 0; i < MAX_DEVICES; i++) + init_rwsem(_dev_locks + i); +} + +static __init int local_init(void) +{ + int r; + + init_locks(); + + /* allocate a slab for the io-hooks */ + if (!_io_hook_cache && + !(_io_hook_cache = kmem_cache_create("dm io hooks", + sizeof(struct io_hook), + 0, 0, NULL, NULL))) + return -ENOMEM; + + _major = major; + r = devfs_register_blkdev(_major, _name, &dm_blk_dops); + if (r < 0) { + DMERR("register_blkdev failed"); + kmem_cache_destroy(_io_hook_cache); + return r; + } + + if (!_major) + _major = r; + + /* set up the arrays */ + read_ahead[_major] = DEFAULT_READ_AHEAD; + blk_size[_major] = _block_size; + blksize_size[_major] = _blksize_size; + hardsect_size[_major] = _hardsect_size; + + blk_queue_make_request(BLK_DEFAULT_QUEUE(_major), request); + + _dev_dir = devfs_mk_dir(0, DM_DIR, NULL); + + return 0; +} + +static void local_exit(void) +{ + if (kmem_cache_destroy(_io_hook_cache)) + DMWARN("io_hooks still allocated during unregistration"); + _io_hook_cache = NULL; + + if (devfs_unregister_blkdev(_major, _name) < 0) + DMERR("devfs_unregister_blkdev failed"); + + read_ahead[_major] = 0; + blk_size[_major] = NULL; + blksize_size[_major] = NULL; + hardsect_size[_major] = NULL; + _major = 0; + + DMINFO("cleaned up"); +} + +/* + * We have a lot of init/exit functions, so it seems easier to + * store them in an array. The disposable macro 'xx' + * expands a prefix into a pair of function names. + */ +static struct { + int (*init)(void); + void (*exit)(void); + +} _inits[] = { +#define xx(n) {n ## _init, n ## _exit}, + xx(local) + xx(dm_target) + xx(dm_linear) + xx(dm_stripe) + xx(dm_snapshot) + xx(dm_interface) +#undef xx +}; + +static int __init dm_init(void) +{ + const int count = sizeof(_inits) / sizeof(*_inits); + + int r, i; + + for (i = 0; i < count; i++) { + r = _inits[i].init(); + if (r) + goto bad; + } + + return 0; + + bad: + while (i--) + _inits[i].exit(); + + return r; +} + +static void __exit dm_exit(void) +{ + int i = sizeof(_inits) / sizeof(*_inits); + + dm_destroy_all(); + while (i--) + _inits[i].exit(); +} + +/* + * Block device functions + */ +static int dm_blk_open(struct inode *inode, struct file *file) +{ + struct mapped_device *md; + + md = dm_get_w(MINOR(inode->i_rdev)); + if (!md) + return -ENXIO; + + md->use_count++; + dm_put_w(md); + + return 0; +} + +static int dm_blk_close(struct inode *inode, struct file *file) +{ + struct mapped_device *md; + + md = dm_get_w(MINOR(inode->i_rdev)); + if (!md) + return -ENXIO; + + if (md->use_count < 1) + DMWARN("incorrect reference count found in mapped_device"); + + md->use_count--; + dm_put_w(md); + + return 0; +} + +/* In 512-byte units */ +#define VOLUME_SIZE(minor) (_block_size[(minor)] << 1) + +static int dm_blk_ioctl(struct inode *inode, struct file *file, + uint command, unsigned long a) +{ + int minor = MINOR(inode->i_rdev); + long size; + + if (minor >= MAX_DEVICES) + return -ENXIO; + + switch (command) { + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKFLSBUF: + case BLKSSZGET: + //case BLKRRPART: /* Re-read partition tables */ + //case BLKPG: + case BLKELVGET: + case BLKELVSET: + case BLKBSZGET: + case BLKBSZSET: + return blk_ioctl(inode->i_rdev, command, a); + break; + + case BLKGETSIZE: + size = VOLUME_SIZE(minor); + if (copy_to_user((void *) a, &size, sizeof(long))) + return -EFAULT; + break; + + case BLKGETSIZE64: + size = VOLUME_SIZE(minor); + if (put_user((u64) ((u64) size) << 9, (u64 *) a)) + return -EFAULT; + break; + + case BLKRRPART: + return -ENOTTY; + + case LV_BMAP: + return dm_user_bmap(inode, (struct lv_bmap *) a); + + default: + DMWARN("unknown block ioctl 0x%x", command); + return -ENOTTY; + } + + return 0; +} + +static inline struct io_hook *alloc_io_hook(void) +{ + return kmem_cache_alloc(_io_hook_cache, GFP_NOIO); +} + +static inline void free_io_hook(struct io_hook *ih) +{ + kmem_cache_free(_io_hook_cache, ih); +} + +/* + * FIXME: We need to decide if deferred_io's need + * their own slab, I say no for now since they are + * only used when the device is suspended. + */ +static inline struct deferred_io *alloc_deferred(void) +{ + return kmalloc(sizeof(struct deferred_io), GFP_NOIO); +} + +static inline void free_deferred(struct deferred_io *di) +{ + kfree(di); +} + +/* + * Call a target's optional error function if an I/O failed. + */ +static inline int call_err_fn(struct io_hook *ih, struct buffer_head *bh) +{ + dm_err_fn err = ih->target->type->err; + + if (err) + return err(bh, ih->rw, ih->target->private); + + return 0; +} + +/* + * bh->b_end_io routine that decrements the pending count + * and then calls the original bh->b_end_io fn. + */ +static void dec_pending(struct buffer_head *bh, int uptodate) +{ + struct io_hook *ih = bh->b_private; + + if (!uptodate && call_err_fn(ih, bh)) + return; + + if (atomic_dec_and_test(&ih->md->pending)) + /* nudge anyone waiting on suspend queue */ + wake_up(&ih->md->wait); + + bh->b_end_io = ih->end_io; + bh->b_private = ih->context; + free_io_hook(ih); + + bh->b_end_io(bh, uptodate); +} + +/* + * Add the bh to the list of deferred io. + */ +static int queue_io(struct buffer_head *bh, int rw) +{ + struct deferred_io *di = alloc_deferred(); + struct mapped_device *md; + + if (!di) + return -ENOMEM; + + md = dm_get_w(MINOR(bh->b_rdev)); + if (!md) { + free_deferred(di); + return -ENXIO; + } + + if (!md->suspended) { + dm_put_w(md); + free_deferred(di); + return 1; + } + + di->bh = bh; + di->rw = rw; + di->next = md->deferred; + md->deferred = di; + + dm_put_w(md); + + return 0; /* deferred successfully */ +} + +/* + * Do the bh mapping for a given leaf + */ +static inline int __map_buffer(struct mapped_device *md, + struct buffer_head *bh, int rw, int leaf) +{ + int r; + dm_map_fn fn; + void *context; + struct io_hook *ih = NULL; + struct target *ti = md->map->targets + leaf; + + fn = ti->type->map; + context = ti->private; + + ih = alloc_io_hook(); + + if (!ih) + return -1; + + ih->md = md; + ih->rw = rw; + ih->target = ti; + ih->end_io = bh->b_end_io; + ih->context = bh->b_private; + + r = fn(bh, rw, context); + + if (r > 0) { + /* hook the end io request fn */ + atomic_inc(&md->pending); + bh->b_end_io = dec_pending; + bh->b_private = ih; + + } else if (r == 0) + /* we don't need to hook */ + free_io_hook(ih); + + else if (r < 0) { + free_io_hook(ih); + return -1; + } + + return r; +} + +/* + * Search the btree for the correct target. + */ +static inline int __find_node(struct dm_table *t, struct buffer_head *bh) +{ + int l, n = 0, k = 0; + offset_t *node; + + for (l = 0; l < t->depth; l++) { + n = get_child(n, k); + node = get_node(t, l, n); + + for (k = 0; k < KEYS_PER_NODE; k++) + if (node[k] >= bh->b_rsector) + break; + } + + return (KEYS_PER_NODE * n) + k; +} + +static int request(request_queue_t * q, int rw, struct buffer_head *bh) +{ + struct mapped_device *md; + int r, minor = MINOR(bh->b_rdev); + unsigned int block_size = _blksize_size[minor]; + + md = dm_get_r(minor); + if (!md) { + buffer_IO_error(bh); + return 0; + } + + /* + * Sanity checks. + */ + if (bh->b_size > block_size) + DMERR("request is larger than block size " + "b_size (%d), block size (%d)", + bh->b_size, block_size); + + if (bh->b_rsector & ((bh->b_size >> 9) - 1)) + DMERR("misaligned block requested logical " + "sector (%lu), b_size (%d)", + bh->b_rsector, bh->b_size); + + /* + * If we're suspended we have to queue + * this io for later. + */ + while (md->suspended) { + dm_put_r(md); + + if (rw == READA) + goto bad_no_lock; + + r = queue_io(bh, rw); + + if (r < 0) + goto bad_no_lock; + + else if (r == 0) + return 0; /* deferred successfully */ + + /* + * We're in a while loop, because someone could suspend + * before we get to the following read lock. + */ + md = dm_get_r(minor); + if (!md) { + buffer_IO_error(bh); + return 0; + } + } + + if ((r = __map_buffer(md, bh, rw, __find_node(md->map, bh))) < 0) + goto bad; + + dm_put_r(md); + return r; + + bad: + dm_put_r(md); + + bad_no_lock: + buffer_IO_error(bh); + return 0; +} + +static int check_dev_size(int minor, unsigned long block) +{ + /* FIXME: check this */ + unsigned long max_sector = (_block_size[minor] << 1) + 1; + unsigned long sector = (block + 1) * (_blksize_size[minor] >> 9); + + return (sector > max_sector) ? 0 : 1; +} + +/* + * Creates a dummy buffer head and maps it (for lilo). + */ +static int do_bmap(kdev_t dev, unsigned long block, + kdev_t * r_dev, unsigned long *r_block) +{ + struct mapped_device *md; + struct buffer_head bh; + int minor = MINOR(dev), r; + struct target *t; + + md = dm_get_r(minor); + if (!md) + return -ENXIO; + + if (md->suspended) { + dm_put_r(md); + return -EPERM; + } + + if (!check_dev_size(minor, block)) { + dm_put_r(md); + return -EINVAL; + } + + /* setup dummy bh */ + memset(&bh, 0, sizeof(bh)); + bh.b_blocknr = block; + bh.b_dev = bh.b_rdev = dev; + bh.b_size = _blksize_size[minor]; + bh.b_rsector = block * (bh.b_size >> 9); + + /* find target */ + t = md->map->targets + __find_node(md->map, &bh); + + /* do the mapping */ + r = t->type->map(&bh, READ, t->private); + + *r_dev = bh.b_rdev; + *r_block = bh.b_rsector / (bh.b_size >> 9); + + dm_put_r(md); + return r; +} + +/* + * Marshals arguments and results between user and kernel space. + */ +static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb) +{ + unsigned long block, r_block; + kdev_t r_dev; + int r; + + if (get_user(block, &lvb->lv_block)) + return -EFAULT; + + if ((r = do_bmap(inode->i_rdev, block, &r_dev, &r_block))) + return r; + + if (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) || + put_user(r_block, &lvb->lv_block)) + return -EFAULT; + + return 0; +} + +/* + * See if the device with a specific minor # is free. The write + * lock is held when it returns successfully. + */ +static inline int specific_dev(int minor, struct mapped_device *md) +{ + if (minor >= MAX_DEVICES) { + DMWARN("request for a mapped_device beyond MAX_DEVICES (%d)", + MAX_DEVICES); + return -1; + } + + down_write(_dev_locks + minor); + if (_devs[minor]) { + /* in use */ + up_write(_dev_locks + minor); + return -1; + } + + return minor; +} + +/* + * Find the first free device. Again the write lock is held on + * success. + */ +static int any_old_dev(struct mapped_device *md) +{ + int i; + + for (i = 0; i < MAX_DEVICES; i++) + if (specific_dev(i, md) != -1) + return i; + + return -1; +} + +/* + * Allocate and initialise a blank device. + * Caller must ensure uuid is null-terminated. + * Device is returned with a write lock held. + */ +static struct mapped_device *alloc_dev(const char *name, const char *uuid, + int minor) +{ + struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL); + int len; + + if (!md) { + DMWARN("unable to allocate device, out of memory."); + return NULL; + } + + memset(md, 0, sizeof(*md)); + + /* + * This grabs the write lock if it succeeds. + */ + minor = (minor < 0) ? any_old_dev(md) : specific_dev(minor, md); + if (minor < 0) { + kfree(md); + return NULL; + } + + md->dev = MKDEV(_major, minor); + md->suspended = 0; + + strncpy(md->name, name, sizeof(md->name) - 1); + md->name[sizeof(md->name) - 1] = '\0'; + + /* + * Copy in the uuid. + */ + if (uuid && *uuid) { + len = strlen(uuid) + 1; + if (!(md->uuid = kmalloc(len, GFP_KERNEL))) { + DMWARN("unable to allocate uuid - out of memory."); + kfree(md); + return NULL; + } + strcpy(md->uuid, uuid); + } + + init_waitqueue_head(&md->wait); + return md; +} + +static int __register_device(struct mapped_device *md) +{ + md->devfs_entry = + devfs_register(_dev_dir, md->name, DEVFS_FL_CURRENT_OWNER, + MAJOR(md->dev), MINOR(md->dev), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + &dm_blk_dops, NULL); + + return 0; +} + +static int __unregister_device(struct mapped_device *md) +{ + devfs_unregister(md->devfs_entry); + return 0; +} + +/* + * The hardsect size for a mapped device is the largest hardsect size + * from the devices it maps onto. + */ +static int __find_hardsect_size(struct list_head *devices) +{ + int result = 512, size; + struct list_head *tmp; + + list_for_each(tmp, devices) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + size = get_hardsect_size(dd->dev); + if (size > result) + result = size; + } + + return result; +} + +/* + * Bind a table to the device. + */ +static int __bind(struct mapped_device *md, struct dm_table *t) +{ + int minor = MINOR(md->dev); + + md->map = t; + + if (!t->num_targets) { + _block_size[minor] = 0; + _blksize_size[minor] = BLOCK_SIZE; + _hardsect_size[minor] = 0; + return 0; + } + + /* in k */ + _block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1; + + _blksize_size[minor] = BLOCK_SIZE; + _hardsect_size[minor] = __find_hardsect_size(&t->devices); + register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]); + + return 0; +} + +static void __unbind(struct mapped_device *md) +{ + int minor = MINOR(md->dev); + + dm_table_destroy(md->map); + md->map = NULL; + + _block_size[minor] = 0; + _blksize_size[minor] = 0; + _hardsect_size[minor] = 0; +} + +static int check_name(const char *name) +{ + struct mapped_device *md; + + if (strchr(name, '/') || strlen(name) > DM_NAME_LEN) { + DMWARN("invalid device name"); + return -1; + } + + md = dm_get_name_r(name, DM_LOOKUP_BY_NAME); + if (md) { + dm_put_r(md); + DMWARN("device name already in use"); + return -1; + } + + return 0; +} + +static int check_uuid(const char *uuid) +{ + struct mapped_device *md; + + if (uuid) { + md = dm_get_name_r(uuid, DM_LOOKUP_BY_UUID); + if (md) { + dm_put_r(md); + DMWARN("device uuid already in use"); + return -1; + } + } + + return 0; +} + +/* + * Constructor for a new device. + */ +int dm_create(const char *name, const char *uuid, int minor, int ro, + struct dm_table *table) +{ + int r; + struct mapped_device *md; + + spin_lock(&_create_lock); + if (check_name(name) || check_uuid(uuid)) { + spin_unlock(&_create_lock); + return -EINVAL; + } + + md = alloc_dev(name, uuid, minor); + if (!md) { + spin_unlock(&_create_lock); + return -ENXIO; + } + minor = MINOR(md->dev); + _devs[minor] = md; + + r = __register_device(md); + if (r) + goto err; + + r = __bind(md, table); + if (r) + goto err; + + dm_set_ro(md, ro); + + spin_unlock(&_create_lock); + dm_put_w(md); + return 0; + + err: + _devs[minor] = NULL; + if (md->uuid) + kfree(md->uuid); + + dm_put_w(md); + kfree(md); + spin_unlock(&_create_lock); + return r; +} + +/* + * Renames the device. No lock held. + */ +int dm_set_name(const char *name, int nametype, const char *newname) +{ + int r; + struct mapped_device *md; + + spin_lock(&_create_lock); + if (check_name(newname) < 0) { + spin_unlock(&_create_lock); + return -EINVAL; + } + + md = dm_get_name_w(name, nametype); + if (!md) { + spin_unlock(&_create_lock); + return -ENXIO; + } + + r = __unregister_device(md); + if (r) + goto out; + + strcpy(md->name, newname); + r = __register_device(md); + + out: + dm_put_w(md); + spin_unlock(&_create_lock); + return r; +} + +/* + * Destructor for the device. You cannot destroy an open + * device. Write lock must be held before calling. + * Caller must dm_put_w(md) then kfree(md) if call was successful. + */ +int dm_destroy(struct mapped_device *md) +{ + int minor, r; + + if (md->use_count) + return -EPERM; + + r = __unregister_device(md); + if (r) + return r; + + minor = MINOR(md->dev); + _devs[minor] = NULL; + __unbind(md); + + if (md->uuid) + kfree(md->uuid); + + return 0; +} + +/* + * Destroy all devices - except open ones + */ +void dm_destroy_all(void) +{ + int i, some_destroyed, r; + struct mapped_device *md; + + do { + some_destroyed = 0; + for (i = 0; i < MAX_DEVICES; i++) { + md = dm_get_w(i); + if (!md) + continue; + + r = dm_destroy(md); + dm_put_w(md); + + if (!r) { + kfree(md); + some_destroyed = 1; + } + } + } while (some_destroyed); +} + +/* + * Sets or clears the read-only flag for the device. Write lock + * must be held. + */ +void dm_set_ro(struct mapped_device *md, int ro) +{ + md->read_only = ro; + set_device_ro(md->dev, ro); +} + +/* + * Requeue the deferred buffer_heads by calling generic_make_request. + */ +static void flush_deferred_io(struct deferred_io *c) +{ + struct deferred_io *n; + + while (c) { + n = c->next; + generic_make_request(c->rw, c->bh); + free_deferred(c); + c = n; + } +} + +/* + * Swap in a new table (destroying old one). Write lock must be + * held. + */ +int dm_swap_table(struct mapped_device *md, struct dm_table *table) +{ + int r; + + /* device must be suspended */ + if (!md->suspended) + return -EPERM; + + __unbind(md); + + r = __bind(md, table); + if (r) + return r; + + return 0; +} + +/* + * We need to be able to change a mapping table under a mounted + * filesystem. for example we might want to move some data in + * the background. Before the table can be swapped with + * dm_bind_table, dm_suspend must be called to flush any in + * flight buffer_heads and ensure that any further io gets + * deferred. Write lock must be held. + */ +int dm_suspend(struct mapped_device *md) +{ + int minor = MINOR(md->dev); + DECLARE_WAITQUEUE(wait, current); + + if (md->suspended) + return -EINVAL; + + md->suspended = 1; + dm_put_w(md); + + /* wait for all the pending io to flush */ + add_wait_queue(&md->wait, &wait); + current->state = TASK_UNINTERRUPTIBLE; + do { + md = dm_get_w(minor); + if (!md) { + /* Caller expects to free this lock. Yuck. */ + down_write(_dev_locks + minor); + return -ENXIO; + } + + if (!atomic_read(&md->pending)) + break; + + dm_put_w(md); + schedule(); + + } while (1); + + current->state = TASK_RUNNING; + remove_wait_queue(&md->wait, &wait); + + return 0; +} + +int dm_resume(struct mapped_device *md) +{ + int minor = MINOR(md->dev); + struct deferred_io *def; + + if (!md->suspended || !md->map->num_targets) + return -EINVAL; + + md->suspended = 0; + def = md->deferred; + md->deferred = NULL; + + dm_put_w(md); + flush_deferred_io(def); + run_task_queue(&tq_disk); + + if (!dm_get_w(minor)) { + /* FIXME: yuck */ + down_write(_dev_locks + minor); + return -ENXIO; + } + + return 0; +} + +struct block_device_operations dm_blk_dops = { + open: dm_blk_open, + release: dm_blk_close, + ioctl: dm_blk_ioctl, + owner: THIS_MODULE +}; + +/* + * module hooks + */ +module_init(dm_init); +module_exit(dm_exit); + +MODULE_PARM(major, "i"); +MODULE_PARM_DESC(major, "The major number of the device mapper"); +MODULE_DESCRIPTION(DM_NAME " driver"); +MODULE_AUTHOR("Joe Thornber "); +MODULE_LICENSE("GPL"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-exception-store.c linux.21rc1-ac2/drivers/md/dm-exception-store.c --- linux.21rc1/drivers/md/dm-exception-store.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-exception-store.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,724 @@ +/* + * dm-snapshot.c + * + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm-snapshot.h" +#include "kcopyd.h" +#include +#include + +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 + +/*----------------------------------------------------------------- + * Persistent snapshots, by persistent we mean that the snapshot + * will survive a reboot. + *---------------------------------------------------------------*/ + +/* + * We need to store a record of which parts of the origin have + * been copied to the snapshot device. The snapshot code + * requires that we copy exception chunks to chunk aligned areas + * of the COW store. It makes sense therefore, to store the + * metadata in chunk size blocks. + * + * There is no backward or forward compatibility implemented, + * snapshots with different disk versions than the kernel will + * not be usable. It is expected that "lvcreate" will blank out + * the start of a fresh COW device before calling the snapshot + * constructor. + * + * The first chunk of the COW device just contains the header. + * After this there is a chunk filled with exception metadata, + * followed by as many exception chunks as can fit in the + * metadata areas. + * + * All on disk structures are in little-endian format. The end + * of the exceptions info is indicated by an exception with a + * new_chunk of 0, which is invalid since it would point to the + * header chunk. + */ + +/* + * Magic for persistent snapshots: "SnAp" - Feeble isn't it. + */ +#define SNAP_MAGIC 0x70416e53 + +/* + * The on-disk version of the metadata. + */ +#define SNAPSHOT_DISK_VERSION 1 + +struct disk_header { + uint32_t magic; + + /* + * Is this snapshot valid. There is no way of recovering + * an invalid snapshot. + */ + int valid; + + /* + * Simple, incrementing version. no backward + * compatibility. + */ + uint32_t version; + + /* In sectors */ + uint32_t chunk_size; +}; + +struct disk_exception { + uint64_t old_chunk; + uint64_t new_chunk; +}; + +struct commit_callback { + void (*callback)(void *, int success); + void *context; +}; + +/* + * The top level structure for a persistent exception store. + */ +struct pstore { + struct dm_snapshot *snap; /* up pointer to my snapshot */ + int version; + int valid; + uint32_t chunk_size; + uint32_t exceptions_per_area; + + /* + * Now that we have an asynchronous kcopyd there is no + * need for large chunk sizes, so it wont hurt to have a + * whole chunks worth of metadata in memory at once. + */ + void *area; + struct kiobuf *iobuf; + + /* + * Used to keep track of which metadata area the data in + * 'chunk' refers to. + */ + uint32_t current_area; + + /* + * The next free chunk for an exception. + */ + uint32_t next_free; + + /* + * The index of next free exception in the current + * metadata area. + */ + uint32_t current_committed; + + atomic_t pending_count; + uint32_t callback_count; + struct commit_callback *callbacks; +}; + +/* + * For performance reasons we want to defer writing a committed + * exceptions metadata to disk so that we can amortise away this + * exensive operation. + * + * For the initial version of this code we will remain with + * synchronous io. There are some deadlock issues with async + * that I haven't yet worked out. + */ +static int do_io(int rw, struct kcopyd_region *where, struct kiobuf *iobuf) +{ + int i, sectors_per_block, nr_blocks, start; + int blocksize = get_hardsect_size(where->dev); + int status; + + sectors_per_block = blocksize / SECTOR_SIZE; + + nr_blocks = where->count / sectors_per_block; + start = where->sector / sectors_per_block; + + for (i = 0; i < nr_blocks; i++) + iobuf->blocks[i] = start++; + + iobuf->length = where->count << 9; + iobuf->locked = 1; + + status = brw_kiovec(rw, 1, &iobuf, where->dev, iobuf->blocks, + blocksize); + if (status != (where->count << 9)) + return -EIO; + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION ( 2, 4, 19) +/* + * FIXME: Remove once 2.4.19 has been released. + */ +struct page *vmalloc_to_page(void *vmalloc_addr) +{ + unsigned long addr = (unsigned long) vmalloc_addr; + struct page *page = NULL; + pmd_t *pmd; + pte_t *pte; + pgd_t *pgd; + + pgd = pgd_offset_k(addr); + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, addr); + if (!pmd_none(*pmd)) { + pte = pte_offset(pmd, addr); + if (pte_present(*pte)) { + page = pte_page(*pte); + } + } + } + return page; +} +#endif + +static int allocate_iobuf(struct pstore *ps) +{ + size_t i, r = -ENOMEM, len, nr_pages; + struct page *page; + + len = ps->chunk_size << SECTOR_SHIFT; + + /* + * Allocate the chunk_size block of memory that will hold + * a single metadata area. + */ + ps->area = vmalloc(len); + if (!ps->area) + return r; + + if (alloc_kiovec(1, &ps->iobuf)) + goto bad; + + nr_pages = ps->chunk_size / (PAGE_SIZE / SECTOR_SIZE); + r = expand_kiobuf(ps->iobuf, nr_pages); + if (r) + goto bad; + + /* + * We lock the pages for ps->area into memory since they'll be + * doing a lot of io. + */ + for (i = 0; i < nr_pages; i++) { + page = vmalloc_to_page(ps->area + (i * PAGE_SIZE)); + LockPage(page); + ps->iobuf->maplist[i] = page; + ps->iobuf->nr_pages++; + } + + ps->iobuf->nr_pages = nr_pages; + ps->iobuf->offset = 0; + + return 0; + + bad: + if (ps->iobuf) + free_kiovec(1, &ps->iobuf); + + if (ps->area) + vfree(ps->area); + ps->iobuf = NULL; + return r; +} + +static void free_iobuf(struct pstore *ps) +{ + int i; + + for (i = 0; i < ps->iobuf->nr_pages; i++) + UnlockPage(ps->iobuf->maplist[i]); + ps->iobuf->locked = 0; + + free_kiovec(1, &ps->iobuf); + vfree(ps->area); +} + +/* + * Read or write a chunk aligned and sized block of data from a device. + */ +static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) +{ + int r; + struct kcopyd_region where; + + where.dev = ps->snap->cow->dev; + where.sector = ps->chunk_size * chunk; + where.count = ps->chunk_size; + + r = do_io(rw, &where, ps->iobuf); + if (r) + return r; + + return 0; +} + +/* + * Read or write a metadata area. Remembering to skip the first + * chunk which holds the header. + */ +static int area_io(struct pstore *ps, uint32_t area, int rw) +{ + int r; + uint32_t chunk; + + /* convert a metadata area index to a chunk index */ + chunk = 1 + ((ps->exceptions_per_area + 1) * area); + + r = chunk_io(ps, chunk, rw); + if (r) + return r; + + ps->current_area = area; + return 0; +} + +static int zero_area(struct pstore *ps, uint32_t area) +{ + memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); + return area_io(ps, area, WRITE); +} + +static int read_header(struct pstore *ps, int *new_snapshot) +{ + int r; + struct disk_header *dh; + + r = chunk_io(ps, 0, READ); + if (r) + return r; + + dh = (struct disk_header *) ps->area; + + if (dh->magic == 0) { + *new_snapshot = 1; + + } else if (dh->magic == SNAP_MAGIC) { + *new_snapshot = 0; + ps->valid = dh->valid; + ps->version = dh->version; + ps->chunk_size = dh->chunk_size; + + } else { + DMWARN("Invalid/corrupt snapshot"); + r = -ENXIO; + } + + return r; +} + +static int write_header(struct pstore *ps) +{ + struct disk_header *dh; + + memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); + + dh = (struct disk_header *) ps->area; + dh->magic = SNAP_MAGIC; + dh->valid = ps->valid; + dh->version = ps->version; + dh->chunk_size = ps->chunk_size; + + return chunk_io(ps, 0, WRITE); +} + +/* + * Access functions for the disk exceptions, these do the endian conversions. + */ +static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) +{ + if (index >= ps->exceptions_per_area) + return NULL; + + return ((struct disk_exception *) ps->area) + index; +} + +static int read_exception(struct pstore *ps, + uint32_t index, struct disk_exception *result) +{ + struct disk_exception *e; + + e = get_exception(ps, index); + if (!e) + return -EINVAL; + + /* copy it */ + result->old_chunk = le64_to_cpu(e->old_chunk); + result->new_chunk = le64_to_cpu(e->new_chunk); + + return 0; +} + +static int write_exception(struct pstore *ps, + uint32_t index, struct disk_exception *de) +{ + struct disk_exception *e; + + e = get_exception(ps, index); + if (!e) + return -EINVAL; + + /* copy it */ + e->old_chunk = cpu_to_le64(de->old_chunk); + e->new_chunk = cpu_to_le64(de->new_chunk); + + return 0; +} + +/* + * Registers the exceptions that are present in the current area. + * 'full' is filled in to indicate if the area has been + * filled. + */ +static int insert_exceptions(struct pstore *ps, int *full) +{ + int i, r; + struct disk_exception de; + + /* presume the area is full */ + *full = 1; + + for (i = 0; i < ps->exceptions_per_area; i++) { + r = read_exception(ps, i, &de); + + if (r) + return r; + + /* + * If the new_chunk is pointing at the start of + * the COW device, where the first metadata area + * is we know that we've hit the end of the + * exceptions. Therefore the area is not full. + */ + if (de.new_chunk == 0LL) { + ps->current_committed = i; + *full = 0; + break; + } + + /* + * Keep track of the start of the free chunks. + */ + if (ps->next_free <= de.new_chunk) + ps->next_free = de.new_chunk + 1; + + /* + * Otherwise we add the exception to the snapshot. + */ + r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk); + if (r) + return r; + } + + return 0; +} + +static int read_exceptions(struct pstore *ps) +{ + uint32_t area; + int r, full = 1; + + /* + * Keeping reading chunks and inserting exceptions until + * we find a partially full area. + */ + for (area = 0; full; area++) { + r = area_io(ps, area, READ); + if (r) + return r; + + r = insert_exceptions(ps, &full); + if (r) + return r; + + area++; + } + + return 0; +} + +static inline struct pstore *get_info(struct exception_store *store) +{ + return (struct pstore *) store->context; +} + +static int persistent_percentfull(struct exception_store *store) +{ + struct pstore *ps = get_info(store); + return (ps->next_free * store->snap->chunk_size * 100) / + get_dev_size(store->snap->cow->dev); +} + +static void persistent_destroy(struct exception_store *store) +{ + struct pstore *ps = get_info(store); + + vfree(ps->callbacks); + free_iobuf(ps); + kfree(ps); +} + +static int persistent_prepare(struct exception_store *store, + struct exception *e) +{ + struct pstore *ps = get_info(store); + uint32_t stride; + offset_t size = get_dev_size(store->snap->cow->dev); + + /* Is there enough room ? */ + if (size <= (ps->next_free * store->snap->chunk_size)) + return -ENOSPC; + + e->new_chunk = ps->next_free; + + /* + * Move onto the next free pending, making sure to take + * into account the location of the metadata chunks. + */ + stride = (ps->exceptions_per_area + 1); + if (!(++ps->next_free % stride)) + ps->next_free++; + + atomic_inc(&ps->pending_count); + return 0; +} + +static void persistent_commit(struct exception_store *store, + struct exception *e, + void (*callback) (void *, int success), + void *callback_context) +{ + int r, i; + struct pstore *ps = get_info(store); + struct disk_exception de; + struct commit_callback *cb; + + de.old_chunk = e->old_chunk; + de.new_chunk = e->new_chunk; + write_exception(ps, ps->current_committed++, &de); + + /* + * Add the callback to the back of the array. This code + * is the only place where the callback array is + * manipulated, and we know that it will never be called + * multiple times concurrently. + */ + cb = ps->callbacks + ps->callback_count++; + cb->callback = callback; + cb->context = callback_context; + + /* + * If there are no more exceptions in flight, or we have + * filled this metadata area we commit the exceptions to + * disk. + */ + if (atomic_dec_and_test(&ps->pending_count) || + (ps->current_committed == ps->exceptions_per_area)) { + r = area_io(ps, ps->current_area, WRITE); + if (r) + ps->valid = 0; + + for (i = 0; i < ps->callback_count; i++) { + cb = ps->callbacks + i; + cb->callback(cb->context, r == 0 ? 1 : 0); + } + + ps->callback_count = 0; + } + + /* + * Have we completely filled the current area ? + */ + if (ps->current_committed == ps->exceptions_per_area) { + ps->current_committed = 0; + r = zero_area(ps, ps->current_area + 1); + if (r) + ps->valid = 0; + } +} + +static void persistent_drop(struct exception_store *store) +{ + struct pstore *ps = get_info(store); + + ps->valid = 0; + if (write_header(ps)) + DMWARN("write header failed"); +} + +int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) +{ + int r, new_snapshot; + struct pstore *ps; + + /* allocate the pstore */ + ps = kmalloc(sizeof(*ps), GFP_KERNEL); + if (!ps) + return -ENOMEM; + + ps->snap = store->snap; + ps->valid = 1; + ps->version = SNAPSHOT_DISK_VERSION; + ps->chunk_size = chunk_size; + ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) / + sizeof(struct disk_exception); + ps->next_free = 2; /* skipping the header and first area */ + ps->current_committed = 0; + + r = allocate_iobuf(ps); + if (r) + goto bad; + + /* + * Allocate space for all the callbacks. + */ + ps->callback_count = 0; + atomic_set(&ps->pending_count, 0); + ps->callbacks = vcalloc(ps->exceptions_per_area, + sizeof(*ps->callbacks)); + + if (!ps->callbacks) + goto bad; + + /* + * Read the snapshot header. + */ + r = read_header(ps, &new_snapshot); + if (r) + goto bad; + + /* + * Do we need to setup a new snapshot ? + */ + if (new_snapshot) { + r = write_header(ps); + if (r) { + DMWARN("write_header failed"); + goto bad; + } + + r = zero_area(ps, 0); + if (r) { + DMWARN("zero_area(0) failed"); + goto bad; + } + + } else { + /* + * Sanity checks. + */ + if (ps->chunk_size != chunk_size) { + DMWARN("chunk size for existing snapshot different " + "from that requested"); + r = -EINVAL; + goto bad; + } + + if (ps->version != SNAPSHOT_DISK_VERSION) { + DMWARN("unable to handle snapshot disk version %d", + ps->version); + r = -EINVAL; + goto bad; + } + + /* + * Read the metadata. + */ + r = read_exceptions(ps); + if (r) + goto bad; + } + + store->destroy = persistent_destroy; + store->prepare_exception = persistent_prepare; + store->commit_exception = persistent_commit; + store->drop_snapshot = persistent_drop; + store->percent_full = persistent_percentfull; + store->context = ps; + + return r; + + bad: + if (ps) { + if (ps->callbacks) + vfree(ps->callbacks); + + if (ps->iobuf) + free_iobuf(ps); + + kfree(ps); + } + return r; +} + +/*----------------------------------------------------------------- + * Implementation of the store for non-persistent snapshots. + *---------------------------------------------------------------*/ +struct transient_c { + offset_t next_free; +}; + +void transient_destroy(struct exception_store *store) +{ + kfree(store->context); +} + +int transient_prepare(struct exception_store *store, struct exception *e) +{ + struct transient_c *tc = (struct transient_c *) store->context; + offset_t size = get_dev_size(store->snap->cow->dev); + + if (size < (tc->next_free + store->snap->chunk_size)) + return -1; + + e->new_chunk = sector_to_chunk(store->snap, tc->next_free); + tc->next_free += store->snap->chunk_size; + + return 0; +} + +void transient_commit(struct exception_store *store, + struct exception *e, + void (*callback) (void *, int success), + void *callback_context) +{ + /* Just succeed */ + callback(callback_context, 1); +} + +static int transient_percentfull(struct exception_store *store) +{ + struct transient_c *tc = (struct transient_c *) store->context; + return (tc->next_free * 100) / get_dev_size(store->snap->cow->dev); +} + +int dm_create_transient(struct exception_store *store, + struct dm_snapshot *s, int blocksize, void **error) +{ + struct transient_c *tc; + + memset(store, 0, sizeof(*store)); + store->destroy = transient_destroy; + store->prepare_exception = transient_prepare; + store->commit_exception = transient_commit; + store->percent_full = transient_percentfull; + store->snap = s; + + tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + tc->next_free = 0; + store->context = tc; + + return 0; +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm.h linux.21rc1-ac2/drivers/md/dm.h --- linux.21rc1/drivers/md/dm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm.h 2003-04-25 16:05:08.000000000 +0100 @@ -0,0 +1,241 @@ +/* + * Internal header file for device mapper + * + * Copyright (C) 2001 Sistina Software + * + * This file is released under the LGPL. + */ + +#ifndef DM_INTERNAL_H +#define DM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DM_NAME "device-mapper" /* Name for messaging */ +#define DM_DRIVER_EMAIL "lvm-devel@lists.sistina.com" +#define MAX_DEPTH 16 +#define NODE_SIZE L1_CACHE_BYTES +#define KEYS_PER_NODE (NODE_SIZE / sizeof(offset_t)) +#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1) +#define MAX_ARGS 32 +#define MAX_DEVICES 256 + +/* + * List of devices that a metadevice uses and should open/close. + */ +struct dm_dev { + atomic_t count; + struct list_head list; + + int mode; + + kdev_t dev; + struct block_device *bd; +}; + +/* + * I/O that had to be deferred while we were suspended + */ +struct deferred_io { + int rw; + struct buffer_head *bh; + struct deferred_io *next; +}; + +/* + * Btree leaf - this does the actual mapping + */ +struct target { + struct target_type *type; + void *private; +}; + +/* + * The btree + */ +struct dm_table { + /* btree table */ + int depth; + int counts[MAX_DEPTH]; /* in nodes */ + offset_t *index[MAX_DEPTH]; + + int num_targets; + int num_allocated; + offset_t *highs; + struct target *targets; + + /* + * Indicates the rw permissions for the new logical + * device. This should be a combination of FMODE_READ + * and FMODE_WRITE. + */ + int mode; + + /* a list of devices used by this table */ + struct list_head devices; + + /* + * A waitqueue for processes waiting for something + * interesting to happen to this table. + */ + wait_queue_head_t eventq; +}; + +/* + * The actual device struct + */ +struct mapped_device { + kdev_t dev; + char name[DM_NAME_LEN]; + char *uuid; + + int use_count; + int suspended; + int read_only; + + /* a list of io's that arrived while we were suspended */ + atomic_t pending; + wait_queue_head_t wait; + struct deferred_io *deferred; + + struct dm_table *map; + + /* used by dm-fs.c */ + devfs_handle_t devfs_entry; +}; + +extern struct block_device_operations dm_blk_dops; + +/* dm-target.c */ +int dm_target_init(void); +struct target_type *dm_get_target_type(const char *name); +void dm_put_target_type(struct target_type *t); +void dm_target_exit(void); + +/* + * Destructively splits argument list to pass to ctr. + */ +int split_args(int max, int *argc, char **argv, char *input); + +/* dm.c */ +struct mapped_device *dm_get_r(int minor); +struct mapped_device *dm_get_w(int minor); + +/* + * There are two ways to lookup a device. + */ +enum { + DM_LOOKUP_BY_NAME, + DM_LOOKUP_BY_UUID +}; + +struct mapped_device *dm_get_name_r(const char *name, int nametype); +struct mapped_device *dm_get_name_w(const char *name, int nametype); + +void dm_put_r(struct mapped_device *md); +void dm_put_w(struct mapped_device *md); + +/* + * Call with no lock. + */ +int dm_create(const char *name, const char *uuid, int minor, int ro, + struct dm_table *table); +int dm_set_name(const char *name, int nametype, const char *newname); +void dm_destroy_all(void); + +/* + * You must have the write lock before calling the remaining md + * methods. + */ +int dm_destroy(struct mapped_device *md); +void dm_set_ro(struct mapped_device *md, int ro); + +/* + * The device must be suspended before calling this method. + */ +int dm_swap_table(struct mapped_device *md, struct dm_table *t); + +/* + * A device can still be used while suspended, but I/O is deferred. + */ +int dm_suspend(struct mapped_device *md); +int dm_resume(struct mapped_device *md); + +/* dm-table.c */ +int dm_table_create(struct dm_table **result, int mode); +void dm_table_destroy(struct dm_table *t); + +int dm_table_add_target(struct dm_table *t, offset_t highs, + struct target_type *type, void *private); +int dm_table_complete(struct dm_table *t); + +/* + * Event handling + */ +void dm_table_event(struct dm_table *t); + +#define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x) +#define DMERR(f, x...) printk(KERN_ERR DM_NAME ": " f "\n" , ## x) +#define DMINFO(f, x...) printk(KERN_INFO DM_NAME ": " f "\n" , ## x) + +/* + * Calculate the index of the child node of the n'th node k'th key. + */ +static inline int get_child(int n, int k) +{ + return (n * CHILDREN_PER_NODE) + k; +} + +/* + * Return the n'th node of level l from table t. + */ +static inline offset_t *get_node(struct dm_table *t, int l, int n) +{ + return t->index[l] + (n * KEYS_PER_NODE); +} + +static inline int array_too_big(unsigned long fixed, unsigned long obj, + unsigned long num) +{ + return (num > (ULONG_MAX - fixed) / obj); +} + + +/* + * Targets + */ +int dm_linear_init(void); +void dm_linear_exit(void); + +int dm_stripe_init(void); +void dm_stripe_exit(void); + +int dm_snapshot_init(void); +void dm_snapshot_exit(void); + + +/* + * Init functions for the user interface to device-mapper. At + * the moment an ioctl interface on a special char device is + * used. A filesystem based interface would be a nicer way to + * go. + */ +int __init dm_interface_init(void); +void dm_interface_exit(void); + + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-ioctl.c linux.21rc1-ac2/drivers/md/dm-ioctl.c --- linux.21rc1/drivers/md/dm-ioctl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-ioctl.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include +#include + +/*----------------------------------------------------------------- + * Implementation of the ioctl commands + *---------------------------------------------------------------*/ + +/* + * All the ioctl commands get dispatched to functions with this + * prototype. + */ +typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user); + +/* + * This is really a debug only call. + */ +static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user) +{ + dm_destroy_all(); + return 0; +} + +/* + * Check a string doesn't overrun the chunk of + * memory we copied from userland. + */ +static int valid_str(char *str, void *begin, void *end) +{ + while (((void *) str >= begin) && ((void *) str < end)) + if (!*str++) + return 0; + + return -EINVAL; +} + +static int next_target(struct dm_target_spec *last, uint32_t next, + void *begin, void *end, + struct dm_target_spec **spec, char **params) +{ + *spec = (struct dm_target_spec *) + ((unsigned char *) last + next); + *params = (char *) (*spec + 1); + + if (*spec < (last + 1) || ((void *) *spec > end)) + return -EINVAL; + + return valid_str(*params, begin, end); +} + +/* + * Checks to see if there's a gap in the table. + * Returns true iff there is a gap. + */ +static int gap(struct dm_table *table, struct dm_target_spec *spec) +{ + if (!table->num_targets) + return (spec->sector_start > 0) ? 1 : 0; + + if (spec->sector_start != table->highs[table->num_targets - 1] + 1) + return 1; + + return 0; +} + +static int populate_table(struct dm_table *table, struct dm_ioctl *args) +{ + int i = 0, r, first = 1, argc; + struct dm_target_spec *spec; + char *params, *argv[MAX_ARGS]; + struct target_type *ttype; + void *context, *begin, *end; + offset_t highs = 0; + + if (!args->target_count) { + DMWARN("populate_table: no targets specified"); + return -EINVAL; + } + + begin = (void *) args; + end = begin + args->data_size; + +#define PARSE_ERROR(msg) {DMWARN(msg); return -EINVAL;} + + for (i = 0; i < args->target_count; i++) { + + if (first) + r = next_target((struct dm_target_spec *) args, + args->data_start, + begin, end, &spec, ¶ms); + else + r = next_target(spec, spec->next, begin, end, + &spec, ¶ms); + + if (r) + PARSE_ERROR("unable to find target"); + + /* Look up the target type */ + ttype = dm_get_target_type(spec->target_type); + if (!ttype) + PARSE_ERROR("unable to find target type"); + + if (gap(table, spec)) + PARSE_ERROR("gap in target ranges"); + + /* Split up the parameter list */ + if (split_args(MAX_ARGS, &argc, argv, params) < 0) + PARSE_ERROR("Too many arguments"); + + /* Build the target */ + if (ttype->ctr(table, spec->sector_start, spec->length, + argc, argv, &context)) { + DMWARN("%s: target constructor failed", + (char *) context); + return -EINVAL; + } + + /* Add the target to the table */ + highs = spec->sector_start + (spec->length - 1); + if (dm_table_add_target(table, highs, ttype, context)) + PARSE_ERROR("internal error adding target to table"); + + first = 0; + } + +#undef PARSE_ERROR + + r = dm_table_complete(table); + return r; +} + +/* + * Round up the ptr to the next 'align' boundary. Obviously + * 'align' must be a power of 2. + */ +static inline void *align_ptr(void *ptr, unsigned int align) +{ + align--; + return (void *) (((unsigned long) (ptr + align)) & ~align); +} + +/* + * Copies a dm_ioctl and an optional additional payload to + * userland. + */ +static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param, + void *data, uint32_t len) +{ + int r; + void *ptr = NULL; + + if (data) { + ptr = align_ptr(user + 1, sizeof(unsigned long)); + param->data_start = ptr - (void *) user; + } + + /* + * The version number has already been filled in, so we + * just copy later fields. + */ + r = copy_to_user(&user->data_size, ¶m->data_size, + sizeof(*param) - sizeof(param->version)); + if (r) + return -EFAULT; + + if (data) { + if (param->data_start + len > param->data_size) + return -ENOSPC; + + if (copy_to_user(ptr, data, len)) + r = -EFAULT; + } + + return r; +} + +/* + * Fills in a dm_ioctl structure, ready for sending back to + * userland. + */ +static void __info(struct mapped_device *md, struct dm_ioctl *param) +{ + param->flags = DM_EXISTS_FLAG; + if (md->suspended) + param->flags |= DM_SUSPEND_FLAG; + if (md->read_only) + param->flags |= DM_READONLY_FLAG; + + strncpy(param->name, md->name, sizeof(param->name)); + + if (md->uuid) + strncpy(param->uuid, md->uuid, sizeof(param->uuid) - 1); + else + param->uuid[0] = '\0'; + + param->open_count = md->use_count; + param->dev = kdev_t_to_nr(md->dev); + param->target_count = md->map->num_targets; +} + +/* + * Always use UUID for lookups if it's present, otherwise use name. + */ +static inline char *lookup_name(struct dm_ioctl *param) +{ + return (*param->uuid) ? param->uuid : param->name; +} + +static inline int lookup_type(struct dm_ioctl *param) +{ + return (*param->uuid) ? DM_LOOKUP_BY_UUID : DM_LOOKUP_BY_NAME; +} + +#define ALIGNMENT sizeof(int) +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long align = --a; + + return (void *) (((unsigned long) ptr + align) & ~align); +} + +/* + * Copies device info back to user space, used by + * the create and info ioctls. + */ +static int info(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + + param->flags = 0; + + md = dm_get_name_r(lookup_name(param), lookup_type(param)); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + + __info(md, param); + dm_put_r(md); + + out: + return results_to_user(user, param, NULL, 0); +} + +static inline int get_mode(struct dm_ioctl *param) +{ + int mode = FMODE_READ | FMODE_WRITE; + + if (param->flags & DM_READONLY_FLAG) + mode = FMODE_READ; + + return mode; +} + +static int create(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r, ro; + struct dm_table *t; + int minor; + + r = dm_table_create(&t, get_mode(param)); + if (r) + return r; + + r = populate_table(t, param); + if (r) { + dm_table_destroy(t); + return r; + } + + minor = (param->flags & DM_PERSISTENT_DEV_FLAG) ? + MINOR(to_kdev_t(param->dev)) : -1; + + ro = (param->flags & DM_READONLY_FLAG) ? 1 : 0; + + r = dm_create(param->name, param->uuid, minor, ro, t); + if (r) { + dm_table_destroy(t); + return r; + } + + r = info(param, user); + return r; +} + + + +/* + * Build up the status struct for each target + */ +static int __status(struct mapped_device *md, struct dm_ioctl *param, + char *outbuf, int *len) +{ + int i; + struct dm_target_spec *spec; + uint64_t sector = 0LL; + char *outptr; + status_type_t type; + + if (param->flags & DM_STATUS_TABLE_FLAG) + type = STATUSTYPE_TABLE; + else + type = STATUSTYPE_INFO; + + outptr = outbuf; + + /* Get all the target info */ + for (i = 0; i < md->map->num_targets; i++) { + struct target_type *tt = md->map->targets[i].type; + offset_t high = md->map->highs[i]; + + if (outptr - outbuf + + sizeof(struct dm_target_spec) > param->data_size) + return -ENOMEM; + + spec = (struct dm_target_spec *) outptr; + + spec->status = 0; + spec->sector_start = sector; + spec->length = high - sector + 1; + strncpy(spec->target_type, tt->name, sizeof(spec->target_type)); + + outptr += sizeof(struct dm_target_spec); + + /* Get the status/table string from the target driver */ + if (tt->status) + tt->status(type, outptr, + outbuf + param->data_size - outptr, + md->map->targets[i].private); + else + outptr[0] = '\0'; + + outptr += strlen(outptr) + 1; + _align(outptr, ALIGNMENT); + + sector = high + 1; + + spec->next = outptr - outbuf; + } + + param->target_count = md->map->num_targets; + *len = outptr - outbuf; + + return 0; +} + +/* + * Return the status of a device as a text string for each + * target. + */ +static int get_status(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + int len = 0; + int ret; + char *outbuf = NULL; + + md = dm_get_name_r(lookup_name(param), lookup_type(param)); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + + /* We haven't a clue how long the resultant data will be so + just allocate as much as userland has allowed us and make sure + we don't overun it */ + outbuf = kmalloc(param->data_size, GFP_KERNEL); + if (!outbuf) + goto out; + /* + * Get the status of all targets + */ + __status(md, param, outbuf, &len); + + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + out: + if (md) + dm_put_r(md); + + ret = results_to_user(user, param, outbuf, len); + + if (outbuf) + kfree(outbuf); + + return ret; +} + +/* + * Wait for a device to report an event + */ +static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user) +{ + struct mapped_device *md; + DECLARE_WAITQUEUE(wq, current); + + md = dm_get_name_r(lookup_name(param), lookup_type(param)); + if (!md) + /* + * Device not found - returns cleared exists flag. + */ + goto out; + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + /* + * Wait for a notification event + */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&md->map->eventq, &wq); + + dm_put_r(md); + + schedule(); + set_current_state(TASK_RUNNING); + + out: + return results_to_user(user, param, NULL, 0); +} + +/* + * Retrieves a list of devices used by a particular dm device. + */ +static int dep(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int count, r; + struct mapped_device *md; + struct list_head *tmp; + size_t len = 0; + struct dm_target_deps *deps = NULL; + + md = dm_get_name_r(lookup_name(param), lookup_type(param)); + if (!md) + goto out; + + /* + * Setup the basic dm_ioctl structure. + */ + __info(md, param); + + /* + * Count the devices. + */ + count = 0; + list_for_each(tmp, &md->map->devices) + count++; + + /* + * Allocate a kernel space version of the dm_target_status + * struct. + */ + if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) { + dm_put_r(md); + return -ENOMEM; + } + + len = sizeof(*deps) + (sizeof(*deps->dev) * count); + deps = kmalloc(len, GFP_KERNEL); + if (!deps) { + dm_put_r(md); + return -ENOMEM; + } + + /* + * Fill in the devices. + */ + deps->count = count; + count = 0; + list_for_each(tmp, &md->map->devices) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + deps->dev[count++] = kdev_t_to_nr(dd->dev); + } + dm_put_r(md); + + out: + r = results_to_user(user, param, deps, len); + + kfree(deps); + return r; +} + +static int remove(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct mapped_device *md; + + md = dm_get_name_w(lookup_name(param), lookup_type(param)); + if (!md) + return -ENXIO; + + r = dm_destroy(md); + dm_put_w(md); + if (!r) + kfree(md); + + return r; +} + +static int suspend(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct mapped_device *md; + + md = dm_get_name_w(lookup_name(param), lookup_type(param)); + if (!md) + return -ENXIO; + + r = (param->flags & DM_SUSPEND_FLAG) ? dm_suspend(md) : dm_resume(md); + dm_put_w(md); + + return r; +} + +static int reload(struct dm_ioctl *param, struct dm_ioctl *user) +{ + int r; + struct mapped_device *md; + struct dm_table *t; + + r = dm_table_create(&t, get_mode(param)); + if (r) + return r; + + r = populate_table(t, param); + if (r) { + dm_table_destroy(t); + return r; + } + + md = dm_get_name_w(lookup_name(param), lookup_type(param)); + if (!md) { + dm_table_destroy(t); + return -ENXIO; + } + + r = dm_swap_table(md, t); + if (r) { + dm_put_w(md); + dm_table_destroy(t); + return r; + } + + dm_set_ro(md, (param->flags & DM_READONLY_FLAG) ? 1 : 0); + dm_put_w(md); + + r = info(param, user); + return r; +} + +static int rename(struct dm_ioctl *param, struct dm_ioctl *user) +{ + char *newname = (char *) param + param->data_start; + + if (valid_str(newname, (void *) param, + (void *) param + param->data_size) || + dm_set_name(lookup_name(param), lookup_type(param), newname)) { + DMWARN("Invalid new logical volume name supplied."); + return -EINVAL; + } + + return 0; +} + + +/*----------------------------------------------------------------- + * Implementation of open/close/ioctl on the special char + * device. + *---------------------------------------------------------------*/ +static int ctl_open(struct inode *inode, struct file *file) +{ + /* only root can open this */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int ctl_close(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static ioctl_fn lookup_ioctl(unsigned int cmd) +{ + static struct { + int cmd; + ioctl_fn fn; + } _ioctls[] = { + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ + {DM_REMOVE_ALL_CMD, remove_all}, + {DM_DEV_CREATE_CMD, create}, + {DM_DEV_REMOVE_CMD, remove}, + {DM_DEV_RELOAD_CMD, reload}, + {DM_DEV_RENAME_CMD, rename}, + {DM_DEV_SUSPEND_CMD, suspend}, + {DM_DEV_DEPS_CMD, dep}, + {DM_DEV_STATUS_CMD, info}, + {DM_TARGET_STATUS_CMD, get_status}, + {DM_TARGET_WAIT_CMD, wait_device_event}, + }; + static int nelts = sizeof(_ioctls) / sizeof(*_ioctls); + + return (cmd >= nelts) ? NULL : _ioctls[cmd].fn; +} + +/* + * As well as checking the version compatibility this always + * copies the kernel interface version out. + */ +static int check_version(int cmd, struct dm_ioctl *user) +{ + uint32_t version[3]; + int r = 0; + + if (copy_from_user(version, user->version, sizeof(version))) + return -EFAULT; + + if ((DM_VERSION_MAJOR != version[0]) || + (DM_VERSION_MINOR < version[1])) { + DMWARN("ioctl interface mismatch: " + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DM_VERSION_MAJOR, DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL, + version[0], version[1], version[2], cmd); + r = -EINVAL; + } + + /* + * Fill in the kernel version. + */ + version[0] = DM_VERSION_MAJOR; + version[1] = DM_VERSION_MINOR; + version[2] = DM_VERSION_PATCHLEVEL; + if (copy_to_user(user->version, version, sizeof(version))) + return -EFAULT; + + return r; +} + +static void free_params(struct dm_ioctl *param) +{ + vfree(param); +} + +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) +{ + struct dm_ioctl tmp, *dmi; + + if (copy_from_user(&tmp, user, sizeof(tmp))) + return -EFAULT; + + if (tmp.data_size < sizeof(tmp) || tmp.data_size > 65536) + return -EINVAL; + + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); + if (!dmi) + return -ENOMEM; + + if (copy_from_user(dmi, user, tmp.data_size)) { + vfree(dmi); + return -EFAULT; + } + + *param = dmi; + return 0; +} + +static int validate_params(uint cmd, struct dm_ioctl *param) +{ + /* Ignores parameters */ + if (cmd == DM_REMOVE_ALL_CMD) + return 0; + + /* Unless creating, either name of uuid but not both */ + if (cmd != DM_DEV_CREATE_CMD) { + if ((!*param->uuid && !*param->name) || + (*param->uuid && *param->name)) { + DMWARN("one of name or uuid must be supplied"); + return -EINVAL; + } + } + + /* Ensure strings are terminated */ + param->name[DM_NAME_LEN - 1] = '\0'; + param->uuid[DM_UUID_LEN - 1] = '\0'; + + return 0; +} + +static int ctl_ioctl(struct inode *inode, struct file *file, + uint command, ulong u) +{ + + int r = 0, cmd; + struct dm_ioctl *param; + struct dm_ioctl *user = (struct dm_ioctl *) u; + ioctl_fn fn = NULL; + + if (_IOC_TYPE(command) != DM_IOCTL) + return -ENOTTY; + + cmd = _IOC_NR(command); + + /* + * Check the interface version passed in. This also + * writes out the kernel's interface version. + */ + r = check_version(cmd, user); + if (r) + return r; + + /* + * Nothing more to do for the version command. + */ + if (cmd == DM_VERSION_CMD) + return 0; + + fn = lookup_ioctl(cmd); + if (!fn) { + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); + return -ENOTTY; + } + + /* + * Copy the parameters into kernel space. + */ + r = copy_params(user, ¶m); + if (r) + return r; + + r = validate_params(cmd, param); + if (r) { + free_params(param); + return r; + } + + r = fn(param, user); + free_params(param); + return r; +} + +static struct file_operations _ctl_fops = { + open: ctl_open, + release: ctl_close, + ioctl: ctl_ioctl, + owner: THIS_MODULE, +}; + +static devfs_handle_t _ctl_handle; + +static struct miscdevice _dm_misc = { + minor: MISC_DYNAMIC_MINOR, + name: DM_NAME, + fops: &_ctl_fops +}; + +static int __init dm_devfs_init(void) { + int r; + char rname[64]; + + r = devfs_generate_path(_dm_misc.devfs_handle, rname + 3, + sizeof rname - 3); + if (r == -ENOSYS) + return 0; /* devfs not present */ + + if (r < 0) { + DMERR("devfs_generate_path failed for control device"); + return r; + } + + strncpy(rname + r, "../", 3); + r = devfs_mk_symlink(NULL, DM_DIR "/control", + DEVFS_FL_DEFAULT, rname + r, &_ctl_handle, NULL); + if (r) { + DMERR("devfs_mk_symlink failed for control device"); + return r; + } + devfs_auto_unregister(_dm_misc.devfs_handle, _ctl_handle); + + return 0; +} + +/* Create misc character device and link to DM_DIR/control */ +int __init dm_interface_init(void) +{ + int r; + + r = misc_register(&_dm_misc); + if (r) { + DMERR("misc_register failed for control device"); + return r; + } + + r = dm_devfs_init(); + if (r) { + misc_deregister(&_dm_misc); + return r; + } + + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, + DM_DRIVER_EMAIL); + + return 0; +} + +void dm_interface_exit(void) +{ + if (misc_deregister(&_dm_misc) < 0) + DMERR("misc_deregister failed for control device"); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-linear.c linux.21rc1-ac2/drivers/md/dm-linear.c --- linux.21rc1/drivers/md/dm-linear.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-linear.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include + +/* + * Linear: maps a linear range of a device. + */ +struct linear_c { + long delta; /* FIXME: we need a signed offset type */ + long start; /* For display only */ + struct dm_dev *dev; +}; + +/* + * Construct a linear mapping: + */ +static int linear_ctr(struct dm_table *t, offset_t b, offset_t l, + int argc, char **argv, void **context) +{ + struct linear_c *lc; + unsigned long start; /* FIXME: unsigned long long */ + char *end; + + if (argc != 2) { + *context = "dm-linear: Not enough arguments"; + return -EINVAL; + } + + lc = kmalloc(sizeof(*lc), GFP_KERNEL); + if (lc == NULL) { + *context = "dm-linear: Cannot allocate linear context"; + return -ENOMEM; + } + + start = simple_strtoul(argv[1], &end, 10); + if (*end) { + *context = "dm-linear: Invalid device sector"; + goto bad; + } + + if (dm_table_get_device(t, argv[0], start, l, t->mode, &lc->dev)) { + *context = "dm-linear: Device lookup failed"; + goto bad; + } + + lc->delta = (int) start - (int) b; + lc->start = start; + *context = lc; + return 0; + + bad: + kfree(lc); + return -EINVAL; +} + +static void linear_dtr(struct dm_table *t, void *c) +{ + struct linear_c *lc = (struct linear_c *) c; + + dm_table_put_device(t, lc->dev); + kfree(c); +} + +static int linear_map(struct buffer_head *bh, int rw, void *context) +{ + struct linear_c *lc = (struct linear_c *) context; + + bh->b_rdev = lc->dev->dev; + bh->b_rsector = bh->b_rsector + lc->delta; + + return 1; +} + +static int linear_status(status_type_t type, char *result, int maxlen, + void *context) +{ + struct linear_c *lc = (struct linear_c *) context; + + switch (type) { + case STATUSTYPE_INFO: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s %ld", kdevname(lc->dev->dev), + lc->start); + break; + } + return 0; +} + +static struct target_type linear_target = { + name: "linear", + module: THIS_MODULE, + ctr: linear_ctr, + dtr: linear_dtr, + map: linear_map, + status: linear_status, +}; + +int __init dm_linear_init(void) +{ + int r = dm_register_target(&linear_target); + + if (r < 0) + DMERR("linear: register failed %d", r); + + return r; +} + +void dm_linear_exit(void) +{ + int r = dm_unregister_target(&linear_target); + + if (r < 0) + DMERR("linear: unregister failed %d", r); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-snapshot.c linux.21rc1-ac2/drivers/md/dm-snapshot.c --- linux.21rc1/drivers/md/dm-snapshot.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-snapshot.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,1169 @@ +/* + * dm-snapshot.c + * + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm-snapshot.h" +#include "kcopyd.h" + +/* + * FIXME: Remove this before release. + */ +#if 0 +#define DMDEBUG(x...) DMWARN( ## x) +#else +#define DMDEBUG(x...) +#endif + +/* + * The percentage increment we will wake up users at + */ +#define WAKE_UP_PERCENT 5 + +/* + * Hard sector size used all over the kernel + */ +#define SECTOR_SIZE 512 + +/* + * kcopyd priority of snapshot operations + */ +#define SNAPSHOT_COPY_PRIORITY 2 + +struct pending_exception { + struct exception e; + + /* + * Origin buffers waiting for this to complete are held + * in a list (using b_reqnext). + */ + struct buffer_head *origin_bhs; + struct buffer_head *snapshot_bhs; + + /* + * Other pending_exceptions that are processing this + * chunk. When this list is empty, we know we can + * complete the origins. + */ + struct list_head siblings; + + /* Pointer back to snapshot context */ + struct dm_snapshot *snap; + + /* + * 1 indicates the exception has already been sent to + * kcopyd. + */ + int started; +}; + +/* + * Hash table mapping origin volumes to lists of snapshots and + * a lock to protect it + */ +static kmem_cache_t *exception_cache; +static kmem_cache_t *pending_cache; +static mempool_t *pending_pool; + +/* + * One of these per registered origin, held in the snapshot_origins hash + */ +struct origin { + /* The origin device */ + kdev_t dev; + + struct list_head hash_list; + + /* List of snapshots for this origin */ + struct list_head snapshots; +}; + +/* + * Size of the hash table for origin volumes. If we make this + * the size of the minors list then it should be nearly perfect + */ +#define ORIGIN_HASH_SIZE 256 +#define ORIGIN_MASK 0xFF +static struct list_head *_origins; +static struct rw_semaphore _origins_lock; + +static int init_origin_hash(void) +{ + int i; + + _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head), + GFP_KERNEL); + if (!_origins) { + DMERR("Device mapper: Snapshot: unable to allocate memory"); + return -ENOMEM; + } + + for (i = 0; i < ORIGIN_HASH_SIZE; i++) + INIT_LIST_HEAD(_origins + i); + init_rwsem(&_origins_lock); + + return 0; +} + +static void exit_origin_hash(void) +{ + kfree(_origins); +} + +static inline unsigned int origin_hash(kdev_t dev) +{ + return MINOR(dev) & ORIGIN_MASK; +} + +static struct origin *__lookup_origin(kdev_t origin) +{ + struct list_head *slist; + struct list_head *ol; + struct origin *o; + + ol = &_origins[origin_hash(origin)]; + list_for_each(slist, ol) { + o = list_entry(slist, struct origin, hash_list); + + if (o->dev == origin) + return o; + } + + return NULL; +} + +static void __insert_origin(struct origin *o) +{ + struct list_head *sl = &_origins[origin_hash(o->dev)]; + list_add_tail(&o->hash_list, sl); +} + +/* + * Make a note of the snapshot and its origin so we can look it + * up when the origin has a write on it. + */ +static int register_snapshot(struct dm_snapshot *snap) +{ + struct origin *o; + kdev_t dev = snap->origin->dev; + + down_write(&_origins_lock); + o = __lookup_origin(dev); + + if (!o) { + /* New origin */ + o = kmalloc(sizeof(*o), GFP_KERNEL); + if (!o) { + up_write(&_origins_lock); + return -ENOMEM; + } + + /* Initialise the struct */ + INIT_LIST_HEAD(&o->snapshots); + o->dev = dev; + + __insert_origin(o); + } + + list_add_tail(&snap->list, &o->snapshots); + + up_write(&_origins_lock); + return 0; +} + +static void unregister_snapshot(struct dm_snapshot *s) +{ + struct origin *o; + + down_write(&_origins_lock); + o = __lookup_origin(s->origin->dev); + + list_del(&s->list); + if (list_empty(&o->snapshots)) { + list_del(&o->hash_list); + kfree(o); + } + + up_write(&_origins_lock); +} + +/* + * Implementation of the exception hash tables. + */ +static int init_exception_table(struct exception_table *et, uint32_t size) +{ + int i; + + et->hash_mask = size - 1; + et->table = vcalloc(size, sizeof(struct list_head)); + if (!et->table) + return -ENOMEM; + + for (i = 0; i < size; i++) + INIT_LIST_HEAD(et->table + i); + + return 0; +} + +static void exit_exception_table(struct exception_table *et, kmem_cache_t *mem) +{ + struct list_head *slot, *entry, *temp; + struct exception *ex; + int i, size; + + size = et->hash_mask + 1; + for (i = 0; i < size; i++) { + slot = et->table + i; + + list_for_each_safe(entry, temp, slot) { + ex = list_entry(entry, struct exception, hash_list); + kmem_cache_free(mem, ex); + } + } + + vfree(et->table); +} + +/* + * FIXME: check how this hash fn is performing. + */ +static inline uint32_t exception_hash(struct exception_table *et, chunk_t chunk) +{ + return chunk & et->hash_mask; +} + +static void insert_exception(struct exception_table *eh, struct exception *e) +{ + struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)]; + list_add(&e->hash_list, l); +} + +static inline void remove_exception(struct exception *e) +{ + list_del(&e->hash_list); +} + +/* + * Return the exception data for a sector, or NULL if not + * remapped. + */ +static struct exception *lookup_exception(struct exception_table *et, + chunk_t chunk) +{ + struct list_head *slot, *el; + struct exception *e; + + slot = &et->table[exception_hash(et, chunk)]; + list_for_each(el, slot) { + e = list_entry(el, struct exception, hash_list); + if (e->old_chunk == chunk) + return e; + } + + return NULL; +} + +static inline struct exception *alloc_exception(void) +{ + struct exception *e; + + e = kmem_cache_alloc(exception_cache, GFP_NOIO); + if (!e) + e = kmem_cache_alloc(exception_cache, GFP_ATOMIC); + + return e; +} + +static inline void free_exception(struct exception *e) +{ + kmem_cache_free(exception_cache, e); +} + +static inline struct pending_exception *alloc_pending_exception(void) +{ + return mempool_alloc(pending_pool, GFP_NOIO); +} + +static inline void free_pending_exception(struct pending_exception *pe) +{ + mempool_free(pe, pending_pool); +} + +int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new) +{ + struct exception *e; + + e = alloc_exception(); + if (!e) + return -ENOMEM; + + e->old_chunk = old; + e->new_chunk = new; + insert_exception(&s->complete, e); + return 0; +} + +/* + * Hard coded magic. + */ +static int calc_max_buckets(void) +{ + unsigned long mem; + + mem = num_physpages << PAGE_SHIFT; + mem /= 50; + mem /= sizeof(struct list_head); + + return mem; +} + +/* + * Rounds a number down to a power of 2. + */ +static inline uint32_t round_down(uint32_t n) +{ + while (n & (n - 1)) + n &= (n - 1); + return n; +} + +/* + * Allocate room for a suitable hash table. + */ +static int init_hash_tables(struct dm_snapshot *s) +{ + offset_t hash_size, cow_dev_size, origin_dev_size, max_buckets; + + /* + * Calculate based on the size of the original volume or + * the COW volume... + */ + cow_dev_size = get_dev_size(s->cow->dev); + origin_dev_size = get_dev_size(s->origin->dev); + max_buckets = calc_max_buckets(); + + hash_size = min(origin_dev_size, cow_dev_size) / s->chunk_size; + hash_size = min(hash_size, max_buckets); + + /* Round it down to a power of 2 */ + hash_size = round_down(hash_size); + if (init_exception_table(&s->complete, hash_size)) + return -ENOMEM; + + /* + * Allocate hash table for in-flight exceptions + * Make this smaller than the real hash table + */ + hash_size >>= 3; + if (!hash_size) + hash_size = 64; + + if (init_exception_table(&s->pending, hash_size)) { + exit_exception_table(&s->complete, exception_cache); + return -ENOMEM; + } + + return 0; +} + +/* + * Round a number up to the nearest 'size' boundary. size must + * be a power of 2. + */ +static inline ulong round_up(ulong n, ulong size) +{ + size--; + return (n + size) & ~size; +} + +/* + * Construct a snapshot mapping:

+ */ +static int snapshot_ctr(struct dm_table *t, offset_t b, offset_t l, + int argc, char **argv, void **context) +{ + struct dm_snapshot *s; + unsigned long chunk_size; + int r = -EINVAL; + char *persistent; + char *origin_path; + char *cow_path; + char *value; + int blocksize; + + if (argc < 4) { + *context = "dm-snapshot: requires exactly 4 arguments"; + r = -EINVAL; + goto bad; + } + + origin_path = argv[0]; + cow_path = argv[1]; + persistent = argv[2]; + + if ((*persistent & 0x5f) != 'P' && (*persistent & 0x5f) != 'N') { + *context = "Persistent flag is not P or N"; + r = -EINVAL; + goto bad; + } + + chunk_size = simple_strtoul(argv[3], &value, 10); + if (chunk_size == 0 || value == NULL) { + *context = "Invalid chunk size"; + r = -EINVAL; + goto bad; + } + + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) { + *context = "Cannot allocate snapshot context private structure"; + r = -ENOMEM; + goto bad; + } + + r = dm_table_get_device(t, origin_path, 0, 0, FMODE_READ, &s->origin); + if (r) { + *context = "Cannot get origin device"; + goto bad_free; + } + + r = dm_table_get_device(t, cow_path, 0, 0, + FMODE_READ | FMODE_WRITE, &s->cow); + if (r) { + dm_table_put_device(t, s->origin); + *context = "Cannot get COW device"; + goto bad_free; + } + + /* + * Chunk size must be multiple of page size. Silently + * round up if it's not. + */ + chunk_size = round_up(chunk_size, PAGE_SIZE / SECTOR_SIZE); + + /* Validate the chunk size against the device block size */ + blocksize = get_hardsect_size(s->cow->dev); + if (chunk_size % (blocksize / SECTOR_SIZE)) { + *context = "Chunk size is not a multiple of device blocksize"; + r = -EINVAL; + goto bad_putdev; + } + + /* Check the sizes are small enough to fit in one kiovec */ + if (chunk_size > KIO_MAX_SECTORS) { + *context = "Chunk size is too big"; + r = -EINVAL; + goto bad_putdev; + } + + /* Check chunk_size is a power of 2 */ + if (chunk_size & (chunk_size - 1)) { + *context = "Chunk size is not a power of 2"; + r = -EINVAL; + goto bad_putdev; + } + + s->chunk_size = chunk_size; + s->chunk_mask = chunk_size - 1; + s->type = *persistent; + for (s->chunk_shift = 0; chunk_size; + s->chunk_shift++, chunk_size >>= 1) + ; + s->chunk_shift--; + + s->valid = 1; + s->last_percent = 0; + s->table = t; + init_rwsem(&s->lock); + + /* Allocate hash table for COW data */ + if (init_hash_tables(s)) { + *context = "Unable to allocate hash table space"; + r = -ENOMEM; + goto bad_putdev; + } + + /* + * Check the persistent flag - done here because we need the iobuf + * to check the LV header + */ + s->store.snap = s; + + if ((*persistent & 0x5f) == 'P') + r = dm_create_persistent(&s->store, s->chunk_size); + else + r = dm_create_transient(&s->store, s, blocksize, context); + + if (r) { + *context = "Couldn't create exception store"; + r = -EINVAL; + goto bad_free1; + } + + /* Flush IO to the origin device */ +#if LVM_VFS_ENHANCEMENT + fsync_dev_lockfs(s->origin->dev); +#else + fsync_dev(s->origin->dev); +#endif + + /* Add snapshot to the list of snapshots for this origin */ + if (register_snapshot(s)) { + r = -EINVAL; + *context = "Cannot register snapshot origin"; + goto bad_free2; + } +#if LVM_VFS_ENHANCEMENT + unlockfs(s->origin->dev); +#endif + kcopyd_inc_client_count(); + + *context = s; + return 0; + + bad_free2: + s->store.destroy(&s->store); + + bad_free1: + exit_exception_table(&s->pending, pending_cache); + exit_exception_table(&s->complete, exception_cache); + + bad_putdev: + dm_table_put_device(t, s->cow); + dm_table_put_device(t, s->origin); + + bad_free: + kfree(s); + + bad: + return r; +} + +static void snapshot_dtr(struct dm_table *t, void *context) +{ + struct dm_snapshot *s = (struct dm_snapshot *) context; + + dm_table_event(s->table); + + unregister_snapshot(s); + + exit_exception_table(&s->pending, pending_cache); + exit_exception_table(&s->complete, exception_cache); + + /* Deallocate memory used */ + s->store.destroy(&s->store); + + dm_table_put_device(t, s->origin); + dm_table_put_device(t, s->cow); + kfree(s); + + kcopyd_dec_client_count(); +} + +/* + * We hold lists of buffer_heads, using the b_reqnext field. + */ +static void queue_buffer(struct buffer_head **queue, struct buffer_head *bh) +{ + bh->b_reqnext = *queue; + *queue = bh; +} + +/* + * Flush a list of buffers. + */ +static void flush_buffers(struct buffer_head *bh) +{ + struct buffer_head *n; + + DMDEBUG("begin flush"); + while (bh) { + n = bh->b_reqnext; + bh->b_reqnext = NULL; + DMDEBUG("flushing %p", bh); + generic_make_request(WRITE, bh); + bh = n; + } + + run_task_queue(&tq_disk); +} + +/* + * Error a list of buffers. + */ +static void error_buffers(struct buffer_head *bh) +{ + struct buffer_head *n; + + while (bh) { + n = bh->b_reqnext; + bh->b_reqnext = NULL; + buffer_IO_error(bh); + bh = n; + } +} + +static void pending_complete(struct pending_exception *pe, int success) +{ + struct exception *e; + struct dm_snapshot *s = pe->snap; + + if (success) { + e = alloc_exception(); + if (!e) { + printk("Unable to allocate exception."); + down_write(&s->lock); + s->store.drop_snapshot(&s->store); + s->valid = 0; + up_write(&s->lock); + return; + } + + /* + * Add a proper exception, and remove the + * inflight exception from the list. + */ + down_write(&s->lock); + + memcpy(e, &pe->e, sizeof(*e)); + insert_exception(&s->complete, e); + remove_exception(&pe->e); + + /* Submit any pending write BHs */ + up_write(&s->lock); + + flush_buffers(pe->snapshot_bhs); + DMDEBUG("Exception completed successfully."); + + /* Notify any interested parties */ + if (s->store.percent_full) { + int pc = s->store.percent_full(&s->store); + + if (pc >= s->last_percent + WAKE_UP_PERCENT) { + dm_table_event(s->table); + s->last_percent = pc - pc % WAKE_UP_PERCENT; + } + } + + } else { + /* Read/write error - snapshot is unusable */ + DMERR("Error reading/writing snapshot"); + + down_write(&s->lock); + s->store.drop_snapshot(&s->store); + s->valid = 0; + remove_exception(&pe->e); + up_write(&s->lock); + + error_buffers(pe->snapshot_bhs); + + dm_table_event(s->table); + DMDEBUG("Exception failed."); + } + + if (list_empty(&pe->siblings)) + flush_buffers(pe->origin_bhs); + else + list_del(&pe->siblings); + + free_pending_exception(pe); +} + +static void commit_callback(void *context, int success) +{ + struct pending_exception *pe = (struct pending_exception *) context; + pending_complete(pe, success); +} + +/* + * Called when the copy I/O has finished. kcopyd actually runs + * this code so don't block. + */ +static void copy_callback(int err, void *context) +{ + struct pending_exception *pe = (struct pending_exception *) context; + struct dm_snapshot *s = pe->snap; + + if (err) + pending_complete(pe, 0); + + else + /* Update the metadata if we are persistent */ + s->store.commit_exception(&s->store, &pe->e, commit_callback, + pe); +} + +/* + * Dispatches the copy operation to kcopyd. + */ +static inline void start_copy(struct pending_exception *pe) +{ + struct dm_snapshot *s = pe->snap; + struct kcopyd_region src, dest; + + src.dev = s->origin->dev; + src.sector = chunk_to_sector(s, pe->e.old_chunk); + src.count = s->chunk_size; + + dest.dev = s->cow->dev; + dest.sector = chunk_to_sector(s, pe->e.new_chunk); + dest.count = s->chunk_size; + + if (!pe->started) { + /* Hand over to kcopyd */ + kcopyd_copy(&src, &dest, copy_callback, pe); + pe->started = 1; + } +} + +/* + * Looks to see if this snapshot already has a pending exception + * for this chunk, otherwise it allocates a new one and inserts + * it into the pending table. + */ +static struct pending_exception *find_pending_exception(struct dm_snapshot *s, + struct buffer_head *bh) +{ + struct exception *e; + struct pending_exception *pe; + chunk_t chunk = sector_to_chunk(s, bh->b_rsector); + + /* + * Is there a pending exception for this already ? + */ + e = lookup_exception(&s->pending, chunk); + if (e) { + /* cast the exception to a pending exception */ + pe = list_entry(e, struct pending_exception, e); + + } else { + /* Create a new pending exception */ + pe = alloc_pending_exception(); + if (!pe) { + DMWARN("Couldn't allocate pending exception."); + return NULL; + } + + pe->e.old_chunk = chunk; + pe->origin_bhs = pe->snapshot_bhs = NULL; + INIT_LIST_HEAD(&pe->siblings); + pe->snap = s; + pe->started = 0; + + if (s->store.prepare_exception(&s->store, &pe->e)) { + free_pending_exception(pe); + s->valid = 0; + return NULL; + } + + insert_exception(&s->pending, &pe->e); + } + + return pe; +} + +static inline void remap_exception(struct dm_snapshot *s, struct exception *e, + struct buffer_head *bh) +{ + bh->b_rdev = s->cow->dev; + bh->b_rsector = chunk_to_sector(s, e->new_chunk) + + (bh->b_rsector & s->chunk_mask); +} + +static int snapshot_map(struct buffer_head *bh, int rw, void *context) +{ + struct exception *e; + struct dm_snapshot *s = (struct dm_snapshot *) context; + int r = 1; + chunk_t chunk; + struct pending_exception *pe; + + chunk = sector_to_chunk(s, bh->b_rsector); + + /* Full snapshots are not usable */ + if (!s->valid) + return -1; + + /* + * Write to snapshot - higher level takes care of RW/RO + * flags so we should only get this if we are + * writeable. + */ + if (rw == WRITE) { + + down_write(&s->lock); + + /* If the block is already remapped - use that, else remap it */ + e = lookup_exception(&s->complete, chunk); + if (e) + remap_exception(s, e, bh); + + else { + pe = find_pending_exception(s, bh); + + if (!pe) { + s->store.drop_snapshot(&s->store); + s->valid = 0; + } + + queue_buffer(&pe->snapshot_bhs, bh); + start_copy(pe); + r = 0; + } + + up_write(&s->lock); + + } else { + /* + * FIXME: this read path scares me because we + * always use the origin when we have a pending + * exception. However I can't think of a + * situation where this is wrong - ejt. + */ + + /* Do reads */ + down_read(&s->lock); + + /* See if it it has been remapped */ + e = lookup_exception(&s->complete, chunk); + if (e) + remap_exception(s, e, bh); + else + bh->b_rdev = s->origin->dev; + + up_read(&s->lock); + } + + return r; +} + +static void list_merge(struct list_head *l1, struct list_head *l2) +{ + struct list_head *l1_n, *l2_p; + + l1_n = l1->next; + l2_p = l2->prev; + + l1->next = l2; + l2->prev = l1; + + l2_p->next = l1_n; + l1_n->prev = l2_p; +} + +static int __origin_write(struct list_head *snapshots, struct buffer_head *bh) +{ + int r = 1; + struct list_head *sl; + struct dm_snapshot *snap; + struct exception *e; + struct pending_exception *pe, *last = NULL; + chunk_t chunk; + + /* Do all the snapshots on this origin */ + list_for_each(sl, snapshots) { + snap = list_entry(sl, struct dm_snapshot, list); + + /* Only deal with valid snapshots */ + if (!snap->valid) + continue; + + down_write(&snap->lock); + + /* + * Remember, different snapshots can have + * different chunk sizes. + */ + chunk = sector_to_chunk(snap, bh->b_rsector); + + /* + * Check exception table to see if block + * is already remapped in this snapshot + * and trigger an exception if not. + */ + e = lookup_exception(&snap->complete, chunk); + if (!e) { + pe = find_pending_exception(snap, bh); + if (!pe) { + snap->store.drop_snapshot(&snap->store); + snap->valid = 0; + + } else { + if (last) + list_merge(&pe->siblings, + &last->siblings); + + last = pe; + r = 0; + } + } + + up_write(&snap->lock); + } + + /* + * Now that we have a complete pe list we can start the copying. + */ + if (last) { + pe = last; + do { + down_write(&pe->snap->lock); + queue_buffer(&pe->origin_bhs, bh); + start_copy(pe); + up_write(&pe->snap->lock); + pe = list_entry(pe->siblings.next, + struct pending_exception, siblings); + + } while (pe != last); + } + + return r; +} + +static int snapshot_status(status_type_t type, char *result, + int maxlen, void *context) +{ + struct dm_snapshot *snap = (struct dm_snapshot *) context; + char cow[16]; + char org[16]; + + switch (type) { + case STATUSTYPE_INFO: + if (!snap->valid) + snprintf(result, maxlen, "Invalid"); + else { + if (snap->store.percent_full) + snprintf(result, maxlen, "%d%%", + snap->store.percent_full(&snap-> + store)); + else + snprintf(result, maxlen, "Unknown"); + } + break; + + case STATUSTYPE_TABLE: + /* + * kdevname returns a static pointer so we need + * to make private copies if the output is to + * make sense. + */ + strncpy(cow, kdevname(snap->cow->dev), sizeof(cow)); + strncpy(org, kdevname(snap->origin->dev), sizeof(org)); + snprintf(result, maxlen, "%s %s %c %ld", org, cow, + snap->type, snap->chunk_size); + break; + } + + return 0; +} + +/* + * Called on a write from the origin driver. + */ +int do_origin(struct dm_dev *origin, struct buffer_head *bh) +{ + struct origin *o; + int r; + + down_read(&_origins_lock); + o = __lookup_origin(origin->dev); + if (!o) + BUG(); + + r = __origin_write(&o->snapshots, bh); + up_read(&_origins_lock); + + return r; +} + +/* + * Origin: maps a linear range of a device, with hooks for snapshotting. + */ + +/* + * Construct an origin mapping: + * The context for an origin is merely a 'struct dm_dev *' + * pointing to the real device. + */ +static int origin_ctr(struct dm_table *t, offset_t b, offset_t l, + int argc, char **argv, void **context) +{ + int r; + struct dm_dev *dev; + + if (argc != 1) { + *context = "dm-origin: incorrect number of arguments"; + return -EINVAL; + } + + r = dm_table_get_device(t, argv[0], 0, l, t->mode, &dev); + if (r) { + *context = "Cannot get target device"; + return r; + } + + *context = dev; + + return 0; +} + +static void origin_dtr(struct dm_table *t, void *c) +{ + struct dm_dev *dev = (struct dm_dev *) c; + dm_table_put_device(t, dev); +} + +static int origin_map(struct buffer_head *bh, int rw, void *context) +{ + struct dm_dev *dev = (struct dm_dev *) context; + bh->b_rdev = dev->dev; + + /* Only tell snapshots if this is a write */ + return (rw == WRITE) ? do_origin(dev, bh) : 1; +} + +static int origin_status(status_type_t type, char *result, + int maxlen, void *context) +{ + struct dm_dev *dev = (struct dm_dev *) context; + + switch (type) { + case STATUSTYPE_INFO: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s", kdevname(dev->dev)); + break; + } + + return 0; +} + +static struct target_type origin_target = { + name: "snapshot-origin", + module: THIS_MODULE, + ctr: origin_ctr, + dtr: origin_dtr, + map: origin_map, + status: origin_status, + err: NULL +}; + +static struct target_type snapshot_target = { + name: "snapshot", + module: THIS_MODULE, + ctr: snapshot_ctr, + dtr: snapshot_dtr, + map: snapshot_map, + status: snapshot_status, + err: NULL +}; + +int __init dm_snapshot_init(void) +{ + int r; + + r = dm_register_target(&snapshot_target); + if (r) { + DMERR("snapshot target register failed %d", r); + return r; + } + + r = dm_register_target(&origin_target); + if (r < 0) { + DMERR("Device mapper: Origin: register failed %d\n", r); + goto bad1; + } + + r = init_origin_hash(); + if (r) { + DMERR("init_origin_hash failed."); + goto bad2; + } + + exception_cache = kmem_cache_create("dm-snapshot-ex", + sizeof(struct exception), + __alignof__(struct exception), + 0, NULL, NULL); + if (!exception_cache) { + DMERR("Couldn't create exception cache."); + r = -ENOMEM; + goto bad3; + } + + pending_cache = + kmem_cache_create("dm-snapshot-in", + sizeof(struct pending_exception), + __alignof__(struct pending_exception), + 0, NULL, NULL); + if (!pending_cache) { + DMERR("Couldn't create pending cache."); + r = -ENOMEM; + goto bad4; + } + + pending_pool = mempool_create(128, mempool_alloc_slab, + mempool_free_slab, pending_cache); + if (!pending_pool) { + DMERR("Couldn't create pending pool."); + r = -ENOMEM; + goto bad5; + } + + return 0; + + bad5: + kmem_cache_destroy(pending_cache); + bad4: + kmem_cache_destroy(exception_cache); + bad3: + exit_origin_hash(); + bad2: + dm_unregister_target(&origin_target); + bad1: + dm_unregister_target(&snapshot_target); + return r; +} + +void dm_snapshot_exit(void) +{ + int r; + + r = dm_unregister_target(&snapshot_target); + if (r) + DMERR("snapshot unregister failed %d", r); + + r = dm_unregister_target(&origin_target); + if (r) + DMERR("origin unregister failed %d", r); + + exit_origin_hash(); + mempool_destroy(pending_pool); + kmem_cache_destroy(pending_cache); + kmem_cache_destroy(exception_cache); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-snapshot.h linux.21rc1-ac2/drivers/md/dm-snapshot.h --- linux.21rc1/drivers/md/dm-snapshot.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-snapshot.h 2003-04-25 16:05:08.000000000 +0100 @@ -0,0 +1,147 @@ +/* + * dm-snapshot.c + * + * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#ifndef DM_SNAPSHOT_H +#define DM_SNAPSHOT_H + +#include "dm.h" +#include + +struct exception_table { + uint32_t hash_mask; + struct list_head *table; +}; + +/* + * The snapshot code deals with largish chunks of the disk at a + * time. Typically 64k - 256k. + */ +/* FIXME: can we get away with limiting these to a uint32_t ? */ +typedef offset_t chunk_t; + +/* + * An exception is used where an old chunk of data has been + * replaced by a new one. + */ +struct exception { + struct list_head hash_list; + + chunk_t old_chunk; + chunk_t new_chunk; +}; + +/* + * Abstraction to handle the meta/layout of exception stores (the + * COW device). + */ +struct exception_store { + + /* + * Destroys this object when you've finished with it. + */ + void (*destroy) (struct exception_store *store); + + /* + * Find somewhere to store the next exception. + */ + int (*prepare_exception) (struct exception_store *store, + struct exception *e); + + /* + * Update the metadata with this exception. + */ + void (*commit_exception) (struct exception_store *store, + struct exception *e, + void (*callback) (void *, int success), + void *callback_context); + + /* + * The snapshot is invalid, note this in the metadata. + */ + void (*drop_snapshot) (struct exception_store *store); + + /* + * Return the %age full of the snapshot + */ + int (*percent_full) (struct exception_store *store); + + struct dm_snapshot *snap; + void *context; +}; + +struct dm_snapshot { + struct rw_semaphore lock; + struct dm_table *table; + + struct dm_dev *origin; + struct dm_dev *cow; + + /* List of snapshots per Origin */ + struct list_head list; + + /* Size of data blocks saved - must be a power of 2 */ + chunk_t chunk_size; + chunk_t chunk_mask; + chunk_t chunk_shift; + + /* You can't use a snapshot if this is 0 (e.g. if full) */ + int valid; + + /* Used for display of table */ + char type; + + /* The last percentage we notified */ + int last_percent; + + struct exception_table pending; + struct exception_table complete; + + /* The on disk metadata handler */ + struct exception_store store; +}; + +/* + * Used by the exception stores to load exceptions hen + * initialising. + */ +int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new); + +/* + * Constructor and destructor for the default persistent + * store. + */ +int dm_create_persistent(struct exception_store *store, uint32_t chunk_size); + +int dm_create_transient(struct exception_store *store, + struct dm_snapshot *s, int blocksize, void **error); + +/* + * Return the number of sectors in the device. + */ +static inline offset_t get_dev_size(kdev_t dev) +{ + int *sizes; + + sizes = blk_size[MAJOR(dev)]; + if (sizes) + return sizes[MINOR(dev)] << 1; + + return 0; +} + +static inline chunk_t sector_to_chunk(struct dm_snapshot *s, offset_t sector) +{ + return (sector & ~s->chunk_mask) >> s->chunk_shift; +} + +static inline offset_t chunk_to_sector(struct dm_snapshot *s, chunk_t chunk) +{ + return chunk << s->chunk_shift; +} + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-stripe.c linux.21rc1-ac2/drivers/md/dm-stripe.c --- linux.21rc1/drivers/md/dm-stripe.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-stripe.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include + +struct stripe { + struct dm_dev *dev; + offset_t physical_start; +}; + +struct stripe_c { + offset_t logical_start; + uint32_t stripes; + + /* The size of this target / num. stripes */ + uint32_t stripe_width; + + /* stripe chunk size */ + uint32_t chunk_shift; + offset_t chunk_mask; + + struct stripe stripe[0]; +}; + +static inline struct stripe_c *alloc_context(int stripes) +{ + size_t len; + + if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe), + stripes)) + return NULL; + + len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes); + + return kmalloc(len, GFP_KERNEL); +} + +/* + * Parse a single pair + */ +static int get_stripe(struct dm_table *t, struct stripe_c *sc, + int stripe, char **argv) +{ + char *end; + unsigned long start; + + start = simple_strtoul(argv[1], &end, 10); + if (*end) + return -EINVAL; + + if (dm_table_get_device(t, argv[0], start, sc->stripe_width, + t->mode, &sc->stripe[stripe].dev)) + return -ENXIO; + + sc->stripe[stripe].physical_start = start; + return 0; +} + +/* + * Construct a striped mapping. + * [ ]+ + */ +static int stripe_ctr(struct dm_table *t, offset_t b, offset_t l, + int argc, char **argv, void **context) +{ + struct stripe_c *sc; + uint32_t stripes; + uint32_t chunk_size; + char *end; + int r, i; + + if (argc < 2) { + *context = "dm-stripe: Not enough arguments"; + return -EINVAL; + } + + stripes = simple_strtoul(argv[0], &end, 10); + if (*end) { + *context = "dm-stripe: Invalid stripe count"; + return -EINVAL; + } + + chunk_size = simple_strtoul(argv[1], &end, 10); + if (*end) { + *context = "dm-stripe: Invalid chunk_size"; + return -EINVAL; + } + + if (l % stripes) { + *context = "dm-stripe: Target length not divisable by " + "number of stripes"; + return -EINVAL; + } + + sc = alloc_context(stripes); + if (!sc) { + *context = "dm-stripe: Memory allocation for striped context " + "failed"; + return -ENOMEM; + } + + sc->logical_start = b; + sc->stripes = stripes; + sc->stripe_width = l / stripes; + + /* + * chunk_size is a power of two + */ + if (!chunk_size || (chunk_size & (chunk_size - 1))) { + *context = "dm-stripe: Invalid chunk size"; + kfree(sc); + return -EINVAL; + } + + sc->chunk_mask = chunk_size - 1; + for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++) + chunk_size >>= 1; + sc->chunk_shift--; + + /* + * Get the stripe destinations. + */ + for (i = 0; i < stripes; i++) { + if (argc < 2) { + *context = "dm-stripe: Not enough destinations " + "specified"; + kfree(sc); + return -EINVAL; + } + + argv += 2; + + r = get_stripe(t, sc, i, argv); + if (r < 0) { + *context = "dm-stripe: Couldn't parse stripe " + "destination"; + while (i--) + dm_table_put_device(t, sc->stripe[i].dev); + kfree(sc); + return r; + } + } + + *context = sc; + return 0; +} + +static void stripe_dtr(struct dm_table *t, void *c) +{ + unsigned int i; + struct stripe_c *sc = (struct stripe_c *) c; + + for (i = 0; i < sc->stripes; i++) + dm_table_put_device(t, sc->stripe[i].dev); + + kfree(sc); +} + +static int stripe_map(struct buffer_head *bh, int rw, void *context) +{ + struct stripe_c *sc = (struct stripe_c *) context; + + offset_t offset = bh->b_rsector - sc->logical_start; + uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift); + uint32_t stripe = chunk % sc->stripes; /* 32bit modulus */ + chunk = chunk / sc->stripes; + + bh->b_rdev = sc->stripe[stripe].dev->dev; + bh->b_rsector = sc->stripe[stripe].physical_start + + (chunk << sc->chunk_shift) + (offset & sc->chunk_mask); + return 1; +} + +static int stripe_status(status_type_t type, char *result, int maxlen, + void *context) +{ + struct stripe_c *sc = (struct stripe_c *) context; + int offset; + int i; + + switch (type) { + case STATUSTYPE_INFO: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + offset = snprintf(result, maxlen, "%d %ld", + sc->stripes, sc->chunk_mask + 1); + for (i = 0; i < sc->stripes; i++) { + offset += + snprintf(result + offset, maxlen - offset, + " %s %ld", + kdevname(sc->stripe[i].dev->dev), + sc->stripe[i].physical_start); + } + break; + } + return 0; +} + +static struct target_type stripe_target = { + name: "striped", + module: THIS_MODULE, + ctr: stripe_ctr, + dtr: stripe_dtr, + map: stripe_map, + status: stripe_status, +}; + +int __init dm_stripe_init(void) +{ + int r; + + r = dm_register_target(&stripe_target); + if (r < 0) + DMWARN("striped target registration failed"); + + return r; +} + +void dm_stripe_exit(void) +{ + if (dm_unregister_target(&stripe_target)) + DMWARN("striped target unregistration failed"); + + return; +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-table.c linux.21rc1-ac2/drivers/md/dm-table.c --- linux.21rc1/drivers/md/dm-table.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-table.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include + +/* ceiling(n / size) * size */ +static inline unsigned long round_up(unsigned long n, unsigned long size) +{ + unsigned long r = n % size; + return n + (r ? (size - r) : 0); +} + +/* ceiling(n / size) */ +static inline unsigned long div_up(unsigned long n, unsigned long size) +{ + return round_up(n, size) / size; +} + +/* similar to ceiling(log_size(n)) */ +static uint int_log(unsigned long n, unsigned long base) +{ + int result = 0; + + while (n > 1) { + n = div_up(n, base); + result++; + } + + return result; +} + +/* + * return the highest key that you could lookup + * from the n'th node on level l of the btree. + */ +static offset_t high(struct dm_table *t, int l, int n) +{ + for (; l < t->depth - 1; l++) + n = get_child(n, CHILDREN_PER_NODE - 1); + + if (n >= t->counts[l]) + return (offset_t) - 1; + + return get_node(t, l, n)[KEYS_PER_NODE - 1]; +} + +/* + * fills in a level of the btree based on the + * highs of the level below it. + */ +static int setup_btree_index(int l, struct dm_table *t) +{ + int n, k; + offset_t *node; + + for (n = 0; n < t->counts[l]; n++) { + node = get_node(t, l, n); + + for (k = 0; k < KEYS_PER_NODE; k++) + node[k] = high(t, l + 1, get_child(n, k)); + } + + return 0; +} + +/* + * highs, and targets are managed as dynamic + * arrays during a table load. + */ +static int alloc_targets(struct dm_table *t, int num) +{ + offset_t *n_highs; + struct target *n_targets; + int n = t->num_targets; + + /* + * Allocate both the target array and offset array at once. + */ + n_highs = (offset_t *) vcalloc(sizeof(struct target) + sizeof(offset_t), + num); + if (!n_highs) + return -ENOMEM; + + n_targets = (struct target *) (n_highs + num); + + if (n) { + memcpy(n_highs, t->highs, sizeof(*n_highs) * n); + memcpy(n_targets, t->targets, sizeof(*n_targets) * n); + } + + memset(n_highs + n, -1, sizeof(*n_highs) * (num - n)); + if (t->highs) + vfree(t->highs); + + t->num_allocated = num; + t->highs = n_highs; + t->targets = n_targets; + + return 0; +} + +int dm_table_create(struct dm_table **result, int mode) +{ + struct dm_table *t = kmalloc(sizeof(*t), GFP_NOIO); + + if (!t) + return -ENOMEM; + + memset(t, 0, sizeof(*t)); + INIT_LIST_HEAD(&t->devices); + + /* allocate a single node's worth of targets to begin with */ + if (alloc_targets(t, KEYS_PER_NODE)) { + kfree(t); + t = NULL; + return -ENOMEM; + } + + init_waitqueue_head(&t->eventq); + t->mode = mode; + *result = t; + return 0; +} + +static void free_devices(struct list_head *devices) +{ + struct list_head *tmp, *next; + + for (tmp = devices->next; tmp != devices; tmp = next) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + next = tmp->next; + kfree(dd); + } +} + +void dm_table_destroy(struct dm_table *t) +{ + int i; + + /* destroying the table counts as an event */ + dm_table_event(t); + + /* free the indexes (see dm_table_complete) */ + if (t->depth >= 2) + vfree(t->index[t->depth - 2]); + + /* free the targets */ + for (i = 0; i < t->num_targets; i++) { + struct target *tgt = &t->targets[i]; + + dm_put_target_type(t->targets[i].type); + + if (tgt->type->dtr) + tgt->type->dtr(t, tgt->private); + } + + vfree(t->highs); + + /* free the device list */ + if (t->devices.next != &t->devices) { + DMWARN("devices still present during destroy: " + "dm_table_remove_device calls missing"); + + free_devices(&t->devices); + } + + kfree(t); +} + +/* + * Checks to see if we need to extend highs or targets. + */ +static inline int check_space(struct dm_table *t) +{ + if (t->num_targets >= t->num_allocated) + return alloc_targets(t, t->num_allocated * 2); + + return 0; +} + +/* + * Convert a device path to a kdev_t. + */ +int lookup_device(const char *path, kdev_t *dev) +{ + int r; + struct nameidata nd; + struct inode *inode; + + if (!path_init(path, LOOKUP_FOLLOW, &nd)) + return 0; + + if ((r = path_walk(path, &nd))) + goto bad; + + inode = nd.dentry->d_inode; + if (!inode) { + r = -ENOENT; + goto bad; + } + + if (!S_ISBLK(inode->i_mode)) { + r = -EINVAL; + goto bad; + } + + *dev = inode->i_rdev; + + bad: + path_release(&nd); + return r; +} + +/* + * See if we've already got a device in the list. + */ +static struct dm_dev *find_device(struct list_head *l, kdev_t dev) +{ + struct list_head *tmp; + + list_for_each(tmp, l) { + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + if (dd->dev == dev) + return dd; + } + + return NULL; +} + +/* + * Open a device so we can use it as a map destination. + */ +static int open_dev(struct dm_dev *d) +{ + int err; + + if (d->bd) + BUG(); + + if (!(d->bd = bdget(kdev_t_to_nr(d->dev)))) + return -ENOMEM; + + if ((err = blkdev_get(d->bd, d->mode, 0, BDEV_FILE))) + return err; + + return 0; +} + +/* + * Close a device that we've been using. + */ +static void close_dev(struct dm_dev *d) +{ + if (!d->bd) + return; + + blkdev_put(d->bd, BDEV_FILE); + d->bd = NULL; +} + +/* + * If possible (ie. blk_size[major] is set), this + * checks an area of a destination device is + * valid. + */ +static int check_device_area(kdev_t dev, offset_t start, offset_t len) +{ + int *sizes; + offset_t dev_size; + + if (!(sizes = blk_size[MAJOR(dev)]) || !(dev_size = sizes[MINOR(dev)])) + /* we don't know the device details, + * so give the benefit of the doubt */ + return 1; + + /* convert to 512-byte sectors */ + dev_size <<= 1; + + return ((start < dev_size) && (len <= (dev_size - start))); +} + +/* + * This upgrades the mode on an already open dm_dev. Being + * careful to leave things as they were if we fail to reopen the + * device. + */ +static int upgrade_mode(struct dm_dev *dd, int new_mode) +{ + int r; + struct dm_dev dd_copy; + + memcpy(&dd_copy, dd, sizeof(dd_copy)); + + dd->mode |= new_mode; + dd->bd = NULL; + r = open_dev(dd); + if (!r) + close_dev(&dd_copy); + else + memcpy(dd, &dd_copy, sizeof(dd_copy)); + + return r; +} + +/* + * Add a device to the list, or just increment the usage count + * if it's already present. + */ +int dm_table_get_device(struct dm_table *t, const char *path, + offset_t start, offset_t len, int mode, + struct dm_dev **result) +{ + int r; + kdev_t dev; + struct dm_dev *dd; + int major, minor; + + if (sscanf(path, "%x:%x", &major, &minor) == 2) { + /* Extract the major/minor numbers */ + dev = MKDEV(major, minor); + } else { + /* convert the path to a device */ + if ((r = lookup_device(path, &dev))) + return r; + } + + dd = find_device(&t->devices, dev); + if (!dd) { + dd = kmalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->mode = mode; + dd->dev = dev; + dd->bd = NULL; + + if ((r = open_dev(dd))) { + kfree(dd); + return r; + } + + atomic_set(&dd->count, 0); + list_add(&dd->list, &t->devices); + + } else if (dd->mode != (mode | dd->mode)) { + r = upgrade_mode(dd, mode); + if (r) + return r; + } + atomic_inc(&dd->count); + + if (!check_device_area(dd->dev, start, len)) { + DMWARN("device %s too small for target", path); + dm_table_put_device(t, dd); + return -EINVAL; + } + + *result = dd; + + return 0; +} + +/* + * Decrement a devices use count and remove it if neccessary. + */ +void dm_table_put_device(struct dm_table *t, struct dm_dev *dd) +{ + if (atomic_dec_and_test(&dd->count)) { + close_dev(dd); + list_del(&dd->list); + kfree(dd); + } +} + +/* + * Adds a target to the map + */ +int dm_table_add_target(struct dm_table *t, offset_t highs, + struct target_type *type, void *private) +{ + int r, n; + + if ((r = check_space(t))) + return r; + + n = t->num_targets++; + t->highs[n] = highs; + t->targets[n].type = type; + t->targets[n].private = private; + + return 0; +} + +static int setup_indexes(struct dm_table *t) +{ + int i, total = 0; + offset_t *indexes; + + /* allocate the space for *all* the indexes */ + for (i = t->depth - 2; i >= 0; i--) { + t->counts[i] = div_up(t->counts[i + 1], CHILDREN_PER_NODE); + total += t->counts[i]; + } + + indexes = (offset_t *) vcalloc(total, (unsigned long) NODE_SIZE); + if (!indexes) + return -ENOMEM; + + /* set up internal nodes, bottom-up */ + for (i = t->depth - 2, total = 0; i >= 0; i--) { + t->index[i] = indexes; + indexes += (KEYS_PER_NODE * t->counts[i]); + setup_btree_index(i, t); + } + + return 0; +} + +/* + * Builds the btree to index the map + */ +int dm_table_complete(struct dm_table *t) +{ + int leaf_nodes, r = 0; + + /* how many indexes will the btree have ? */ + leaf_nodes = div_up(t->num_targets, KEYS_PER_NODE); + t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); + + /* leaf layer has already been set up */ + t->counts[t->depth - 1] = leaf_nodes; + t->index[t->depth - 1] = t->highs; + + if (t->depth >= 2) + r = setup_indexes(t); + + return r; +} + +void dm_table_event(struct dm_table *t) +{ + wake_up_interruptible(&t->eventq); +} + +EXPORT_SYMBOL(dm_table_get_device); +EXPORT_SYMBOL(dm_table_put_device); +EXPORT_SYMBOL(dm_table_event); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/dm-target.c linux.21rc1-ac2/drivers/md/dm-target.c --- linux.21rc1/drivers/md/dm-target.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/dm-target.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include + +struct tt_internal { + struct target_type tt; + + struct list_head list; + long use; +}; + +static LIST_HEAD(_targets); +static rwlock_t _lock = RW_LOCK_UNLOCKED; + +#define DM_MOD_NAME_SIZE 32 + +/* + * Destructively splits up the argument list to pass to ctr. + */ +int split_args(int max, int *argc, char **argv, char *input) +{ + char *start, *end = input, *out; + *argc = 0; + + while (1) { + start = end; + + /* Skip whitespace */ + while (*start && isspace(*start)) + start++; + + if (!*start) + break; /* success, we hit the end */ + + /* 'out' is used to remove any back-quotes */ + end = out = start; + while (*end) { + /* Everything apart from '\0' can be quoted */ + if (*end == '\\' && *(end + 1)) { + *out++ = *(end + 1); + end += 2; + continue; + } + + if (isspace(*end)) + break; /* end of token */ + + *out++ = *end++; + } + + /* have we already filled the array ? */ + if ((*argc + 1) > max) + return -EINVAL; + + /* we know this is whitespace */ + if (*end) + end++; + + /* terminate the string and put it in the array */ + *out = '\0'; + argv[*argc] = start; + (*argc)++; + } + + return 0; +} + +static inline struct tt_internal *__find_target_type(const char *name) +{ + struct list_head *tih; + struct tt_internal *ti; + + list_for_each(tih, &_targets) { + ti = list_entry(tih, struct tt_internal, list); + + if (!strcmp(name, ti->tt.name)) + return ti; + } + + return NULL; +} + +static struct tt_internal *get_target_type(const char *name) +{ + struct tt_internal *ti; + + read_lock(&_lock); + ti = __find_target_type(name); + + if (ti) { + if (ti->use == 0 && ti->tt.module) + __MOD_INC_USE_COUNT(ti->tt.module); + ti->use++; + } + read_unlock(&_lock); + + return ti; +} + +static void load_module(const char *name) +{ + char module_name[DM_MOD_NAME_SIZE] = "dm-"; + + /* Length check for strcat() below */ + if (strlen(name) > (DM_MOD_NAME_SIZE - 4)) + return; + + strcat(module_name, name); + request_module(module_name); + + return; +} + +struct target_type *dm_get_target_type(const char *name) +{ + struct tt_internal *ti = get_target_type(name); + + if (!ti) { + load_module(name); + ti = get_target_type(name); + } + + return ti ? &ti->tt : NULL; +} + +void dm_put_target_type(struct target_type *t) +{ + struct tt_internal *ti = (struct tt_internal *) t; + + read_lock(&_lock); + if (--ti->use == 0 && ti->tt.module) + __MOD_DEC_USE_COUNT(ti->tt.module); + + if (ti->use < 0) + BUG(); + read_unlock(&_lock); + + return; +} + +static struct tt_internal *alloc_target(struct target_type *t) +{ + struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL); + + if (ti) { + memset(ti, 0, sizeof(*ti)); + ti->tt = *t; + } + + return ti; +} + +int dm_register_target(struct target_type *t) +{ + int rv = 0; + struct tt_internal *ti = alloc_target(t); + + if (!ti) + return -ENOMEM; + + write_lock(&_lock); + if (__find_target_type(t->name)) + rv = -EEXIST; + else + list_add(&ti->list, &_targets); + + write_unlock(&_lock); + return rv; +} + +int dm_unregister_target(struct target_type *t) +{ + struct tt_internal *ti; + + write_lock(&_lock); + if (!(ti = __find_target_type(t->name))) { + write_unlock(&_lock); + return -EINVAL; + } + + if (ti->use) { + write_unlock(&_lock); + return -ETXTBSY; + } + + list_del(&ti->list); + kfree(ti); + + write_unlock(&_lock); + return 0; +} + +/* + * io-err: always fails an io, useful for bringing + * up LV's that have holes in them. + */ +static int io_err_ctr(struct dm_table *t, offset_t b, offset_t l, + int argc, char **args, void **context) +{ + *context = NULL; + return 0; +} + +static void io_err_dtr(struct dm_table *t, void *c) +{ + /* empty */ + return; +} + +static int io_err_map(struct buffer_head *bh, int rw, void *context) +{ + buffer_IO_error(bh); + return 0; +} + +static struct target_type error_target = { + name: "error", + ctr: io_err_ctr, + dtr: io_err_dtr, + map: io_err_map, + status: NULL, +}; + +int dm_target_init(void) +{ + return dm_register_target(&error_target); +} + +void dm_target_exit(void) +{ + if (dm_unregister_target(&error_target)) + DMWARN("error target unregistration failed"); +} + +EXPORT_SYMBOL(dm_register_target); +EXPORT_SYMBOL(dm_unregister_target); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/kcopyd.c linux.21rc1-ac2/drivers/md/kcopyd.c --- linux.21rc1/drivers/md/kcopyd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/kcopyd.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,841 @@ +/* + * Copyright (C) 2002 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcopyd.h" + +/* FIXME: this is only needed for the DMERR macros */ +#include "dm.h" + +/* + * Hard sector size used all over the kernel. + */ +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 + +static void wake_kcopyd(void); + +/*----------------------------------------------------------------- + * We reserve our own pool of preallocated pages that are + * only used for kcopyd io. + *---------------------------------------------------------------*/ + +/* + * FIXME: This should be configurable. + */ +#define NUM_PAGES 512 + +static DECLARE_MUTEX(_pages_lock); +static int _num_free_pages; +static struct page *_pages_array[NUM_PAGES]; +static DECLARE_MUTEX(start_lock); + +static int init_pages(void) +{ + int i; + struct page *p; + + for (i = 0; i < NUM_PAGES; i++) { + p = alloc_page(GFP_KERNEL); + if (!p) + goto bad; + + LockPage(p); + _pages_array[i] = p; + } + + _num_free_pages = NUM_PAGES; + return 0; + + bad: + while (i--) + __free_page(_pages_array[i]); + return -ENOMEM; +} + +static void exit_pages(void) +{ + int i; + struct page *p; + + for (i = 0; i < NUM_PAGES; i++) { + p = _pages_array[i]; + UnlockPage(p); + __free_page(p); + } + + _num_free_pages = 0; +} + +static int kcopyd_get_pages(int num, struct page **result) +{ + int i; + + down(&_pages_lock); + if (_num_free_pages < num) { + up(&_pages_lock); + return -ENOMEM; + } + + for (i = 0; i < num; i++) { + _num_free_pages--; + result[i] = _pages_array[_num_free_pages]; + } + up(&_pages_lock); + + return 0; +} + +static void kcopyd_free_pages(int num, struct page **result) +{ + int i; + + down(&_pages_lock); + for (i = 0; i < num; i++) + _pages_array[_num_free_pages++] = result[i]; + up(&_pages_lock); +} + +/*----------------------------------------------------------------- + * We keep our own private pool of buffer_heads. These are just + * held in a list on the b_reqnext field. + *---------------------------------------------------------------*/ + +/* + * Make sure we have enough buffers to always keep the pages + * occupied. So we assume the worst case scenario where blocks + * are the size of a single sector. + */ +#define NUM_BUFFERS NUM_PAGES * (PAGE_SIZE / SECTOR_SIZE) + +static spinlock_t _buffer_lock = SPIN_LOCK_UNLOCKED; +static struct buffer_head *_all_buffers; +static struct buffer_head *_free_buffers; + +static int init_buffers(void) +{ + int i; + struct buffer_head *buffers; + + buffers = vcalloc(NUM_BUFFERS, sizeof(struct buffer_head)); + if (!buffers) { + DMWARN("Couldn't allocate buffer heads."); + return -ENOMEM; + } + + for (i = 0; i < NUM_BUFFERS; i++) { + if (i < NUM_BUFFERS - 1) + buffers[i].b_reqnext = &buffers[i + 1]; + init_waitqueue_head(&buffers[i].b_wait); + INIT_LIST_HEAD(&buffers[i].b_inode_buffers); + } + + _all_buffers = _free_buffers = buffers; + return 0; +} + +static void exit_buffers(void) +{ + vfree(_all_buffers); +} + +static struct buffer_head *alloc_buffer(void) +{ + struct buffer_head *r; + int flags; + + spin_lock_irqsave(&_buffer_lock, flags); + + if (!_free_buffers) + r = NULL; + else { + r = _free_buffers; + _free_buffers = _free_buffers->b_reqnext; + r->b_reqnext = NULL; + } + + spin_unlock_irqrestore(&_buffer_lock, flags); + + return r; +} + +/* + * Only called from interrupt context. + */ +static void free_buffer(struct buffer_head *bh) +{ + int flags, was_empty; + + spin_lock_irqsave(&_buffer_lock, flags); + was_empty = (_free_buffers == NULL) ? 1 : 0; + bh->b_reqnext = _free_buffers; + _free_buffers = bh; + spin_unlock_irqrestore(&_buffer_lock, flags); + + /* + * If the buffer list was empty then kcopyd probably went + * to sleep because it ran out of buffer heads, so let's + * wake it up. + */ + if (was_empty) + wake_kcopyd(); +} + +/*----------------------------------------------------------------- + * kcopyd_jobs need to be allocated by the *clients* of kcopyd, + * for this reason we use a mempool to prevent the client from + * ever having to do io (which could cause a + * deadlock). + *---------------------------------------------------------------*/ +#define MIN_JOBS NUM_PAGES + +static kmem_cache_t *_job_cache = NULL; +static mempool_t *_job_pool = NULL; + +/* + * We maintain three lists of jobs: + * + * i) jobs waiting for pages + * ii) jobs that have pages, and are waiting for the io to be issued. + * iii) jobs that have completed. + * + * All three of these are protected by job_lock. + */ + +static spinlock_t _job_lock = SPIN_LOCK_UNLOCKED; + +static LIST_HEAD(_complete_jobs); +static LIST_HEAD(_io_jobs); +static LIST_HEAD(_pages_jobs); + +static int init_jobs(void) +{ + INIT_LIST_HEAD(&_complete_jobs); + INIT_LIST_HEAD(&_io_jobs); + INIT_LIST_HEAD(&_pages_jobs); + + _job_cache = kmem_cache_create("kcopyd-jobs", sizeof(struct kcopyd_job), + __alignof__(struct kcopyd_job), + 0, NULL, NULL); + if (!_job_cache) + return -ENOMEM; + + _job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab, + mempool_free_slab, _job_cache); + if (!_job_pool) { + kmem_cache_destroy(_job_cache); + return -ENOMEM; + } + + return 0; +} + +static void exit_jobs(void) +{ + mempool_destroy(_job_pool); + kmem_cache_destroy(_job_cache); +} + +struct kcopyd_job *kcopyd_alloc_job(void) +{ + struct kcopyd_job *job; + + job = mempool_alloc(_job_pool, GFP_KERNEL); + if (!job) + return NULL; + + memset(job, 0, sizeof(*job)); + return job; +} + +void kcopyd_free_job(struct kcopyd_job *job) +{ + mempool_free(job, _job_pool); +} + +/* + * Functions to push and pop a job onto the head of a given job + * list. + */ +static inline struct kcopyd_job *pop(struct list_head *jobs) +{ + struct kcopyd_job *job = NULL; + int flags; + + spin_lock_irqsave(&_job_lock, flags); + + if (!list_empty(jobs)) { + job = list_entry(jobs->next, struct kcopyd_job, list); + list_del(&job->list); + } + spin_unlock_irqrestore(&_job_lock, flags); + + return job; +} + +static inline void push(struct list_head *jobs, struct kcopyd_job *job) +{ + int flags; + + spin_lock_irqsave(&_job_lock, flags); + list_add(&job->list, jobs); + spin_unlock_irqrestore(&_job_lock, flags); +} + +/* + * Completion function for one of our buffers. + */ +static void end_bh(struct buffer_head *bh, int uptodate) +{ + struct kcopyd_job *job = bh->b_private; + + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + + if (!uptodate) + job->err = -EIO; + + /* are we the last ? */ + if (atomic_dec_and_test(&job->nr_incomplete)) { + push(&_complete_jobs, job); + wake_kcopyd(); + } + + free_buffer(bh); +} + +static void dispatch_bh(struct kcopyd_job *job, + struct buffer_head *bh, int block) +{ + int p; + + /* + * Add in the job offset + */ + bh->b_blocknr = (job->disk.sector >> job->block_shift) + block; + + p = block >> job->bpp_shift; + block &= job->bpp_mask; + + bh->b_dev = B_FREE; + bh->b_size = job->block_size; + set_bh_page(bh, job->pages[p], ((block << job->block_shift) + + job->offset) << SECTOR_SHIFT); + bh->b_this_page = bh; + + init_buffer(bh, end_bh, job); + + bh->b_dev = job->disk.dev; + bh->b_state = ((1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req)); + + set_bit(BH_Uptodate, &bh->b_state); + if (job->rw == WRITE) + clear_bit(BH_Dirty, &bh->b_state); + + submit_bh(job->rw, bh); +} + +/* + * These three functions process 1 item from the corresponding + * job list. + * + * They return: + * < 0: error + * 0: success + * > 0: can't process yet. + */ +static int run_complete_job(struct kcopyd_job *job) +{ + job->callback(job); + return 0; +} + +/* + * Request io on as many buffer heads as we can currently get for + * a particular job. + */ +static int run_io_job(struct kcopyd_job *job) +{ + unsigned int block; + struct buffer_head *bh; + + for (block = atomic_read(&job->nr_requested); + block < job->nr_blocks; block++) { + bh = alloc_buffer(); + if (!bh) + break; + + atomic_inc(&job->nr_requested); + dispatch_bh(job, bh, block); + } + + return (block == job->nr_blocks) ? 0 : 1; +} + +static int run_pages_job(struct kcopyd_job *job) +{ + int r; + + job->nr_pages = (job->disk.count + job->offset) / + (PAGE_SIZE / SECTOR_SIZE); + r = kcopyd_get_pages(job->nr_pages, job->pages); + + if (!r) { + /* this job is ready for io */ + push(&_io_jobs, job); + return 0; + } + + if (r == -ENOMEM) + /* can complete now */ + return 1; + + return r; +} + +/* + * Run through a list for as long as possible. Returns the count + * of successful jobs. + */ +static int process_jobs(struct list_head *jobs, int (*fn) (struct kcopyd_job *)) +{ + struct kcopyd_job *job; + int r, count = 0; + + while ((job = pop(jobs))) { + + r = fn(job); + + if (r < 0) { + /* error this rogue job */ + job->err = r; + push(&_complete_jobs, job); + break; + } + + if (r > 0) { + /* + * We couldn't service this job ATM, so + * push this job back onto the list. + */ + push(jobs, job); + break; + } + + count++; + } + + return count; +} + +/* + * kcopyd does this every time it's woken up. + */ +static void do_work(void) +{ + int count; + + /* + * We loop round until there is no more work to do. + */ + do { + count = process_jobs(&_complete_jobs, run_complete_job); + count += process_jobs(&_io_jobs, run_io_job); + count += process_jobs(&_pages_jobs, run_pages_job); + + } while (count); + + run_task_queue(&tq_disk); +} + +/*----------------------------------------------------------------- + * The daemon + *---------------------------------------------------------------*/ +static atomic_t _kcopyd_must_die; +static DECLARE_MUTEX(_run_lock); +static DECLARE_WAIT_QUEUE_HEAD(_job_queue); + +static int kcopyd(void *arg) +{ + DECLARE_WAITQUEUE(wq, current); + + daemonize(); + strcpy(current->comm, "kcopyd"); + atomic_set(&_kcopyd_must_die, 0); + + add_wait_queue(&_job_queue, &wq); + + down(&_run_lock); + up(&start_lock); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (atomic_read(&_kcopyd_must_die)) + break; + + do_work(); + schedule(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&_job_queue, &wq); + + up(&_run_lock); + + return 0; +} + +static int start_daemon(void) +{ + static pid_t pid = 0; + + down(&start_lock); + + pid = kernel_thread(kcopyd, NULL, 0); + if (pid <= 0) { + DMERR("Failed to start kcopyd thread"); + return -EAGAIN; + } + + /* + * wait for the daemon to up this mutex. + */ + down(&start_lock); + up(&start_lock); + + return 0; +} + +static int stop_daemon(void) +{ + atomic_set(&_kcopyd_must_die, 1); + wake_kcopyd(); + down(&_run_lock); + up(&_run_lock); + + return 0; +} + +static void wake_kcopyd(void) +{ + wake_up_interruptible(&_job_queue); +} + +static int calc_shift(unsigned int n) +{ + int s; + + for (s = 0; n; s++, n >>= 1) + ; + + return --s; +} + +static void calc_block_sizes(struct kcopyd_job *job) +{ + job->block_size = get_hardsect_size(job->disk.dev); + job->block_shift = calc_shift(job->block_size / SECTOR_SIZE); + job->bpp_shift = PAGE_SHIFT - job->block_shift - SECTOR_SHIFT; + job->bpp_mask = (1 << job->bpp_shift) - 1; + job->nr_blocks = job->disk.count >> job->block_shift; + atomic_set(&job->nr_requested, 0); + atomic_set(&job->nr_incomplete, job->nr_blocks); +} + +int kcopyd_io(struct kcopyd_job *job) +{ + calc_block_sizes(job); + push(job->pages[0] ? &_io_jobs : &_pages_jobs, job); + wake_kcopyd(); + return 0; +} + +/*----------------------------------------------------------------- + * The copier is implemented on top of the simpler async io + * daemon above. + *---------------------------------------------------------------*/ +struct copy_info { + kcopyd_notify_fn notify; + void *notify_context; + + struct kcopyd_region to; +}; + +#define MIN_INFOS 128 +static kmem_cache_t *_copy_cache = NULL; +static mempool_t *_copy_pool = NULL; + +static int init_copier(void) +{ + _copy_cache = kmem_cache_create("kcopyd-info", + sizeof(struct copy_info), + __alignof__(struct copy_info), + 0, NULL, NULL); + if (!_copy_cache) + return -ENOMEM; + + _copy_pool = mempool_create(MIN_INFOS, mempool_alloc_slab, + mempool_free_slab, _copy_cache); + if (!_copy_pool) { + kmem_cache_destroy(_copy_cache); + return -ENOMEM; + } + + return 0; +} + +static void exit_copier(void) +{ + if (_copy_pool) + mempool_destroy(_copy_pool); + + if (_copy_cache) + kmem_cache_destroy(_copy_cache); +} + +static inline struct copy_info *alloc_copy_info(void) +{ + return mempool_alloc(_copy_pool, GFP_KERNEL); +} + +static inline void free_copy_info(struct copy_info *info) +{ + mempool_free(info, _copy_pool); +} + +void copy_complete(struct kcopyd_job *job) +{ + struct copy_info *info = (struct copy_info *) job->context; + + if (info->notify) + info->notify(job->err, info->notify_context); + + free_copy_info(info); + + kcopyd_free_pages(job->nr_pages, job->pages); + + kcopyd_free_job(job); +} + +static void page_write_complete(struct kcopyd_job *job) +{ + struct copy_info *info = (struct copy_info *) job->context; + int i; + + if (info->notify) + info->notify(job->err, info->notify_context); + + free_copy_info(info); + for (i = 0; i < job->nr_pages; i++) + put_page(job->pages[i]); + + kcopyd_free_job(job); +} + +/* + * These callback functions implement the state machine that copies regions. + */ +void copy_write(struct kcopyd_job *job) +{ + struct copy_info *info = (struct copy_info *) job->context; + + if (job->err && info->notify) { + info->notify(job->err, job->context); + kcopyd_free_job(job); + free_copy_info(info); + return; + } + + job->rw = WRITE; + memcpy(&job->disk, &info->to, sizeof(job->disk)); + job->callback = copy_complete; + job->context = info; + + /* + * Queue the write. + */ + kcopyd_io(job); +} + +int kcopyd_write_pages(struct kcopyd_region *to, int nr_pages, + struct page **pages, int offset, kcopyd_notify_fn fn, + void *context) +{ + struct copy_info *info; + struct kcopyd_job *job; + int i; + + /* + * Allocate a new copy_info. + */ + info = alloc_copy_info(); + if (!info) + return -ENOMEM; + + job = kcopyd_alloc_job(); + if (!job) { + free_copy_info(info); + return -ENOMEM; + } + + /* + * set up for the write. + */ + info->notify = fn; + info->notify_context = context; + memcpy(&info->to, to, sizeof(*to)); + + /* Get the pages */ + job->nr_pages = nr_pages; + for (i = 0; i < nr_pages; i++) { + get_page(pages[i]); + job->pages[i] = pages[i]; + } + + job->rw = WRITE; + + memcpy(&job->disk, &info->to, sizeof(job->disk)); + job->offset = offset; + calc_block_sizes(job); + job->callback = page_write_complete; + job->context = info; + + /* + * Trigger job. + */ + kcopyd_io(job); + return 0; +} + +int kcopyd_copy(struct kcopyd_region *from, struct kcopyd_region *to, + kcopyd_notify_fn fn, void *context) +{ + struct copy_info *info; + struct kcopyd_job *job; + + /* + * Allocate a new copy_info. + */ + info = alloc_copy_info(); + if (!info) + return -ENOMEM; + + job = kcopyd_alloc_job(); + if (!job) { + free_copy_info(info); + return -ENOMEM; + } + + /* + * set up for the read. + */ + info->notify = fn; + info->notify_context = context; + memcpy(&info->to, to, sizeof(*to)); + + job->rw = READ; + memcpy(&job->disk, from, sizeof(*from)); + + job->offset = 0; + calc_block_sizes(job); + job->callback = copy_write; + job->context = info; + + /* + * Trigger job. + */ + kcopyd_io(job); + return 0; +} + +/*----------------------------------------------------------------- + * Unit setup + *---------------------------------------------------------------*/ +static struct { + int (*init) (void); + void (*exit) (void); + +} _inits[] = { +#define xx(n) { init_ ## n, exit_ ## n} + xx(pages), + xx(buffers), + xx(jobs), + xx(copier) +#undef xx +}; + +static int _client_count = 0; +static DECLARE_MUTEX(_client_count_sem); + +static int kcopyd_init(void) +{ + const int count = sizeof(_inits) / sizeof(*_inits); + + int r, i; + + for (i = 0; i < count; i++) { + r = _inits[i].init(); + if (r) + goto bad; + } + + start_daemon(); + return 0; + + bad: + while (i--) + _inits[i].exit(); + + return r; +} + +static void kcopyd_exit(void) +{ + int i = sizeof(_inits) / sizeof(*_inits); + + if (stop_daemon()) + DMWARN("Couldn't stop kcopyd."); + + while (i--) + _inits[i].exit(); +} + +void kcopyd_inc_client_count(void) +{ + /* + * What I need here is an atomic_test_and_inc that returns + * the previous value of the atomic... In its absence I lock + * an int with a semaphore. :-( + */ + down(&_client_count_sem); + if (_client_count == 0) + kcopyd_init(); + _client_count++; + + up(&_client_count_sem); +} + +void kcopyd_dec_client_count(void) +{ + down(&_client_count_sem); + if (--_client_count == 0) + kcopyd_exit(); + + up(&_client_count_sem); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/kcopyd.h linux.21rc1-ac2/drivers/md/kcopyd.h --- linux.21rc1/drivers/md/kcopyd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/kcopyd.h 2003-04-25 16:05:08.000000000 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2001 Sistina Software + * + * This file is released under the GPL. + */ + +#ifndef DM_KCOPYD_H +#define DM_KCOPYD_H + +/* + * Needed for the definition of offset_t. + */ +#include +#include + +struct kcopyd_region { + kdev_t dev; + offset_t sector; + offset_t count; +}; + +#define MAX_KCOPYD_PAGES 128 + +struct kcopyd_job { + struct list_head list; + + /* + * Error state of the job. + */ + int err; + + /* + * Either READ or WRITE + */ + int rw; + + /* + * The source or destination for the transfer. + */ + struct kcopyd_region disk; + + int nr_pages; + struct page *pages[MAX_KCOPYD_PAGES]; + + /* + * Shifts and masks that will be useful when dispatching + * each buffer_head. + */ + offset_t offset; + offset_t block_size; + offset_t block_shift; + offset_t bpp_shift; /* blocks per page */ + offset_t bpp_mask; + + /* + * nr_blocks is how many buffer heads will have to be + * displatched to service this job, nr_requested is how + * many have been dispatched and nr_complete is how many + * have come back. + */ + unsigned int nr_blocks; + atomic_t nr_requested; + atomic_t nr_incomplete; + + /* + * Set this to ensure you are notified when the job has + * completed. 'context' is for callback to use. + */ + void (*callback)(struct kcopyd_job *job); + void *context; +}; + +/* + * Low level async io routines. + */ +struct kcopyd_job *kcopyd_alloc_job(void); +void kcopyd_free_job(struct kcopyd_job *job); + +int kcopyd_queue_job(struct kcopyd_job *job); + +/* + * Submit a copy job to kcopyd. This is built on top of the + * previous three fns. + */ +typedef void (*kcopyd_notify_fn)(int err, void *context); + +int kcopyd_copy(struct kcopyd_region *from, struct kcopyd_region *to, + kcopyd_notify_fn fn, void *context); + +int kcopyd_write_pages(struct kcopyd_region *to, int nr_pages, + struct page **pages, int offset, kcopyd_notify_fn fn, + void *context); + +/* + * We only want kcopyd to reserve resources if someone is + * actually using it. + */ +void kcopyd_inc_client_count(void); +void kcopyd_dec_client_count(void); + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/Makefile linux.21rc1-ac2/drivers/md/Makefile --- linux.21rc1/drivers/md/Makefile 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -4,9 +4,11 @@ O_TARGET := mddev.o -export-objs := md.o xor.o +export-objs := md.o xor.o dm-table.o dm-target.o kcopyd.o list-multi := lvm-mod.o lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o +dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ + dm-ioctl.o dm-snapshot.o dm-exception-store.o kcopyd.o # Note: link order is important. All raid personalities # and xor.o must come before md.o, as they each initialise @@ -20,8 +22,12 @@ obj-$(CONFIG_MD_MULTIPATH) += multipath.o obj-$(CONFIG_BLK_DEV_MD) += md.o obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o +obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o include $(TOPDIR)/Rules.make lvm-mod.o: $(lvm-mod-objs) $(LD) -r -o $@ $(lvm-mod-objs) + +dm-mod.o: $(dm-mod-objs) + $(LD) -r -o $@ $(dm-mod-objs) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/md/md.c linux.21rc1-ac2/drivers/md/md.c --- linux.21rc1/drivers/md/md.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/md/md.c 2003-04-22 16:44:37.000000000 +0100 @@ -77,7 +77,7 @@ */ static int sysctl_speed_limit_min = 100; -static int sysctl_speed_limit_max = 100000; +static int sysctl_speed_limit_max = 10000; static struct ctl_table_header *raid_table_header; @@ -2931,8 +2931,6 @@ * bdflush, otherwise bdflush will deadlock if there are too * many dirty RAID5 blocks. */ - current->policy = SCHED_OTHER; - current->nice = -20; md_unlock_kernel(); complete(thread->event); @@ -3454,11 +3452,6 @@ "(but not more than %d KB/sec) for reconstruction.\n", sysctl_speed_limit_max); - /* - * Resync has low priority. - */ - current->nice = 19; - is_mddev_idle(mddev); /* this also initializes IO event counters */ for (m = 0; m < SYNC_MARKS; m++) { mark[m] = jiffies; @@ -3536,16 +3529,13 @@ currspeed = (j-mddev->resync_mark_cnt)/2/((jiffies-mddev->resync_mark)/HZ +1) +1; if (currspeed > sysctl_speed_limit_min) { - current->nice = 19; - if ((currspeed > sysctl_speed_limit_max) || !is_mddev_idle(mddev)) { current->state = TASK_INTERRUPTIBLE; md_schedule_timeout(HZ/4); goto repeat; } - } else - current->nice = -20; + } } printk(KERN_INFO "md: md%d: sync done.\n",mdidx(mddev)); err = 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/media/video/bttv-driver.c linux.21rc1-ac2/drivers/media/video/bttv-driver.c --- linux.21rc1/drivers/media/video/bttv-driver.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/media/video/bttv-driver.c 2003-04-22 16:44:37.000000000 +0100 @@ -1649,28 +1649,23 @@ case VIDIOCGCHAN: { struct video_channel v; - unsigned int channel; - if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - channel = v.channel; - if (channel>=bttv_tvcards[btv->type].video_inputs) - return -EINVAL; v.flags=VIDEO_VC_AUDIO; v.tuners=0; v.type=VIDEO_TYPE_CAMERA; v.norm = btv->win.norm; - if(channel==bttv_tvcards[btv->type].tuner) + if (v.channel < 0 || v.channel >= bttv_tvcards[btv->type].video_inputs) + return -EINVAL; + if(v.channel==bttv_tvcards[btv->type].tuner) { strcpy(v.name,"Television"); v.flags|=VIDEO_VC_TUNER; v.type=VIDEO_TYPE_TV; v.tuners=1; } - else if (channel==bttv_tvcards[btv->type].svhs) + else if(v.channel==bttv_tvcards[btv->type].svhs) strcpy(v.name,"S-Video"); - else if (bttv_tvcards[btv->type].muxsel[v.channel] < 0) - strcpy(v.name,"Digital Video"); else sprintf(v.name,"Composite%d",v.channel); @@ -1684,20 +1679,17 @@ case VIDIOCSCHAN: { struct video_channel v; - unsigned int channel; - if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - channel = v.channel; - if (channel>bttv_tvcards[btv->type].video_inputs) + if (v.channel < 0 || v.channel >= bttv_tvcards[btv->type].video_inputs) return -EINVAL; if (v.norm > TVNORMS) return -EOPNOTSUPP; bttv_call_i2c_clients(btv,cmd,&v); down(&btv->lock); - bt848_muxsel(btv, channel); + bt848_muxsel(btv, v.channel); bttv_set_norm(btv, v.norm); up(&btv->lock); return 0; @@ -1726,13 +1718,10 @@ case VIDIOCSTUNER: { struct video_tuner v; - unsigned int tuner; - if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; - tuner = v.tuner; /* Only one channel has a tuner */ - if(tuner!=bttv_tvcards[btv->type].tuner) + if(v.tuner!=bttv_tvcards[btv->type].tuner) return -EINVAL; if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC @@ -1990,13 +1979,9 @@ case VIDIOCSAUDIO: { struct video_audio v; - unsigned int n; if(copy_from_user(&v,arg, sizeof(v))) return -EFAULT; - n = v.audio; - if(n >= bttv_tvcards[btv->type].audio_inputs) - return -EINVAL; down(&btv->lock); if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); @@ -2017,12 +2002,12 @@ case VIDIOCSYNC: { + int i; DECLARE_WAITQUEUE(wait, current); - unsigned int i; if(copy_from_user((void *)&i,arg,sizeof(int))) return -EFAULT; - if (i >= gbuffers) + if (i < 0 || i >= gbuffers) return -EINVAL; switch (btv->gbuf[i].stat) { case GBUFFER_UNUSED: @@ -2094,8 +2079,7 @@ case VIDIOCGMBUF: { struct video_mbuf vm; - unsigned int i; - + int i; memset(&vm, 0 , sizeof(vm)); vm.size=gbufsize*gbuffers; vm.frames=gbuffers; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/media/video/zoran_procfs.c linux.21rc1-ac2/drivers/media/video/zoran_procfs.c --- linux.21rc1/drivers/media/video/zoran_procfs.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/media/video/zoran_procfs.c 2003-04-22 16:44:37.000000000 +0100 @@ -119,7 +119,11 @@ printk(KERN_ERR "%s: write_proc: can not allocate memory\n", zr->name); return -ENOMEM; } - memcpy(string, buffer, count); + if(copy_from_user(string, buffer, count)) + { + vfree(string); + return -EFAULT; + } string[count] = 0; DEBUG2(printk(KERN_INFO "%s: write_proc: name=%s count=%lu data=%x\n", zr->name, file->f_dentry->d_name.name, count, (int) data)); ldelim = " \t\n"; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/media/video/zr36120.c linux.21rc1-ac2/drivers/media/video/zr36120.c --- linux.21rc1/drivers/media/video/zr36120.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/media/video/zr36120.c 2003-04-22 16:44:37.000000000 +0100 @@ -1700,12 +1700,12 @@ for (x=0; optr+1w; x++) { unsigned char a = iptr[x*2]; - *optr++ = a; - *optr++ = a; + __put_user(a, optr++); + __put_user(a, optr++); } /* and clear the rest of the line */ for (x*=2; optrbpl; x++) - *optr++ = 0; + __put_user(0, optr++); /* next line */ iptr += done->bpl; } @@ -1722,10 +1722,10 @@ { /* copy to doubled data to userland */ for (x=0; optrw; x++) - *optr++ = iptr[x*2]; + __put_user(iptr[x*2], optr++); /* and clear the rest of the line */ for (;optrbpl; x++) - *optr++ = 0; + __put_user(0, optr++); /* next line */ iptr += done->bpl; } @@ -1734,7 +1734,7 @@ /* API compliance: * place the framenumber (half fieldnr) in the last long */ - ((ulong*)eptr)[-1] = done->fieldnr/2; + __put_user(done->fieldnr/2, ((ulong*)eptr)-1); } /* keep the engine running */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/isense.c linux.21rc1-ac2/drivers/message/fusion/isense.c --- linux.21rc1/drivers/message/fusion/isense.c 2003-04-22 16:38:54.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/isense.c 2003-04-22 16:44:37.000000000 +0100 @@ -92,6 +92,7 @@ EXPORT_NO_SYMBOLS; MODULE_AUTHOR(MODULEAUTHOR); MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ int __init isense_init(void) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/linux_compat.h linux.21rc1-ac2/drivers/message/fusion/linux_compat.h --- linux.21rc1/drivers/message/fusion/linux_compat.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/linux_compat.h 2003-04-25 16:05:08.000000000 +0100 @@ -246,35 +246,17 @@ #endif /* - * We use our new error handling code if the kernel version is 2.5.1 or newer. + * We use our new error handling code if the kernel version is 2.4.18 or newer. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18) #define MPT_SCSI_USE_NEW_EH #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) -#define mptscsih_lock(iocp, flags) \ - spin_lock_irqsave(&iocp->FreeQlock, flags) -#else -#define mptscsih_lock(iocp, flags) \ -({ save_flags(flags); \ - cli(); \ -}) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) -#define mptscsih_unlock(iocp, flags) \ - spin_unlock_irqrestore(&iocp->FreeQlock, flags) -#else -#define mptscsih_unlock(iocp, flags) restore_flags(flags); -#endif - - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41) -#define mpt_work_struct work_struct +#define mpt_work_struct work_struct #define MPT_INIT_WORK(_task, _func, _data) INIT_WORK(_task, _func, _data) #else -#define mpt_work_struct tq_struct +#define mpt_work_struct tq_struct #define MPT_INIT_WORK(_task, _func, _data) \ ({ (_task)->sync = 0; \ (_task)->routine = (_func); \ @@ -282,6 +264,13 @@ }) #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) +#define mptscsih_sync_irq(_irq) synchronize_irq(_irq) +#else +#define mptscsih_sync_irq(_irq) synchronize_irq() +#endif + + /*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #endif /* _LINUX_COMPAT_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptbase.c linux.21rc1-ac2/drivers/message/fusion/mptbase.c --- linux.21rc1/drivers/message/fusion/mptbase.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptbase.c 2003-04-22 16:44:37.000000000 +0100 @@ -49,7 +49,7 @@ * (mailto:sjralston1@netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptbase.c,v 1.124 2002/11/01 17:58:12 pdelaney Exp $ + * $Id: mptbase.c,v 1.126 2002/12/16 15:28:45 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -208,6 +208,7 @@ static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum); static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); static int mpt_findImVolumes(MPT_ADAPTER *ioc); +static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); static void mpt_timer_expired(unsigned long data); static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); @@ -1156,7 +1157,7 @@ dprintk((KERN_INFO MYNAM ": Checking for MPT adapters...\n")); /* - * NOTE: The 929, 929X and 1030 will appear as 2 separate PCI devices, + * NOTE: The 929, 929X, 1030 and 1035 will appear as 2 separate PCI devices, * one for each channel. */ pci_for_each_dev(pdev) { @@ -1170,18 +1171,14 @@ (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929X) && (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919X) && (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030) && -#if 0 - /* FIXME! C103x family */ - (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030_ZC) && - (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1035) && -#endif + (pdev->device != MPI_MANUFACTPAGE_DEVID_1030_53C1035) && 1) { dprintk((KERN_INFO MYNAM ": Skipping LSI device=%04xh\n", pdev->device)); continue; } /* GRRRRR - * dual function devices (929, 929X, 1030) may be presented in Func 1,0 order, + * dual function devices (929, 929X, 1030, 1035) may be presented in Func 1,0 order, * but we'd really really rather have them in Func 0,1 order. * Do some kind of look ahead here... */ @@ -1445,15 +1442,24 @@ ioc->chip_type = C1030; ioc->prod_name = "LSI53C1030"; { + u8 revision; + /* 1030 Chip Fix. Disable Split transactions - * for PCIX. Set bits 4 - 6 to zero. + * for PCIX. Set bits 4 - 6 to zero if Rev < C0( = 8) */ - u16 pcixcmd = 0; - pci_read_config_word(pdev, 0x6a, &pcixcmd); - pcixcmd &= 0xFF8F; - pci_write_config_word(pdev, 0x6a, pcixcmd); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + if (revision < 0x08) { + u16 pcixcmd = 0; + pci_read_config_word(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0xFF8F; + pci_write_config_word(pdev, 0x6a, pcixcmd); + } } } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_1030_53C1035) { + ioc->chip_type = C1035; + ioc->prod_name = "LSI53C1035"; + } sprintf(ioc->name, "ioc%d", ioc->id); @@ -1465,6 +1471,12 @@ ioc->active = 0; CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + /* tack onto tail of our MPT adapter list */ + Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER); + + /* Set lookup ptr. */ + mpt_adapters[ioc->id] = ioc; + ioc->pci_irq = -1; if (pdev->irq) { r = request_irq(pdev->irq, mpt_interrupt, SA_SHIRQ, ioc->name, ioc); @@ -1477,6 +1489,8 @@ printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %s!\n", ioc->name, __irq_itoa(pdev->irq)); #endif + Q_DEL_ITEM(ioc); + mpt_adapters[ioc->id] = NULL; iounmap(mem); kfree(ioc); return -EBUSY; @@ -1493,16 +1507,11 @@ #endif } - /* tack onto tail of our MPT adapter list */ - Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER); - - /* Set lookup ptr. */ - mpt_adapters[ioc->id] = ioc; - /* NEW! 20010220 -sralston - * Check for "bound ports" (929, 929X, 1030) to reduce redundant resets. + * Check for "bound ports" (929, 929X, 1030, 1035) to reduce redundant resets. */ - if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) || (ioc->chip_type == FC929X)) + if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) + || (ioc->chip_type == C1035) || (ioc->chip_type == FC929X)) mpt_detect_bound_ports(ioc, pdev); if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) { @@ -1632,6 +1641,9 @@ printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); /* Handle the alt IOC too */ if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){ + ddlprintk((MYIOC_s_INFO_FMT + "Alt-ioc firmware upload required!\n", + ioc->name)); r = mpt_do_upload(ioc->alt_ioc, sleepFlag); if (r != 0) printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); @@ -1708,6 +1720,10 @@ */ if (ioc->facts.MsgVersion >= 0x0102) mpt_findImVolumes(ioc); + + /* Check, and possibly reset, the coalescing value + */ + mpt_read_ioc_pg_1(ioc); } GetIoUnitPage2(ioc); @@ -1746,7 +1762,7 @@ /* * mpt_detect_bound_ports - Search for PCI bus/dev_function * which matches PCI bus/dev_function (+/-1) for newly discovered 929, - * 929X or 1030. + * 929X, 1030 or 1035. * @ioc: Pointer to MPT adapter structure * @pdev: Pointer to (struct pci_dev) structure * @@ -1914,6 +1930,11 @@ sz_first = this->alloc_total; + if (this->alt_ioc != NULL) { + this->alt_ioc->alt_ioc = NULL; + this->alt_ioc = NULL; + } + mpt_adapter_disable(this, 1); if (this->pci_irq != -1) { @@ -2290,9 +2311,6 @@ ioc->reply_sz = ioc->req_sz; ioc->reply_depth = MIN(MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth); - /* 1030 - should we use a smaller DEFAULT_REPLY_DEPTH? - * FIX - */ dprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n", ioc->name, ioc->reply_sz, ioc->reply_depth)); dprintk((MYIOC_s_INFO_FMT "req_sz =%3d, req_depth =%4d\n", @@ -3252,11 +3270,7 @@ ioc->name, diag0val, diag1val)); #endif /* Write the PreventIocBoot bit */ -#if 1 if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { -#else - if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) { -#endif diag0val |= MPI_DIAG_PREVENT_IOC_BOOT; CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); } @@ -3302,11 +3316,7 @@ /* FIXME? Examine results here? */ } -#if 1 if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { -#else - if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) { -#endif /* If the DownloadBoot operation fails, the * IOC will be left unusable. This is a fatal error * case. _diag_reset will return < 0 @@ -4462,17 +4472,13 @@ mpt_findImVolumes(MPT_ADAPTER *ioc) { IOCPage2_t *pIoc2 = NULL; - IOCPage3_t *pIoc3 = NULL; ConfigPageIoc2RaidVol_t *pIocRv = NULL; - u8 *mem; dma_addr_t ioc2_dma; - dma_addr_t ioc3_dma; CONFIGPARMS cfg; ConfigPageHeader_t header; int jj; int rc = 0; int iocpage2sz; - int iocpage3sz = 0; u8 nVols, nPhys; u8 vid, vbus, vioc; @@ -4539,44 +4545,7 @@ /* No physical disks. Done. */ } else { - /* There is at least one physical disk. - * Read and save IOC Page 3 - */ - header.PageVersion = 0; - header.PageLength = 0; - header.PageNumber = 3; - header.PageType = MPI_CONFIG_PAGETYPE_IOC; - cfg.hdr = &header; - cfg.physAddr = -1; - cfg.pageAddr = 0; - cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; - cfg.dir = 0; - cfg.timeout = 0; - if (mpt_config(ioc, &cfg) != 0) - goto done_and_free; - - if (header.PageLength == 0) - goto done_and_free; - - /* Read Header good, alloc memory - */ - iocpage3sz = header.PageLength * 4; - pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma); - if (!pIoc3) - goto done_and_free; - - /* Read the Page and save the data - * into malloc'd memory. - */ - cfg.physAddr = ioc3_dma; - cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; - if (mpt_config(ioc, &cfg) == 0) { - mem = kmalloc(iocpage3sz, GFP_ATOMIC); - if (mem) { - memcpy(mem, (u8 *)pIoc3, iocpage3sz); - ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem; - } - } + mpt_read_ioc_pg_3(ioc); } done_and_free: @@ -4585,14 +4554,159 @@ pIoc2 = NULL; } + return rc; +} + +int +mpt_read_ioc_pg_3(MPT_ADAPTER *ioc) +{ + IOCPage3_t *pIoc3 = NULL; + u8 *mem; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + dma_addr_t ioc3_dma; + int iocpage3sz = 0; + + /* Free the old page + */ + if (ioc->spi_data.pIocPg3) { + kfree(ioc->spi_data.pIocPg3); + ioc->spi_data.pIocPg3 = NULL; + } + + /* There is at least one physical disk. + * Read and save IOC Page 3 + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 3; + header.PageType = MPI_CONFIG_PAGETYPE_IOC; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return 0; + + if (header.PageLength == 0) + return 0; + + /* Read Header good, alloc memory + */ + iocpage3sz = header.PageLength * 4; + pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma); + if (!pIoc3) + return 0; + + /* Read the Page and save the data + * into malloc'd memory. + */ + cfg.physAddr = ioc3_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + mem = kmalloc(iocpage3sz, GFP_ATOMIC); + if (mem) { + memcpy(mem, (u8 *)pIoc3, iocpage3sz); + ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem; + } + } + if (pIoc3) { pci_free_consistent(ioc->pcidev, iocpage3sz, pIoc3, ioc3_dma); pIoc3 = NULL; } - return rc; + return 0; } +static void +mpt_read_ioc_pg_1(MPT_ADAPTER *ioc) +{ + IOCPage1_t *pIoc1 = NULL; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + dma_addr_t ioc1_dma; + int iocpage1sz = 0; + u32 tmp; + + /* Check the Coalescing Timeout in IOC Page 1 + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 1; + header.PageType = MPI_CONFIG_PAGETYPE_IOC; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return; + + if (header.PageLength == 0) + return; + + /* Read Header good, alloc memory + */ + iocpage1sz = header.PageLength * 4; + pIoc1 = pci_alloc_consistent(ioc->pcidev, iocpage1sz, &ioc1_dma); + if (!pIoc1) + return; + + /* Read the Page and check coalescing timeout + */ + cfg.physAddr = ioc1_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + + tmp = le32_to_cpu(pIoc1->Flags) & MPI_IOCPAGE1_REPLY_COALESCING; + if (tmp == MPI_IOCPAGE1_REPLY_COALESCING) { + tmp = le32_to_cpu(pIoc1->CoalescingTimeout); + + dprintk((MYIOC_s_INFO_FMT "Coalescing Enabled Timeout = %d\n", + ioc->name, tmp)); + + if (tmp > MPT_COALESCING_TIMEOUT) { + pIoc1->CoalescingTimeout = cpu_to_le32(MPT_COALESCING_TIMEOUT); + + /* Write NVRAM and current + */ + cfg.dir = 1; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + dprintk((MYIOC_s_INFO_FMT "Reset Current Coalescing Timeout to = %d\n", + ioc->name, MPT_COALESCING_TIMEOUT)); + + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM; + if (mpt_config(ioc, &cfg) == 0) { + dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout to = %d\n", + ioc->name, MPT_COALESCING_TIMEOUT)); + } else { + dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout Failed\n", + ioc->name)); + } + + } else { + dprintk((MYIOC_s_WARN_FMT "Reset of Current Coalescing Timeout Failed!\n", + ioc->name)); + } + } + + } else { + dprintk((MYIOC_s_WARN_FMT "Coalescing Disabled\n", ioc->name)); + } + } + + if (pIoc1) { + pci_free_consistent(ioc->pcidev, iocpage1sz, pIoc1, ioc1_dma); + pIoc1 = NULL; + } + + return; +} /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -5094,8 +5208,7 @@ */ if (isense_idx == ii) len += sprintf(buf+len, " Fusion MPT isense driver\n"); - } else - break; + } } MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); @@ -5772,6 +5885,7 @@ EXPORT_SYMBOL(mpt_stm_index); EXPORT_SYMBOL(mpt_HardResetHandler); EXPORT_SYMBOL(mpt_config); +EXPORT_SYMBOL(mpt_read_ioc_pg_3); EXPORT_SYMBOL(mpt_alloc_fw_memory); EXPORT_SYMBOL(mpt_free_fw_memory); @@ -5841,6 +5955,7 @@ fusion_exit(void) { MPT_ADAPTER *this; + struct pci_dev *pdev = NULL; dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n")); @@ -5859,9 +5974,14 @@ this->active = 0; + pdev = (struct pci_dev *)this->pcidev; + mptscsih_sync_irq(pdev->irq); + /* Clear any lingering interrupt */ CHIPREG_WRITE32(&this->chip->IntStatus, 0); + CHIPREG_READ32(&this->chip->IntStatus); + Q_DEL_ITEM(this); mpt_adapter_dispose(this); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptbase.h linux.21rc1-ac2/drivers/message/fusion/mptbase.h --- linux.21rc1/drivers/message/fusion/mptbase.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptbase.h 2003-04-25 16:05:08.000000000 +0100 @@ -13,7 +13,7 @@ * (mailto:sjralston1@netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptbase.h,v 1.139 2002/11/11 18:33:40 pdelaney Exp $ + * $Id: mptbase.h,v 1.144 2003/01/28 21:31:56 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -80,8 +80,8 @@ #define COPYRIGHT "Copyright (c) 1999-2002 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "2.03.00" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.03.00" +#define MPT_LINUX_VERSION_COMMON "2.05.00+" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.05.00+" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -134,8 +134,10 @@ #define CAN_SLEEP 1 #define NO_SLEEP 0 -/* - * SCSI transfer rate defines. +#define MPT_COALESCING_TIMEOUT 0x10 + +/* + * SCSI transfer rate defines. */ #define MPT_ULTRA320 0x08 #define MPT_ULTRA160 0x09 @@ -301,6 +303,7 @@ FC919 = 0x0919, FC929 = 0x0929, C1030 = 0x1030, + C1035 = 0x1035, FCUNK = 0xFBAD } CHIP_TYPE; @@ -368,6 +371,7 @@ typedef struct _VirtDevice { struct _VirtDevice *forw; struct _VirtDevice *back; + struct scsi_device *device; rwlock_t VdevLock; int ref_cnt; u8 tflags; @@ -522,7 +526,7 @@ #define MPT_SCSICFG_DV_PENDING 0x04 /* DV on this physical id pending */ #define MPT_SCSICFG_DV_NOT_DONE 0x08 /* DV has not been performed */ #define MPT_SCSICFG_BLK_NEGO 0x10 /* WriteSDP1 with WDTR and SDTR disabled */ - +#define MPT_SCSICFG_RELOAD_IOC_PG3 0x20 /* IOC Pg 3 data is obsolete */ /* Args passed to writeSDP1: */ #define MPT_SCSICFG_USE_NVRAM 0x01 /* WriteSDP1 using NVRAM */ #define MPT_SCSICFG_ALL_IDS 0x02 /* WriteSDP1 to all IDS */ @@ -915,6 +919,7 @@ unsigned long hard_resets; /* driver forced bus resets count */ unsigned long soft_resets; /* fw/external bus resets count */ unsigned long timeouts; /* cmd timeouts */ + ushort sel_timeout[MPT_MAX_FC_DEVICES]; } MPT_SCSI_HOST; /* @@ -1006,6 +1011,7 @@ extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg); extern void *mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz); extern void mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img); +extern int mpt_read_ioc_pg_3(MPT_ADAPTER *ioc); /* * Public data decl's... @@ -1038,7 +1044,7 @@ #define offsetof(t, m) ((size_t) (&((t *)0)->m)) #endif -#if defined(__alpha__) || defined(__sparc_v9__) || defined(__ia64__) +#if defined(__alpha__) || defined(__sparc_v9__) || defined(__ia64__) || defined(__x86_64__) #define CAST_U32_TO_PTR(x) ((void *)(u64)x) #define CAST_PTR_TO_U32(x) ((u32)(u64)x) #else diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptctl.c linux.21rc1-ac2/drivers/message/fusion/mptctl.c --- linux.21rc1/drivers/message/fusion/mptctl.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptctl.c 2003-04-22 16:44:37.000000000 +0100 @@ -34,7 +34,7 @@ * (mailto:sjralston1@netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptctl.c,v 1.62 2002/11/01 17:58:13 pdelaney Exp $ + * $Id: mptctl.c,v 1.63 2002/12/03 21:26:33 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -139,14 +139,6 @@ static int mptctl_hp_hostinfo(unsigned long arg); static int mptctl_hp_targetinfo(unsigned long arg); -static int mptctl_compaq_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -static int mptctl_cpq_getpciinfo(unsigned long arg); -static int mptctl_cpq_getdriver(unsigned long arg); -static int mptctl_cpq_ctlr_status(unsigned long arg); -static int mptctl_cpq_target_address(unsigned long arg); -static int mptctl_cpq_passthru(unsigned long arg); -static int mptctl_compaq_scsiio(VENDOR_IOCTL_REQ *pVenReq, cpqfc_passthru_t *pPass); - /* * Private function calls. */ @@ -625,14 +617,6 @@ } ret = -ENXIO; /* (-6) No such device or address */ - - /* Test for Compaq-specific IOCTL's. - */ - if ((cmd == CPQFCTS_GETPCIINFO) || (cmd == CPQFCTS_CTLR_STATUS) || - (cmd == CPQFCTS_GETDRIVVER) || (cmd == CPQFCTS_SCSI_PASSTHRU) || - (cmd == CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS)) - return mptctl_compaq_ioctl(file, cmd, arg); - /* Verify intended MPT adapter - set iocnum and the adapter * pointer (iocp) */ @@ -2065,9 +2049,37 @@ } break; + case MPI_FUNCTION_IOC_INIT: + { + IOCInit_t *pInit = (IOCInit_t *) mf; + u32 high_addr, sense_high; + + /* Verify that all entries in the IOC INIT match + * existing setup (and in LE format). + */ + if (sizeof(dma_addr_t) == sizeof(u64)) { + high_addr = cpu_to_le32((u32)((u64)ioc->req_frames_dma >> 32)); + sense_high= cpu_to_le32((u32)((u64)ioc->sense_buf_pool_dma >> 32)); + } else { + high_addr = 0; + sense_high= 0; + } + + if ((pInit->Flags != 0) || (pInit->MaxDevices != ioc->facts.MaxDevices) || + (pInit->MaxBuses != ioc->facts.MaxBuses) || + (pInit->ReplyFrameSize != cpu_to_le16(ioc->reply_sz)) || + (pInit->HostMfaHighAddr != high_addr) || + (pInit->SenseBufferHighAddr != sense_high)) { + printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - " + "IOC_INIT issued with 1 or more incorrect parameters. Rejected.\n", + __FILE__, __LINE__); + rc = -EFAULT; + goto done_free_mem; + } + } + break; default: /* - * MPI_FUNCTION_IOC_INIT * MPI_FUNCTION_PORT_ENABLE * MPI_FUNCTION_TARGET_CMD_BUFFER_POST * MPI_FUNCTION_TARGET_ASSIST @@ -2554,6 +2566,7 @@ SCSIDevicePage0_t *pg0_alloc; SCSIDevicePage3_t *pg3_alloc; MPT_ADAPTER *ioc; + MPT_SCSI_HOST *hd = NULL; hp_target_info_t karg; int iocnum; int data_sz; @@ -2668,6 +2681,9 @@ pci_free_consistent(ioc->pcidev, data_sz, (u8 *) pg3_alloc, page_dma); } } + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd != NULL) + karg.select_timeouts = hd->sel_timeout[karg.hdr.id]; /* Copy the data from kernel memory to user memory */ @@ -2681,558 +2697,6 @@ return 0; } - - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* Routine for the Compaq IOCTL commands. - * - * Outputs: None. - * Return: 0 if successful - * -EBUSY if previous command timout and IOC reset is not complete. - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - * -ETIME if timer expires - * -ENOMEM if memory allocation error - */ -static int -mptctl_compaq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int iocnum = 0; - unsigned iocnumX = 0; - int ret; - int nonblock = (file->f_flags & O_NONBLOCK); - MPT_ADAPTER *iocp = NULL; - - if (cmd == CPQFCTS_SCSI_PASSTHRU) { - /* Update the iocnum */ - if (copy_from_user(&iocnumX, (int *)arg, sizeof(int))) { - printk(KERN_ERR "%s::mptctl_compaq_ioctl() @%d - " - "Unable to read controller number @ %p\n", - __FILE__, __LINE__, (void*)arg); - return -EFAULT; - } - iocnumX &= 0xFF; - } - - if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) || - (iocp == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_compaq_ioctl() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnumX)); - return -ENODEV; - } - - /* All of these commands require an interrupt or - * are unknown/illegal. - */ - if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0) - return ret; - - dctlprintk((MYIOC_s_INFO_FMT ": mptctl_compaq_ioctl()\n", iocp->name)); - - switch(cmd) { - case CPQFCTS_GETPCIINFO: - ret = mptctl_cpq_getpciinfo(arg); - break; - case CPQFCTS_GETDRIVVER: - ret = mptctl_cpq_getdriver(arg); - break; - case CPQFCTS_CTLR_STATUS: - ret = mptctl_cpq_ctlr_status(arg); - break; - case CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS: - ret = mptctl_cpq_target_address(arg); - break; - case CPQFCTS_SCSI_PASSTHRU: - ret = mptctl_cpq_passthru(arg); - break; - default: - ret = -EINVAL; - } - - up(&mptctl_syscall_sem_ioc[iocp->id]); - - return ret; - -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_cpq_getpciinfo - Get PCI Information in format desired by Compaq - * - * Outputs: None. - * Return: 0 if successful - * -EBUSY if previous command timout and IOC reset is not complete. - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - * -ETIME if timer expires - */ -static int -mptctl_cpq_getpciinfo(unsigned long arg) -{ - cpqfc_pci_info_struct *uarg = (cpqfc_pci_info_struct *) arg; - cpqfc_pci_info_struct karg; - MPT_ADAPTER *ioc; - struct pci_dev *pdev; - CONFIGPARMS cfg; - ConfigPageHeader_t hdr; - int iocnum = 0, iocnumX = 0; - dma_addr_t buf_dma; - u8 *pbuf = NULL; - int failed; - - dctlprintk((": mptctl_cpq_pciinfo called.\n")); - if (copy_from_user(&karg, uarg, sizeof(cpqfc_pci_info_struct))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_pciinfo - " - "Unable to read in cpqfc_pci_info_struct @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EINVAL; - } - - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_pciinfo() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum)); - return -ENODEV; - } - - pdev = (struct pci_dev *) ioc->pcidev; - - /* Populate the structure. */ - karg.bus = pdev->bus->number; - karg.bus_type = 1; /* 1 = PCI; 4 = unknown */ - karg.device_fn = PCI_FUNC(pdev->devfn); - karg.slot_number = PCI_SLOT(pdev->devfn); - karg.vendor_id = pdev->vendor; - karg.device_id = pdev->device; - karg.board_id = (karg.device_id | (karg.vendor_id << 16)); - karg.class_code = pdev->class; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - karg.sub_vendor_id = pdev->subsystem_vendor; - karg.sub_device_id = pdev->subsystem_device; -#endif - - /* Issue a config request to get the device serial number - */ - hdr.PageVersion = 0; - hdr.PageLength = 0; - hdr.PageNumber = 0; - hdr.PageType = MPI_CONFIG_PAGETYPE_MANUFACTURING; - cfg.hdr = &hdr; - cfg.physAddr = -1; - cfg.pageAddr = 0; - cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; - cfg.dir = 0; /* read */ - cfg.timeout = 10; - - failed = 1; - - if (mpt_config(ioc, &cfg) == 0) { - if (cfg.hdr->PageLength > 0) { - /* Issue the second config page request */ - cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; - - pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma); - if (pbuf) { - cfg.physAddr = buf_dma; - if (mpt_config(ioc, &cfg) == 0) { - ManufacturingPage0_t *pdata = (ManufacturingPage0_t *) pbuf; - strncpy(karg.serial_number, pdata->BoardTracerNumber, 17); - failed = 0; - } - pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma); - pbuf = NULL; - } - } - } - if (failed) - strncpy(karg.serial_number, " ", 17); - - /* Copy the data from kernel memory to user memory - */ - if (copy_to_user((char *)arg, &karg, - sizeof(cpqfc_pci_info_struct))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_pciinfo - " - "Unable to write out cpqfc_pci_info_struct @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_cpq_getdriver - Get Driver Version in format desired by Compaq - * - * Outputs: None. - * Return: 0 if successful - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - */ -static int -mptctl_cpq_getdriver(unsigned long arg) -{ - int *uarg = (int *)arg; - int karg; - MPT_ADAPTER *ioc = NULL; - int iocnum = 0, iocnumX = 0; - int ii, jj; - char version[10]; - char val; - char *vptr = NULL; - char *pptr = NULL; - - dctlprintk((": mptctl_cpq_getdriver called.\n")); - if (copy_from_user(&karg, uarg, sizeof(int))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_getdriver - " - "Unable to read in struct @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_cpq_getdriver() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum)); - return -ENODEV; - } - - strncpy(version, MPT_LINUX_VERSION_COMMON, 8); - - karg = 0; - vptr = version; - ii = 3; - while (ii > 0) { - pptr = strchr(vptr, '.'); - if (pptr) { - *pptr = '\0'; - val = 0; - for (jj=0; vptr[jj]>='0' && vptr[jj]<='9'; jj++) - val = 10 * val + (vptr[jj] - '0'); - karg |= (val << (8*ii)); - pptr++; - vptr = pptr; - } else - break; - ii--; - } - - /* Copy the data from kernel memory to user memory - */ - if (copy_to_user((char *)arg, &karg, - sizeof(int))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_getdriver - " - "Unable to write out stuct @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_cpq_ctlr_status - Get controller status in format desired by Compaq - * - * Outputs: None. - * Return: 0 if successful - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - */ -static int -mptctl_cpq_ctlr_status(unsigned long arg) -{ - cpqfc_ctlr_status *uarg = (cpqfc_ctlr_status *) arg; - cpqfc_ctlr_status karg; - MPT_ADAPTER *ioc; - int iocnum = 0, iocnumX = 0; - - dctlprintk((": mptctl_cpq_pciinfo called.\n")); - if (copy_from_user(&karg, uarg, sizeof(cpqfc_ctlr_status))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_ctlr_status - " - "Unable to read in cpqfc_ctlr_status @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_cpq_ctlr_status() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum)); - return -ENODEV; - } - - karg.status = ioc->last_state; - karg.offline_reason = 0; - - /* Copy the data from kernel memory to user memory - */ - if (copy_to_user((char *)arg, &karg, - sizeof(cpqfc_ctlr_status))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_ctlr_status - " - "Unable to write out cpqfc_ctlr_status @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_cpq_target_address - Get WWN Information in format desired by Compaq - * - * Outputs: None. - * Return: 0 if successful - * -EBUSY if previous command timout and IOC reset is not complete. - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - * -ETIME if timer expires - */ -static int -mptctl_cpq_target_address(unsigned long arg) -{ - Scsi_FCTargAddress *uarg = (Scsi_FCTargAddress *) arg; - Scsi_FCTargAddress karg; - MPT_ADAPTER *ioc; - int iocnum = 0, iocnumX = 0; - CONFIGPARMS cfg; - ConfigPageHeader_t hdr; - dma_addr_t buf_dma; - u8 *pbuf = NULL; - FCPortPage0_t *ppp0; - int ii, failed; - u32 low, high; - - dctlprintk((": mptctl_cpq_target_address called.\n")); - if (copy_from_user(&karg, uarg, sizeof(Scsi_FCTargAddress))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_target_address - " - "Unable to read in Scsi_FCTargAddress @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_cpq_target_address() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum)); - return -ENODEV; - } - - karg.host_port_id = 0; - - /* Issue a config request to get the device wwn - */ - hdr.PageVersion = 0; - hdr.PageLength = 0; - hdr.PageNumber = 0; - hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT; - cfg.hdr = &hdr; - cfg.physAddr = -1; - cfg.pageAddr = 0; - cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; - cfg.dir = 0; /* read */ - cfg.timeout = 10; - - failed = 1; - - if (mpt_config(ioc, &cfg) == 0) { - if (cfg.hdr->PageLength > 0) { - /* Issue the second config page request */ - cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; - - pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma); - if (pbuf) { - cfg.physAddr = buf_dma; - if (mpt_config(ioc, &cfg) == 0) { - ppp0 = (FCPortPage0_t *) pbuf; - - low = le32_to_cpu(ppp0->WWNN.Low); - high = le32_to_cpu(ppp0->WWNN.High); - - for (ii = 0; ii < 4; ii++) { - karg.host_wwn[7-ii] = low & 0xFF; - karg.host_wwn[3-ii] = high & 0xFF; - low = (low >> 8); - high = (high >> 8); - } - failed = 0; - } - pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma); - pbuf = NULL; - } - } - } - - if (failed) { - for (ii = 7; ii >= 0; ii--) - karg.host_wwn[ii] = 0; - } - - /* Copy the data from kernel memory to user memory - */ - if (copy_to_user((char *)arg, &karg, - sizeof(Scsi_FCTargAddress))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_target_address - " - "Unable to write out Scsi_FCTargAddress @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_cpq_passthru - Construct and issue a SCSI IO Passthru - * - * Requires the SCSI host driver to be loaded. - * I386 version. - * - * Outputs: None. - * Return: 0 if successful - * -EBUSY if previous command timout and IOC reset is not complete. - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - * -ETIME if timer expires - */ -static int -mptctl_cpq_passthru(unsigned long arg) -{ - VENDOR_IOCTL_REQ *uarg = (VENDOR_IOCTL_REQ *) arg; - VENDOR_IOCTL_REQ karg; - cpqfc_passthru_t kpass; - MPT_ADAPTER *ioc; - int iocnum = 0, iocnumX = 0; - int rc; - - dctlprintk((": mptctl_cpq_passthru called.\n")); - if (copy_from_user(&karg, uarg, sizeof(VENDOR_IOCTL_REQ))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_passthru - " - "Unable to read in VENDOR_IOCTL_REQ @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - /* Set the IOC number */ - iocnumX = karg.lc & 0xFF; - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR "%s::mptctl_cpq_passthru() @%d - ioc%d not found!\n", - __FILE__, __LINE__, iocnum)); - return -ENODEV; - } - - if (ioc->sh == NULL) { - printk(KERN_ERR "%s::mptctl_cpq_passthru() @%d - SCSI Host driver not loaded!\n", - __FILE__, __LINE__); - return -EFAULT; - } - - /* Read in the second buffer */ - if (copy_from_user(&kpass, uarg->argp, sizeof(cpqfc_passthru_t))) { - printk(KERN_ERR "%s@%d::mptctl_cpq_passthru - " - "Unable to read in cpqfc_passthru_t @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - - /* Generate the SCSI IO command and issue */ - rc = mptctl_compaq_scsiio(&karg, &kpass); - return rc; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_compaq_scsiio - Reformat Compaq structures into driver structures - * Call the generic _do_mpt_command function. - * - * Requires the SCSI host driver to be loaded. - * I386 version. - * - * Outputs: None. - * Return: 0 if successful - * -EBUSY if previous command timout and IOC reset is not complete. - * -EFAULT if data unavailable - * -ENODEV if no such device/adapter - * -ETIME if timer expires - */ -static int -mptctl_compaq_scsiio(VENDOR_IOCTL_REQ *pVenReq, cpqfc_passthru_t *pPass) -{ - struct mpt_ioctl_command karg; - SCSIIORequest_t request ; - SCSIIORequest_t *pMf; - int ii, rc; - u8 opcode; - - /* Fill in parameters to karg */ - karg.hdr.iocnum = pVenReq->lc; - karg.hdr.port = 0; - karg.hdr.maxDataSize = 0; /* not used */ - karg.timeout = 0; /* use default */ - - karg.replyFrameBufPtr = NULL; /* no reply data */ - karg.maxReplyBytes = 0; - - karg.senseDataPtr = pPass->sense_data; - karg.maxSenseBytes = pPass->sense_len; /* max is 40 */ - - if (pPass->rw_flag == MPT_COMPAQ_WRITE) { - karg.dataOutBufPtr = pPass->bufp; - karg.dataOutSize = pPass->len; - karg.dataInBufPtr = NULL; - karg.dataInSize = 0; - } else { - karg.dataInBufPtr = pPass->bufp; - karg.dataInSize = pPass->len; - karg.dataOutBufPtr = NULL; - karg.dataOutSize = 0; - } - - karg.dataSgeOffset = (sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION))/4; - - /* Construct the Message frame */ - pMf = &request; - - pMf->TargetID = (u8) pVenReq->ld; /* ???? FIXME */ - pMf->Bus = (u8) pPass->bus; - pMf->ChainOffset = 0; - pMf->Function = MPI_FUNCTION_SCSI_IO_REQUEST; - - /* May need some tweaking here */ - opcode = (u8) pPass->cdb[0]; - if (opcode < 0x20) - pMf->CDBLength = 6; - else if (opcode < 0x60) - pMf->CDBLength = 10; - else if ((opcode < 0xC0) && (opcode >= 0xA0)) - pMf->CDBLength = 12; - else - pMf->CDBLength = 16; - - pMf->SenseBufferLength = karg.maxSenseBytes; /* max is 40 */ - pMf->Reserved = 0; - pMf->MsgFlags = 0; /* set later */ - pMf->MsgContext = 0; /* set later */ - - for (ii = 0; ii < 8; ii++) - pMf->LUN[ii] = 0; - pMf->LUN[1] = 0; /* ???? FIXME */ - - /* Tag values set by _do_mpt_command */ - if (pPass->rw_flag == MPT_COMPAQ_WRITE) - pMf->Control = MPI_SCSIIO_CONTROL_WRITE; - else - pMf->Control = MPI_SCSIIO_CONTROL_READ; - - for (ii = 0; ii < 16; ii++) - pMf->CDB[ii] = pPass->cdb[ii]; - - pMf->DataLength = pPass->len; - - /* All remaining fields are set by the next function - */ - rc = mptctl_do_mpt_command (karg, (char *)pMf, 1); - return rc; -} - - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51) @@ -3371,70 +2835,6 @@ return ret; } -static int -sparc32_mptctl_cpq_passthru(unsigned int fd, unsigned int cmd, - unsigned long arg, struct file *filp) -{ - VENDOR_IOCTL_REQ32 *uarg = (VENDOR_IOCTL_REQ32 *) arg; - VENDOR_IOCTL_REQ32 karg32; - VENDOR_IOCTL_REQ karg; - cpqfc_passthru32_t kpass32; - cpqfc_passthru_t kpass; - MPT_ADAPTER *ioc; - int nonblock = (filp->f_flags & O_NONBLOCK); - int iocnum = 0, iocnumX = 0; - int rc; - int ii; - - dctlprintk((KERN_INFO MYNAM "::sparc32_mptctl_cpq_passthru() called\n")); - - if (copy_from_user(&karg32, (char *)arg, sizeof(karg32))) - return -EFAULT; - - /* Verify intended MPT adapter */ - iocnumX = karg32.lc & 0xFF; - if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) || - (ioc == NULL)) { - dctlprintk((KERN_ERR MYNAM "::sparc32_mpt_command @%d - ioc%d not found!\n", - __LINE__, iocnumX)); - return -ENODEV; - } - - if ((rc = mptctl_syscall_down(ioc, nonblock)) != 0) - return rc; - - /* Copy data to karg */ - karg.ld = karg32.ld; - karg.node = karg32.node; - karg.lc = karg32.lc; - karg.nexus = karg32.nexus; - karg.argp = (void *)(unsigned long)karg32.argp; - - /* Read in the second buffer */ - if (copy_from_user(&kpass32, karg.argp, sizeof(cpqfc_passthru32_t))) { - printk(KERN_ERR "%s@%d::sparc32_mptctl_cpq_passthru - " - "Unable to read in cpqfc_passthru_t @ %p\n", - __FILE__, __LINE__, (void*)uarg); - return -EFAULT; - } - - /* Copy the 32bit buffer to kpass */ - for (ii = 0; ii < 16; ii++) - kpass.cdb[ii] = kpass32.cdb[ii]; - kpass.bus = kpass32.bus; - kpass.pdrive = kpass32.pdrive; - kpass.len = kpass32.len; - kpass.sense_len = kpass32.sense_len; - kpass.bufp = (void *)(unsigned long)kpass32.bufp; - kpass.rw_flag = kpass32.rw_flag; - - /* Generate the SCSI IO command and issue */ - rc = mptctl_compaq_scsiio(&karg, &kpass); - - up(&mptctl_syscall_sem_ioc[ioc->id]); - return rc; -} - #endif /*} linux >= 2.3.x */ #endif /*} sparc */ @@ -3503,16 +2903,6 @@ err = register_ioctl32_conversion(MPTFWDOWNLOAD32, sparc32_mptfwxfer_ioctl); if (++where && err) goto out_fail; - err = register_ioctl32_conversion(CPQFCTS_GETPCIINFO, NULL); - if (++where && err) goto out_fail; - err = register_ioctl32_conversion(CPQFCTS_CTLR_STATUS, NULL); - if (++where && err) goto out_fail; - err = register_ioctl32_conversion(CPQFCTS_GETDRIVVER, NULL); - if (++where && err) goto out_fail; - err = register_ioctl32_conversion(CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS, NULL); - if (++where && err) goto out_fail; - err = register_ioctl32_conversion(CPQFCTS_SCSI_PASSTHRU32, sparc32_mptctl_cpq_passthru); - if (++where && err) goto out_fail; err = register_ioctl32_conversion(HP_GETHOSTINFO, NULL); if (++where && err) goto out_fail; err = register_ioctl32_conversion(HP_GETTARGETINFO, NULL); @@ -3521,9 +2911,9 @@ #endif /*} sparc */ /* Register this device */ - if (misc_register(&mptctl_miscdev) == -1) { + err = misc_register(&mptctl_miscdev); + if (err < 0) { printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR); - err = -EBUSY; goto out_fail; } printk(KERN_INFO MYNAM ": Registered with Fusion MPT base driver\n"); @@ -3564,11 +2954,6 @@ unregister_ioctl32_conversion(MPTHARDRESET); unregister_ioctl32_conversion(MPTCOMMAND32); unregister_ioctl32_conversion(MPTFWDOWNLOAD32); - unregister_ioctl32_conversion(CPQFCTS_GETPCIINFO); - unregister_ioctl32_conversion(CPQFCTS_GETDRIVVER); - unregister_ioctl32_conversion(CPQFCTS_CTLR_STATUS); - unregister_ioctl32_conversion(CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS); - unregister_ioctl32_conversion(CPQFCTS_SCSI_PASSTHRU32); unregister_ioctl32_conversion(HP_GETHOSTINFO); unregister_ioctl32_conversion(HP_GETTARGETINFO); #endif /*} linux >= 2.3.x */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptctl.h linux.21rc1-ac2/drivers/message/fusion/mptctl.h --- linux.21rc1/drivers/message/fusion/mptctl.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptctl.h 2003-04-22 17:37:24.000000000 +0100 @@ -20,7 +20,7 @@ * (mailto:sjralston1@netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptctl.h,v 1.12 2002/10/17 20:15:58 pdelaney Exp $ + * $Id: mptctl.h,v 1.13 2002/12/03 21:26:33 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -310,103 +310,15 @@ #endif /*}*/ - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - /* - * COMPAQ Specific IOCTL Defines and Structures - */ - -#define CPQFCTS_IOC_MAGIC 'Z' - -#define CPQFCTS_GETPCIINFO _IOR(CPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct) -#define CPQFCTS_GETDRIVVER _IOR(CPQFCTS_IOC_MAGIC, 9, int) -#define CPQFCTS_CTLR_STATUS _IOR(CPQFCTS_IOC_MAGIC, 3, struct _cpqfc_ctlr_status) -#define CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS _IOR(CPQFCTS_IOC_MAGIC, 13, struct scsi_fctargaddress) -#define CPQFCTS_SCSI_PASSTHRU _IOWR(CPQFCTS_IOC_MAGIC, 11, VENDOR_IOCTL_REQ) -#if defined(__sparc__) && defined(__sparc_v9__) -#define CPQFCTS_SCSI_PASSTHRU32 _IOWR(CPQFCTS_IOC_MAGIC, 11, VENDOR_IOCTL_REQ32) -#endif - -typedef struct { - unsigned short bus; - unsigned short bus_type; - unsigned short device_fn; - u32 board_id; - u32 slot_number; - unsigned short vendor_id; - unsigned short device_id; - unsigned short class_code; - unsigned short sub_vendor_id; - unsigned short sub_device_id; - u8 serial_number[81]; -} cpqfc_pci_info_struct; - - -typedef struct scsi_fctargaddress { - unsigned int host_port_id; - u8 host_wwn[8]; /* WW Network Name */ -} Scsi_FCTargAddress; - -typedef struct _cpqfc_ctlr_status { - u32 status; - u32 offline_reason; -} cpqfc_ctlr_status; - - -/* Compaq SCSI I/O Passthru structures. - */ -#define MPT_COMPAQ_READ 0x26 -#define MPT_COMPAQ_WRITE 0x27 - -typedef struct { - int lc; /* controller number */ - int node; /* node number */ - int ld; /* target logical id */ - u32 nexus; - void *argp; -} VENDOR_IOCTL_REQ; - -#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/ -typedef struct { - int lc; /* controller number */ - int node; /* node number */ - int ld; /* target logical id */ - u32 nexus; - u32 argp; -} VENDOR_IOCTL_REQ32; -#endif - -typedef struct { - char cdb[16]; /* cdb */ - unsigned short bus; /* bus number */ - unsigned short pdrive; /* physical drive */ - int len; /* data area size */ - int sense_len; /* sense size */ - char sense_data[40]; /* sense buffer */ - void *bufp; /* data buffer pointer */ - char rw_flag; -} cpqfc_passthru_t; - -#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/ -typedef struct { - char cdb[16]; /* cdb */ - unsigned short bus; /* bus number */ - unsigned short pdrive; /* physical drive */ - int len; /* data area size */ - int sense_len; /* sense size */ - char sense_data[40]; /* sense buffer */ - u32 bufp; /* data buffer pointer */ - char rw_flag; -} cpqfc_passthru32_t; -#endif - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * HP Specific IOCTL Defines and Structures */ -#define HP_GETHOSTINFO _IOR(CPQFCTS_IOC_MAGIC, 20, hp_host_info_t) -#define HP_GETTARGETINFO _IOR(CPQFCTS_IOC_MAGIC, 21, hp_target_info_t) +#define CPQFCTS_IOC_MAGIC 'Z' +#define HP_IOC_MAGIC 'Z' +#define HP_GETHOSTINFO _IOR(HP_IOC_MAGIC, 20, hp_host_info_t) +#define HP_GETTARGETINFO _IOR(HP_IOC_MAGIC, 21, hp_target_info_t) /* All HP IOCTLs must include this header */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptlan.h linux.21rc1-ac2/drivers/message/fusion/mptlan.h --- linux.21rc1/drivers/message/fusion/mptlan.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptlan.h 2003-04-25 16:05:08.000000000 +0100 @@ -21,11 +21,7 @@ #include #include #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) #include -#else -#include -#endif #include // #include diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptscsih.c linux.21rc1-ac2/drivers/message/fusion/mptscsih.c --- linux.21rc1/drivers/message/fusion/mptscsih.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptscsih.c 2003-04-22 16:44:37.000000000 +0100 @@ -26,7 +26,7 @@ * (mailto:sjralston1@netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptscsih.c,v 1.103 2002/10/17 20:15:59 pdelaney Exp $ + * $Id: mptscsih.c,v 1.106 2003/01/28 21:31:57 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -78,7 +78,9 @@ #include /* notifier code */ #include "../../scsi/scsi.h" #include "../../scsi/hosts.h" +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) #include "../../scsi/sd.h" +#endif #include "mptbase.h" #include "mptscsih.h" @@ -157,11 +159,9 @@ static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq); static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); -static int mptscsih_io_direction(Scsi_Cmnd *cmd); static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, SCSIIORequest_t *pReq, int req_idx); -static int mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex); static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx); static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init); static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); @@ -272,147 +272,579 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* - * mptscsih_io_done - Main SCSI IO callback routine registered to - * Fusion MPT (base) driver - * @ioc: Pointer to MPT_ADAPTER structure - * @mf: Pointer to original MPT request frame - * @r: Pointer to MPT reply frame (NULL if TurboReply) + * Private inline routines... + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* 19991030 -sralston + * Return absolute SCSI data direction: + * 1 = _DATA_OUT + * 0 = _DIR_NONE + * -1 = _DATA_IN * - * This routine is called from mpt.c::mpt_interrupt() at the completion - * of any SCSI IO request. - * This routine is registered with the Fusion MPT (base) driver at driver - * load/init time via the mpt_register() API call. + * Changed: 3-20-2002 pdelaney to use the default data + * direction and the defines set up in the + * 2.4 kernel series + * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1) + * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3) + * -1 = _DATA_IN changed to SCSI_DATA_READ (2) + * If the direction is unknown, fall through to original code. * - * Returns 1 indicating alloc'd request frame ptr should be freed. + * Mid-layer bug fix(): sg interface generates the wrong data + * direction in some cases. Set the direction the hard way for + * the most common commands. */ -static int -mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) +static inline int +mptscsih_io_direction(Scsi_Cmnd *cmd) { - Scsi_Cmnd *sc; - MPT_SCSI_HOST *hd; - SCSIIORequest_t *pScsiReq; - SCSIIOReply_t *pScsiReply; -#ifndef MPT_SCSI_USE_NEW_EH - unsigned long flags; -#endif - u16 req_idx; + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + return SCSI_DATA_WRITE; + break; + case READ_6: + case READ_10: + return SCSI_DATA_READ; + break; + } - hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN) + return cmd->sc_data_direction; +#endif + switch (cmd->cmnd[0]) { + /* _DATA_OUT commands */ + case WRITE_6: case WRITE_10: case WRITE_12: + case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: + case WRITE_VERIFY: case WRITE_VERIFY_12: + case COMPARE: case COPY: case COPY_VERIFY: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: + case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: + case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case REASSIGN_BLOCKS: + case PERSISTENT_RESERVE_OUT: + case 0xea: + case 0xa3: + return SCSI_DATA_WRITE; - if ((mf == NULL) || - (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { - printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n", - ioc->name, mf?"BAD":"NULL", (void *) mf); - /* return 1; CHECKME SteveR. Don't free. */ - return 0; - } + /* No data transfer commands */ + case SEEK_6: case SEEK_10: + case RESERVE: case RELEASE: + case TEST_UNIT_READY: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + return SCSI_DATA_NONE; - req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); - sc = hd->ScsiLookup[req_idx]; - if (sc == NULL) { - MPIHeader_t *hdr = (MPIHeader_t *)mf; + /* Conditional data transfer commands */ + case FORMAT_UNIT: + if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; - atomic_dec(&queue_depth); + case VERIFY: + if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; - /* writeSDP1 will use the ScsiDoneCtx - * There is no processing for the reply. - * Just return to the calling function. - */ - if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) - printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name); + case RESERVE_10: + if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; - mptscsih_freeChainBuffers(hd, req_idx); - return 1; + /* Must be data _IN! */ + default: + return SCSI_DATA_READ; } +} /* mptscsih_io_direction() */ - dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", - ioc->name, mf, mr, sc, req_idx)); +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_add_sge - Place a simple SGE at address pAddr. + * @pAddr: virtual address for SGE + * @flagslength: SGE flags and data transfer length + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +static inline void +mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGESimple64_t *pSge = (SGESimple64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pSge->Address.High = cpu_to_le32(tmp); - atomic_dec(&queue_depth); + } else { + SGESimple32_t *pSge = (SGESimple32_t *) pAddr; + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address = cpu_to_le32(dma_addr); + } +} /* mptscsih_add_sge() */ - sc->result = DID_OK << 16; /* Set default reply as OK */ - pScsiReq = (SCSIIORequest_t *) mf; - pScsiReply = (SCSIIOReply_t *) mr; +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_add_chain - Place a chain SGE at address pAddr. + * @pAddr: virtual address for SGE + * @next: nextChainOffset value (u32's) + * @length: length of next SGL segment + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +static inline void +mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGEChain64_t *pChain = (SGEChain64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + + pChain->NextChainOffset = next; + + pChain->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pChain->Address.High = cpu_to_le32(tmp); + } else { + SGEChain32_t *pChain = (SGEChain32_t *) pAddr; + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + pChain->NextChainOffset = next; + pChain->Address = cpu_to_le32(dma_addr); + } +} /* mptscsih_add_chain() */ - if (pScsiReply == NULL) { - /* special context reply handling */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_getFreeChainBuffes - Function to get a free chain + * from the MPT_SCSI_HOST FreeChainQ. + * @hd: Pointer to the MPT_SCSI_HOST instance + * @req_idx: Index of the SCSI IO request frame. (output) + * + * return SUCCESS or FAILED + */ +static inline int +mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex) +{ + MPT_FRAME_HDR *chainBuf = NULL; + unsigned long flags; + int rc = FAILED; + int chain_idx = MPT_HOST_NO_CHAIN; - /* If regular Inquiry cmd - save inquiry data - */ - if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) { - int dlen; + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + if (!Q_IS_EMPTY(&hd->FreeChainQ)) { - dlen = le32_to_cpu(pScsiReq->DataLength); - if (dlen >= SCSI_STD_INQUIRY_BYTES) { - mptscsih_initTarget(hd, - hd->port, - sc->target, - pScsiReq->LUN[1], - sc->buffer, - dlen); - } - } -#ifdef MPT_SAVE_AUTOSENSE - clear_sense_flag(hd, pScsiReq); -#endif - } else { - u32 xfer_cnt; - u16 status; - u8 scsi_state; + int offset; - status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; - scsi_state = pScsiReply->SCSIState; + chainBuf = hd->FreeChainQ.head; + Q_DEL_ITEM(&chainBuf->u.frame.linkage); + offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer; + chain_idx = offset / hd->ioc->req_sz; + rc = SUCCESS; + } + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", - ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], - mf, mr, sc)); - dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" - ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", - status, scsi_state, pScsiReply->SCSIStatus, - le32_to_cpu(pScsiReply->IOCLogInfo))); - if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) - copy_sense_data(sc, hd, mf, pScsiReply); + *retIndex = chain_idx; - /* - * Look for + dump FCP ResponseInfo[]! - */ - if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { - dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n", - le32_to_cpu(pScsiReply->ResponseInfo))); - } + dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", + hd->ioc->name, *retIndex, chainBuf)); - switch(status) { - case MPI_IOCSTATUS_BUSY: /* 0x0002 */ - /* CHECKME! - * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) - * But not: DID_BUS_BUSY lest one risk - * killing interrupt handler:-( - */ - sc->result = STS_BUSY; - break; + return rc; +} /* mptscsih_getFreeChainBuffer() */ - case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ - case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */ - sc->result = DID_BAD_TARGET << 16; - break; +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the + * SCSIIORequest_t Message Frame. + * @hd: Pointer to MPT_SCSI_HOST structure + * @SCpnt: Pointer to Scsi_Cmnd structure + * @pReq: Pointer to SCSIIORequest_t structure + * + * Returns ... + */ +static int +mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, + SCSIIORequest_t *pReq, int req_idx) +{ + char *psge; + char *chainSge; + struct scatterlist *sg; + int frm_sz; + int sges_left, sg_done; + int chain_idx = MPT_HOST_NO_CHAIN; + int sgeOffset; + int numSgeSlots, numSgeThisFrame; + u32 sgflags, sgdir, thisxfer = 0; + int chain_dma_off = 0; + int newIndex; + int ii; + dma_addr_t v2; - case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ - /* Spoof to SCSI Selection Timeout! */ - sc->result = DID_NO_CONNECT << 16; - break; + sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; + if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { + sgdir = MPT_TRANSFER_HOST_TO_IOC; + } else { + sgdir = MPT_TRANSFER_IOC_TO_HOST; + } - case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ -#ifndef MPT_SCSI_USE_NEW_EH - search_taskQ_for_cmd(sc, hd); -#endif - /* Linux handles an unsolicited DID_RESET better - * than an unsolicited DID_ABORT. - */ - sc->result = DID_RESET << 16; + psge = (char *) &pReq->SGL; + frm_sz = hd->ioc->req_sz; - /* GEM Workaround. */ - if (hd->is_spi) + /* Map the data portion, if any. + * sges_left = 0 if no data transfer. + */ + sges_left = SCpnt->use_sg; + if (SCpnt->use_sg) { + sges_left = pci_map_sg(hd->ioc->pcidev, + (struct scatterlist *) SCpnt->request_buffer, + SCpnt->use_sg, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + } else if (SCpnt->request_bufflen) { + dma_addr_t buf_dma_addr; + scPrivate *my_priv; + + buf_dma_addr = pci_map_single(hd->ioc->pcidev, + SCpnt->request_buffer, + SCpnt->request_bufflen, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + + /* We hide it here for later unmap. */ + my_priv = (scPrivate *) &SCpnt->SCp; + my_priv->p1 = (void *)(ulong) buf_dma_addr; + + dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", + hd->ioc->name, SCpnt, SCpnt->request_bufflen)); + + mptscsih_add_sge((char *) &pReq->SGL, + 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, + buf_dma_addr); + + return SUCCESS; + } + + /* Handle the SG case. + */ + sg = (struct scatterlist *) SCpnt->request_buffer; + sg_done = 0; + sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); + chainSge = NULL; + + /* Prior to entering this loop - the following must be set + * current MF: sgeOffset (bytes) + * chainSge (Null if original MF is not a chain buffer) + * sg_done (num SGE done for this MF) + */ + +nextSGEset: + numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) ); + numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; + + sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir; + + /* Get first (num - 1) SG elements + * Skip any SG entries with a length of 0 + * NOTE: at finish, sg and psge pointed to NEXT data/location positions + */ + for (ii=0; ii < (numSgeThisFrame-1); ii++) { + thisxfer = sg_dma_len(sg); + if (thisxfer == 0) { + sg ++; /* Get next SG element from the OS */ + sg_done++; + continue; + } + + v2 = sg_dma_address(sg); + mptscsih_add_sge(psge, sgflags | thisxfer, v2); + + sg++; /* Get next SG element from the OS */ + psge += (sizeof(u32) + sizeof(dma_addr_t)); + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + sg_done++; + } + + if (numSgeThisFrame == sges_left) { + /* Add last element, end of buffer and end of list flags. + */ + sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT | + MPT_SGE_FLAGS_END_OF_BUFFER | + MPT_SGE_FLAGS_END_OF_LIST; + + /* Add last SGE and set termination flags. + * Note: Last SGE may have a length of 0 - which should be ok. + */ + thisxfer = sg_dma_len(sg); + + v2 = sg_dma_address(sg); + mptscsih_add_sge(psge, sgflags | thisxfer, v2); + /* + sg++; + psge += (sizeof(u32) + sizeof(dma_addr_t)); + */ + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + sg_done++; + + if (chainSge) { + /* The current buffer is a chain buffer, + * but there is not another one. + * Update the chain element + * Offset and Length fields. + */ + mptscsih_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + } else { + /* The current buffer is the original MF + * and there is no Chain buffer. + */ + pReq->ChainOffset = 0; + } + } else { + /* At least one chain buffer is needed. + * Complete the first MF + * - last SGE element, set the LastElement bit + * - set ChainOffset (words) for orig MF + * (OR finish previous MF chain buffer) + * - update MFStructPtr ChainIndex + * - Populate chain element + * Also + * Loop until done. + */ + + dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", + hd->ioc->name, sg_done)); + + /* Set LAST_ELEMENT flag for last non-chain element + * in the buffer. Since psge points at the NEXT + * SGE element, go back one SGE element, update the flags + * and reset the pointer. (Note: sgflags & thisxfer are already + * set properly). + */ + if (sg_done) { + u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); + sgflags = le32_to_cpu(*ptmp); + sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; + *ptmp = cpu_to_le32(sgflags); + } + + if (chainSge) { + /* The current buffer is a chain buffer. + * chainSge points to the previous Chain Element. + * Update its chain element Offset and Length (must + * include chain element size) fields. + * Old chain element is now complete. + */ + u8 nextChain = (u8) (sgeOffset >> 2); + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + } else { + /* The original MF buffer requires a chain buffer - + * set the offset. + * Last element in this MF is a chain element. + */ + pReq->ChainOffset = (u8) (sgeOffset >> 2); + } + + sges_left -= sg_done; + + + /* NOTE: psge points to the beginning of the chain element + * in current buffer. Get a chain buffer. + */ + if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED) + return FAILED; + + /* Update the tracking arrays. + * If chainSge == NULL, update ReqToChain, else ChainToChain + */ + if (chainSge) { + hd->ChainToChain[chain_idx] = newIndex; + } else { + hd->ReqToChain[req_idx] = newIndex; + } + chain_idx = newIndex; + chain_dma_off = hd->ioc->req_sz * chain_idx; + + /* Populate the chainSGE for the current buffer. + * - Set chain buffer pointer to psge and fill + * out the Address and Flags fields. + */ + chainSge = (char *) psge; + dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)", + psge, req_idx)); + + /* Start the SGE for the next buffer + */ + psge = (char *) (hd->ChainBuffer + chain_dma_off); + sgeOffset = 0; + sg_done = 0; + + dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n", + psge, chain_idx)); + + /* Start the SGE for the next buffer + */ + + goto nextSGEset; + } + + return SUCCESS; +} /* mptscsih_AddSGE() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_io_done - Main SCSI IO callback routine registered to + * Fusion MPT (base) driver + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @r: Pointer to MPT reply frame (NULL if TurboReply) + * + * This routine is called from mpt.c::mpt_interrupt() at the completion + * of any SCSI IO request. + * This routine is registered with the Fusion MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + */ +static int +mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) +{ + Scsi_Cmnd *sc; + MPT_SCSI_HOST *hd; + SCSIIORequest_t *pScsiReq; + SCSIIOReply_t *pScsiReply; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) + unsigned long flags; +#endif + u16 req_idx; + + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n", + ioc->name, mf?"BAD":"NULL", (void *) mf); + return 0; + } + + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sc = hd->ScsiLookup[req_idx]; + if (sc == NULL) { + MPIHeader_t *hdr = (MPIHeader_t *)mf; + + atomic_dec(&queue_depth); + + /* writeSDP1 will use the ScsiDoneCtx + * There is no processing for the reply. + * Just return to the calling function. + */ + if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) + printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name); + + mptscsih_freeChainBuffers(hd, req_idx); + return 1; + } + + dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", + ioc->name, mf, mr, sc, req_idx)); + + atomic_dec(&queue_depth); + + sc->result = DID_OK << 16; /* Set default reply as OK */ + pScsiReq = (SCSIIORequest_t *) mf; + pScsiReply = (SCSIIOReply_t *) mr; + + if (pScsiReply == NULL) { + /* special context reply handling */ + + /* If regular Inquiry cmd - save inquiry data + */ + if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) { + int dlen; + + dlen = le32_to_cpu(pScsiReq->DataLength); + if (dlen >= SCSI_STD_INQUIRY_BYTES) { + mptscsih_initTarget(hd, + hd->port, + sc->target, + pScsiReq->LUN[1], + sc->buffer, + dlen); + } + } +#ifdef MPT_SAVE_AUTOSENSE + clear_sense_flag(hd, pScsiReq); +#endif + } else { + u32 xfer_cnt; + u16 status; + u8 scsi_state; + + status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; + scsi_state = pScsiReply->SCSIState; + + dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", + ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], + mf, mr, sc)); + dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" + ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", + status, scsi_state, pScsiReply->SCSIStatus, + le32_to_cpu(pScsiReply->IOCLogInfo))); + + if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) + copy_sense_data(sc, hd, mf, pScsiReply); + + /* + * Look for + dump FCP ResponseInfo[]! + */ + if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { + dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n", + le32_to_cpu(pScsiReply->ResponseInfo))); + } + + switch(status) { + case MPI_IOCSTATUS_BUSY: /* 0x0002 */ + /* CHECKME! + * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) + * But not: DID_BUS_BUSY lest one risk + * killing interrupt handler:-( + */ + sc->result = STS_BUSY; + break; + + case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ + case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */ + sc->result = DID_BAD_TARGET << 16; + break; + + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ + /* Spoof to SCSI Selection Timeout! */ + sc->result = DID_NO_CONNECT << 16; + + if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) + hd->sel_timeout[pScsiReq->TargetID]++; + break; + + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ +#ifndef MPT_SCSI_USE_NEW_EH + search_taskQ_for_cmd(sc, hd); +#endif + /* Linux handles an unsolicited DID_RESET better + * than an unsolicited DID_ABORT. + */ + sc->result = DID_RESET << 16; + + /* GEM Workaround. */ + if (hd->is_spi) mptscsih_no_negotiate(hd, sc->target); break; @@ -423,7 +855,7 @@ #endif sc->result = DID_RESET << 16; - /* GEM Workaround. */ + /* GEM Workaround. */ if (hd->is_spi) mptscsih_no_negotiate(hd, sc->target); break; @@ -501,7 +933,7 @@ ; } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { /* - * If running agains circa 200003dd 909 MPT f/w, + * If running against circa 200003dd 909 MPT f/w, * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL * (QUEUE_FULL) returned from device! --> get 0x0000?128 * and with SenseBytes set to 0. @@ -620,7 +1052,9 @@ hd->ScsiLookup[req_idx] = NULL; - sc->host_scribble = NULL; /* CHECKME! - Do we need to clear this??? */ +#ifndef MPT_SCSI_USE_NEW_EH + sc->host_scribble = NULL; +#endif MPT_HOST_LOCK(flags); sc->scsi_done(sc); /* Issue the command callback */ @@ -889,7 +1323,7 @@ int ii; int max = hd->ioc->req_depth; -#ifndef MPT_SCSI_USE_NEW_EH +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) unsigned long flags; #endif @@ -906,7 +1340,7 @@ search_taskQ_for_cmd(SCpnt, hd); #endif - /* Search pendingQ, if found, + /* Search pendingQ, if found, * delete from Q. If found, do not decrement * queue_depth, command never posted. */ @@ -1056,7 +1490,7 @@ * of chain buffers to be allocated. * index = chain_idx * - * Calculate the number of chain buffers needed(plus 1) per I/O + * Calculate the number of chain buffers needed(plus 1) per I/O * then multiply the the maximum number of simultaneous cmds * * num_sge = num sge in request frame + last chain buffer @@ -1170,6 +1604,7 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int BeenHereDoneThat = 0; +static char *info_kbuf = NULL; /* SCSI host fops start here... */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -1258,9 +1693,10 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) tpnt->proc_dir = &proc_mpt_scsihost; #endif + tpnt->proc_info = mptscsih_proc_info; sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST)); if (sh != NULL) { - mptscsih_lock(this, flags); + spin_lock_irqsave(&this->FreeQlock, flags); sh->io_port = 0; sh->n_io_port = 0; sh->irq = 0; @@ -1295,10 +1731,12 @@ #endif sh->this_id = this->pfacts[portnum].PortSCSIID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44) /* OS entry to allow host drivers to force * a queue depth on a per device basis. */ sh->select_queue_depths = mptscsih_select_queue_depths; +#endif /* Required entry. */ sh->unique_id = this->id; @@ -1319,7 +1757,7 @@ } else { numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale + (this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); - } + } if (numSGE < sh->sg_tablesize) { /* Reset this value */ @@ -1333,7 +1771,7 @@ */ scsi_set_pci_device(sh, this->pcidev); - mptscsih_unlock(this, flags); + spin_unlock_irqrestore(&this->FreeQlock, flags); hd = (MPT_SCSI_HOST *) sh->hostdata; hd->ioc = this; @@ -1496,12 +1934,25 @@ done: if (mpt_scsi_hosts > 0) register_reboot_notifier(&mptscsih_notifier); + else { + mpt_reset_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n")); + + mpt_event_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n")); + + mpt_deregister(ScsiScanDvCtx); + mpt_deregister(ScsiTaskCtx); + mpt_deregister(ScsiDoneCtx); + + if (info_kbuf != NULL) + kfree(info_kbuf); + } return mpt_scsi_hosts; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - static char *info_kbuf = NULL; /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mptscsih_release - Unregister SCSI host from linux scsi mid-layer @@ -1662,763 +2113,589 @@ dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n")); mpt_deregister(ScsiScanDvCtx); - mpt_deregister(ScsiTaskCtx); - mpt_deregister(ScsiDoneCtx); - - if (info_kbuf != NULL) - kfree(info_kbuf); - } - } - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * mptscsih_halt - Process the reboot notification - * @nb: Pointer to a struct notifier_block (ignored) - * @event: event (SYS_HALT, SYS_RESTART, SYS_POWER_OFF) - * @buf: Pointer to a data buffer (ignored) - * - * This routine called if a system shutdown or reboot is to occur. - * - * Return NOTIFY_DONE if this is something other than a reboot message. - * NOTIFY_OK if this is a reboot message. - */ -static int -mptscsih_halt(struct notifier_block *nb, ulong event, void *buf) -{ - MPT_ADAPTER *ioc = NULL; - MPT_SCSI_HOST *hd = NULL; - - /* Ignore all messages other than reboot message - */ - if ((event != SYS_RESTART) && (event != SYS_HALT) - && (event != SYS_POWER_OFF)) - return (NOTIFY_DONE); - - for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { - /* Flush the cache of this adapter - */ - if (ioc->sh) { - hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - if (hd) { - mptscsih_synchronize_cache(hd, 0); - } - } - } - - unregister_reboot_notifier(&mptscsih_notifier); - return NOTIFY_OK; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * mptscsih_info - Return information about MPT adapter - * @SChost: Pointer to Scsi_Host structure - * - * (linux Scsi_Host_Template.info routine) - * - * Returns pointer to buffer where information was written. - */ -const char * -mptscsih_info(struct Scsi_Host *SChost) -{ - MPT_SCSI_HOST *h; - int size = 0; - - if (info_kbuf == NULL) - if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL) - return info_kbuf; - - h = (MPT_SCSI_HOST *)SChost->hostdata; - info_kbuf[0] = '\0'; - mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0); - info_kbuf[size-1] = '\0'; - - return info_kbuf; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - static int max_qd = 1; -#if 0 -static int index_log[128]; -static int index_ent = 0; -static __inline__ void ADD_INDEX_LOG(int req_ent) -{ - int i = index_ent++; - - index_log[i & (128 - 1)] = req_ent; -} -#else -#define ADD_INDEX_LOG(req_ent) do { } while(0) -#endif - -#ifdef DROP_TEST -#define DROP_IOC 1 /* IOC to force failures */ -#define DROP_TARGET 3 /* Target ID to force failures */ -#define DROP_THIS_CMD 10000 /* iteration to drop command */ -static int dropCounter = 0; -static int dropTestOK = 0; /* num did good */ -static int dropTestBad = 0; /* num did bad */ -static int dropTestNum = 0; /* total = good + bad + incomplete */ -static int numTotCmds = 0; -static MPT_FRAME_HDR *dropMfPtr = NULL; -static int numTMrequested = 0; -#endif - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W. - * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx) - * @id: IOC id number - * @mf: Pointer to message frame - * - * Handles the call to mptbase for posting request and queue depth - * tracking. - * - * Returns none. - */ -static void -mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf) -{ - /* Main banana... */ - atomic_inc(&queue_depth); - if (atomic_read(&queue_depth) > max_qd) { - max_qd = atomic_read(&queue_depth); - dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd)); - } - - mpt_put_msg_frame(context, id, mf); - - return; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. - * @SCpnt: Pointer to Scsi_Cmnd structure - * @done: Pointer SCSI mid-layer IO completion function - * - * (linux Scsi_Host_Template.queuecommand routine) - * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest - * from a linux Scsi_Cmnd request and send it to the IOC. - * - * Returns 0. (rtn value discarded by linux scsi mid-layer) - */ -int -mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) -{ - MPT_SCSI_HOST *hd; - MPT_FRAME_HDR *mf; - SCSIIORequest_t *pScsiReq; - VirtDevice *pTarget; - MPT_DONE_Q *buffer = NULL; - unsigned long flags; - int target; - int lun; - int datadir; - u32 datalen; - u32 scsictl; - u32 scsidir; - u32 qtag; - u32 cmd_len; - int my_idx; - int ii; - int rc; - int did_errcode; - int issueCmd; - - did_errcode = 0; - hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; - target = SCpnt->target; - lun = SCpnt->lun; - SCpnt->scsi_done = done; - - pTarget = hd->Targets[target]; - - dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n", - (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); - -#ifdef MPT_SAVE_AUTOSENSE - /* 20000617 -sralston - * GRRRRR... Shouldn't have to do this but... - * Do explicit check for REQUEST_SENSE and cached SenseData. - * If yes, return cached SenseData. - */ - if (SCpnt->cmnd[0] == REQUEST_SENSE) { - u8 *dest = NULL; - int sz; - - if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) { - pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; //sjr-moved-here - if (!SCpnt->use_sg) { - dest = SCpnt->request_buffer; - } else { - struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; - if (sg) - dest = (u8 *)(ulong)sg_dma_address(sg); - } - - if (dest) { - sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen); - memcpy(dest, pTarget->sense, sz); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) - SCpnt->resid = SCpnt->request_bufflen - sz; -#endif - SCpnt->result = 0; - SCpnt->scsi_done(SCpnt); - - //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; - - return 0; - } - } - } -#endif - - if (hd->resetPending) { - /* Prevent new commands from being issued - * while reloading the FW. - */ - did_errcode = 1; - goto did_error; - } - - /* - * Put together a MPT SCSI request... - */ - if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) { - dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n", - hd->ioc->name)); - did_errcode = 2; - goto did_error; - } - - pScsiReq = (SCSIIORequest_t *) mf; - - my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); - - ADD_INDEX_LOG(my_idx); - - /* - * The scsi layer should be handling this stuff - * (In 2.3.x it does -DaveM) - */ - - /* BUG FIX! 19991030 -sralston - * TUR's being issued with scsictl=0x02000000 (DATA_IN)! - * Seems we may receive a buffer (datalen>0) even when there - * will be no data transfer! GRRRRR... - */ - datadir = mptscsih_io_direction(SCpnt); - if (datadir == SCSI_DATA_READ) { - datalen = SCpnt->request_bufflen; - scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */ - } else if (datadir == SCSI_DATA_WRITE) { - datalen = SCpnt->request_bufflen; - scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */ - } else { - datalen = 0; - scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; - } + mpt_deregister(ScsiTaskCtx); + mpt_deregister(ScsiDoneCtx); - /* Default to untagged. Once a target structure has been allocated, - * use the Inquiry data to determine if device supports tagged. - */ - qtag = MPI_SCSIIO_CONTROL_UNTAGGED; - if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES) - && (SCpnt->device->tagged_supported)) { - /* - * Some drives are too stupid to handle fairness issues - * with tagged queueing. We throw in the odd ordered - * tag to stop them starving themselves. - */ - if ((jiffies - hd->qtag_tick) > (5*HZ)) { - qtag = MPI_SCSIIO_CONTROL_ORDEREDQ; - hd->qtag_tick = jiffies; + if (info_kbuf != NULL) + kfree(info_kbuf); } - else - qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; } - scsictl = scsidir | qtag; - /* Use the above information to set up the message frame - */ - pScsiReq->TargetID = target; - pScsiReq->Bus = hd->port; - pScsiReq->ChainOffset = 0; - pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; - pScsiReq->CDBLength = SCpnt->cmd_len; - pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; - pScsiReq->Reserved = 0; - pScsiReq->MsgFlags = mpt_msg_flags(); - pScsiReq->LUN[0] = 0; - pScsiReq->LUN[1] = lun; - pScsiReq->LUN[2] = 0; - pScsiReq->LUN[3] = 0; - pScsiReq->LUN[4] = 0; - pScsiReq->LUN[5] = 0; - pScsiReq->LUN[6] = 0; - pScsiReq->LUN[7] = 0; - pScsiReq->Control = cpu_to_le32(scsictl); + return 0; +} - /* - * Write SCSI CDB into the message +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_halt - Process the reboot notification + * @nb: Pointer to a struct notifier_block (ignored) + * @event: event (SYS_HALT, SYS_RESTART, SYS_POWER_OFF) + * @buf: Pointer to a data buffer (ignored) + * + * This routine called if a system shutdown or reboot is to occur. + * + * Return NOTIFY_DONE if this is something other than a reboot message. + * NOTIFY_OK if this is a reboot message. + */ +static int +mptscsih_halt(struct notifier_block *nb, ulong event, void *buf) +{ + MPT_ADAPTER *ioc = NULL; + MPT_SCSI_HOST *hd = NULL; + + /* Ignore all messages other than reboot message */ - cmd_len = SCpnt->cmd_len; - for (ii=0; ii < cmd_len; ii++) - pScsiReq->CDB[ii] = SCpnt->cmnd[ii]; - for (ii=cmd_len; ii < 16; ii++) - pScsiReq->CDB[ii] = 0; + if ((event != SYS_RESTART) && (event != SYS_HALT) + && (event != SYS_POWER_OFF)) + return (NOTIFY_DONE); - /* DataLength */ - pScsiReq->DataLength = cpu_to_le32(datalen); + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + /* Flush the cache of this adapter + */ + if (ioc->sh) { + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd) { + mptscsih_synchronize_cache(hd, 0); + } + } + } - /* SenseBuffer low address */ - pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma - + (my_idx * MPT_SENSE_BUFFER_ALLOC)); + unregister_reboot_notifier(&mptscsih_notifier); + return NOTIFY_OK; +} - /* Now add the SG list - * Always have a SGE even if null length. - */ - rc = SUCCESS; - if (datalen == 0) { - /* Add a NULL SGE */ - mpt_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0, - (dma_addr_t) -1); - } else { - /* Add a 32 or 64 bit SGE */ - rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx); +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_info - Return information about MPT adapter + * @SChost: Pointer to Scsi_Host structure + * + * (linux Scsi_Host_Template.info routine) + * + * Returns pointer to buffer where information was written. + */ +const char * +mptscsih_info(struct Scsi_Host *SChost) +{ + MPT_SCSI_HOST *h = NULL; + int size = 0; + + if (info_kbuf == NULL) + if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL) + return info_kbuf; + + h = (MPT_SCSI_HOST *)SChost->hostdata; + info_kbuf[0] = '\0'; + if (h) { + mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0); + info_kbuf[size-1] = '\0'; } + return info_kbuf; +} - if (rc == SUCCESS) { - hd->ScsiLookup[my_idx] = SCpnt; - SCpnt->host_scribble = NULL; +struct info_str { + char *buffer; + int length; + int offset; + int pos; +}; -#ifdef DROP_TEST - numTotCmds++; - /* If the IOC number and target match, increment - * counter. If counter matches DROP_THIS, do not - * issue command to FW to force a reset. - * Save the MF pointer so we can free resources - * when task mgmt completes. - */ - if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) { - dropCounter++; +static void copy_mem_info(struct info_str *info, char *data, int len) +{ + if (info->pos + len > info->length) + len = info->length - info->pos; - if (dropCounter == DROP_THIS_CMD) { - dropCounter = 0; + if (info->pos + len < info->offset) { + info->pos += len; + return; + } - /* If global is set, then we are already - * doing something - so keep issuing commands. - */ - if (dropMfPtr == NULL) { - dropTestNum++; - dropMfPtr = mf; - atomic_inc(&queue_depth); - printk(MYIOC_s_INFO_FMT - "Dropped SCSI cmd (%p)\n", - hd->ioc->name, SCpnt); - printk("mf (%p) req (%4x) tot cmds (%d)\n", - mf, my_idx, numTotCmds); + if (info->pos < info->offset) { + data += (info->offset - info->pos); + len -= (info->offset - info->pos); + } - return 0; - } - } - } -#endif + if (len > 0) { + memcpy(info->buffer + info->pos, data, len); + info->pos += len; + } +} - /* SCSI specific processing */ - issueCmd = 1; - if (hd->is_spi) { - int dvStatus = hd->ioc->spi_data.dvStatus[target]; +static int copy_info(struct info_str *info, char *fmt, ...) +{ + va_list args; + char buf[81]; + int len; - if (dvStatus || hd->ioc->spi_data.forceDv) { + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); - /* Write SDP1 on this I/O to this target */ - if (dvStatus & MPT_SCSICFG_NEGOTIATE) { - mptscsih_writeSDP1(hd, 0, target, hd->negoNvram); - dvStatus &= ~MPT_SCSICFG_NEGOTIATE; - hd->ioc->spi_data.dvStatus[target] = dvStatus; - } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) { - mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO); - dvStatus &= ~MPT_SCSICFG_BLK_NEGO; - hd->ioc->spi_data.dvStatus[target] = dvStatus; - } + copy_mem_info(info, buf, len); + return len; +} -#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION - if ((dvStatus & MPT_SCSICFG_NEED_DV) || hd->ioc->spi_data.forceDv) { - unsigned long lflags; - /* Schedule DV if necessary */ - spin_lock_irqsave(&dvtaskQ_lock, lflags); - if (!dvtaskQ_active) { - dvtaskQ_active = 1; - spin_unlock_irqrestore(&dvtaskQ_lock, lflags); - MPT_INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd); +static int mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len) +{ + struct info_str info; - SCHEDULE_TASK(&mptscsih_dvTask); - } else { - spin_unlock_irqrestore(&dvtaskQ_lock, lflags); - } - hd->ioc->spi_data.forceDv = 0; - } + info.buffer = pbuf; + info.length = len; + info.offset = offset; + info.pos = 0; - /* Trying to do DV to this target, extend timeout. - * Wait to issue intil flag is clear - */ - if (dvStatus & MPT_SCSICFG_DV_PENDING) { - mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); - issueCmd = 0; - } + copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name); + copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word); + copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts); + copy_info(&info, "MaxQ=%d\n", ioc->req_depth); - /* Set the DV flags. - */ - if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) - mptscsih_set_dvflags(hd, pScsiReq); -#endif - } - } + return ((info.pos > info.offset) ? info.pos - info.offset : 0); +} - if (issueCmd) { - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); - dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", - hd->ioc->name, SCpnt, mf, my_idx)); - } else { - ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n", - hd->ioc->name, SCpnt, my_idx)); - /* Place this command on the pendingQ if possible */ - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->freeQ)) { - buffer = hd->freeQ.head; - Q_DEL_ITEM(buffer); +static int mptscsih_user_command(MPT_ADAPTER *ioc, char *pbuf, int len) +{ + /* Not yet implemented */ + return len; +} - /* Save the mf pointer - */ - buffer->argp = (void *)mf; +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_proc_info - Return information about MPT adapter + * + * (linux Scsi_Host_Template.info routine) + * + * buffer: if write, user data; if read, buffer for user + * length: if write, return length; + * offset: if write, 0; if read, the current offset into the buffer from + * the previous read. + * hostno: scsi host number + * func: if write = 1; if read = 0 + */ +int mptscsih_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int func) +{ + MPT_ADAPTER *ioc = NULL; + MPT_SCSI_HOST *hd = NULL; + int size = 0; - /* Add to the pendingQ - */ - Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - } else { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - SCpnt->result = (DID_BUS_BUSY << 16); - SCpnt->scsi_done(SCpnt); - } + dprintk(("Called mptscsih_proc_info: hostno=%d, func=%d\n", hostno, func)); + dprintk(("buffer %p, start=%p (%p) offset=%ld length = %d\n", + buffer, start, *start, offset, length)); + + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + if ((ioc->sh) && (ioc->sh->host_no == hostno)) { + hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + break; } + } + if ((ioc == NULL) || (ioc->sh == NULL) || (hd == NULL)) + return 0; + + if (func) { + size = mptscsih_user_command(ioc, buffer, length); } else { - mptscsih_freeChainBuffers(hd, my_idx); - mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); - did_errcode = 3; - goto did_error; + if (start) + *start = buffer; + + size = mptscsih_host_info(ioc, buffer, offset, length); } - return 0; + return size; +} -did_error: - dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n", - hd->ioc->name, did_errcode, SCpnt)); - /* Just wish OS to issue a retry */ - SCpnt->result = (DID_BUS_BUSY << 16); - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->freeQ)) { - buffer = hd->freeQ.head; - Q_DEL_ITEM(buffer); - /* Set the Scsi_Cmnd pointer - */ - buffer->argp = (void *)SCpnt; +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int max_qd = 1; +#if 0 +static int index_log[128]; +static int index_ent = 0; +static __inline__ void ADD_INDEX_LOG(int req_ent) +{ + int i = index_ent++; + + index_log[i & (128 - 1)] = req_ent; +} +#else +#define ADD_INDEX_LOG(req_ent) do { } while(0) +#endif - /* Add to the doneQ - */ - Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - } else { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - SCpnt->scsi_done(SCpnt); +#ifdef DROP_TEST +#define DROP_IOC 1 /* IOC to force failures */ +#define DROP_TARGET 3 /* Target ID to force failures */ +#define DROP_THIS_CMD 10000 /* iteration to drop command */ +static int dropCounter = 0; +static int dropTestOK = 0; /* num did good */ +static int dropTestBad = 0; /* num did bad */ +static int dropTestNum = 0; /* total = good + bad + incomplete */ +static int numTotCmds = 0; +static MPT_FRAME_HDR *dropMfPtr = NULL; +static int numTMrequested = 0; +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W. + * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx) + * @id: IOC id number + * @mf: Pointer to message frame + * + * Handles the call to mptbase for posting request and queue depth + * tracking. + * + * Returns none. + */ +static inline void +mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf) +{ + /* Main banana... */ + atomic_inc(&queue_depth); + if (atomic_read(&queue_depth) > max_qd) { + max_qd = atomic_read(&queue_depth); + dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd)); } - return 0; + mpt_put_msg_frame(context, id, mf); + + return; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the - * SCSIIORequest_t Message Frame. - * @hd: Pointer to MPT_SCSI_HOST structure +/** + * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. * @SCpnt: Pointer to Scsi_Cmnd structure - * @pReq: Pointer to SCSIIORequest_t structure + * @done: Pointer SCSI mid-layer IO completion function * - * Returns ... + * (linux Scsi_Host_Template.queuecommand routine) + * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest + * from a linux Scsi_Cmnd request and send it to the IOC. + * + * Returns 0. (rtn value discarded by linux scsi mid-layer) */ -static int -mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, - SCSIIORequest_t *pReq, int req_idx) +int +mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { - char *psge; - char *chainSge; - struct scatterlist *sg; - int frm_sz; - int sges_left, sg_done; - int chain_idx = MPT_HOST_NO_CHAIN; - int sgeOffset; - int numSgeSlots, numSgeThisFrame; - u32 sgflags, sgdir, thisxfer = 0; - int chain_dma_off = 0; - int newIndex; + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + SCSIIORequest_t *pScsiReq; + VirtDevice *pTarget; + MPT_DONE_Q *buffer = NULL; + unsigned long flags; + int target; + int lun; + int datadir; + u32 datalen; + u32 scsictl; + u32 scsidir; + u32 cmd_len; + int my_idx; int ii; - dma_addr_t v2; + int rc; + int did_errcode; + int issueCmd; - sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; - if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { - sgdir = MPT_TRANSFER_HOST_TO_IOC; - } else { - sgdir = MPT_TRANSFER_IOC_TO_HOST; - } + did_errcode = 0; + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + target = SCpnt->target; + lun = SCpnt->lun; + SCpnt->scsi_done = done; - psge = (char *) &pReq->SGL; - frm_sz = hd->ioc->req_sz; + pTarget = hd->Targets[target]; - /* Map the data portion, if any. - * sges_left = 0 if no data transfer. + dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n", + (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); + +#ifdef MPT_SAVE_AUTOSENSE + /* 20000617 -sralston + * GRRRRR... Shouldn't have to do this but... + * Do explicit check for REQUEST_SENSE and cached SenseData. + * If yes, return cached SenseData. */ - sges_left = SCpnt->use_sg; - if (SCpnt->use_sg) { - sges_left = pci_map_sg(hd->ioc->pcidev, - (struct scatterlist *) SCpnt->request_buffer, - SCpnt->use_sg, - scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); - } else if (SCpnt->request_bufflen) { - dma_addr_t buf_dma_addr; - scPrivate *my_priv; + if (SCpnt->cmnd[0] == REQUEST_SENSE) { + u8 *dest = NULL; + int sz; - buf_dma_addr = pci_map_single(hd->ioc->pcidev, - SCpnt->request_buffer, - SCpnt->request_bufflen, - scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) { + pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; //sjr-moved-here + if (!SCpnt->use_sg) { + dest = SCpnt->request_buffer; + } else { + struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; + if (sg) + dest = (u8 *)(ulong)sg_dma_address(sg); + } - /* We hide it here for later unmap. */ - my_priv = (scPrivate *) &SCpnt->SCp; - my_priv->p1 = (void *)(ulong) buf_dma_addr; + if (dest) { + sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen); + memcpy(dest, pTarget->sense, sz); - dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", - hd->ioc->name, SCpnt, SCpnt->request_bufflen)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + SCpnt->resid = SCpnt->request_bufflen - sz; +#endif + SCpnt->result = 0; + SCpnt->scsi_done(SCpnt); - mpt_add_sge((char *) &pReq->SGL, - 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, - buf_dma_addr); + //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; - return SUCCESS; + return 0; + } + } } +#endif - /* Handle the SG case. - */ - sg = (struct scatterlist *) SCpnt->request_buffer; - sg_done = 0; - sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); - chainSge = NULL; + if (hd->resetPending) { + /* Prevent new commands from being issued + * while reloading the FW. + */ + did_errcode = 1; + goto did_error; + } - /* Prior to entering this loop - the following must be set - * current MF: sgeOffset (bytes) - * chainSge (Null if original MF is not a chain buffer) - * sg_done (num SGE done for this MF) + /* + * Put together a MPT SCSI request... */ + if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) { + dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n", + hd->ioc->name)); + did_errcode = 2; + goto did_error; + } -nextSGEset: - numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) ); - numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; + pScsiReq = (SCSIIORequest_t *) mf; - sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir; + my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); - /* Get first (num - 1) SG elements - * Skip any SG entries with a length of 0 - * NOTE: at finish, sg and psge pointed to NEXT data/location positions + ADD_INDEX_LOG(my_idx); + + /* + * The scsi layer should be handling this stuff + * (In 2.3.x it does -DaveM) */ - for (ii=0; ii < (numSgeThisFrame-1); ii++) { - thisxfer = sg_dma_len(sg); - if (thisxfer == 0) { - sg ++; /* Get next SG element from the OS */ - sg_done++; - continue; - } - v2 = sg_dma_address(sg); - mpt_add_sge(psge, sgflags | thisxfer, v2); + /* BUG FIX! 19991030 -sralston + * TUR's being issued with scsictl=0x02000000 (DATA_IN)! + * Seems we may receive a buffer (datalen>0) even when there + * will be no data transfer! GRRRRR... + */ + datadir = mptscsih_io_direction(SCpnt); + if (datadir == SCSI_DATA_READ) { + datalen = SCpnt->request_bufflen; + scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */ + } else if (datadir == SCSI_DATA_WRITE) { + datalen = SCpnt->request_bufflen; + scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */ + } else { + datalen = 0; + scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; + } - sg++; /* Get next SG element from the OS */ - psge += (sizeof(u32) + sizeof(dma_addr_t)); - sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); - sg_done++; + /* Default to untagged. Once a target structure has been allocated, + * use the Inquiry data to determine if device supports tagged. + */ + if ( pTarget + && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES) + && (SCpnt->device->tagged_supported)) { + scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ; + } else { + scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED; } - if (numSgeThisFrame == sges_left) { - /* Add last element, end of buffer and end of list flags. - */ - sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT | - MPT_SGE_FLAGS_END_OF_BUFFER | - MPT_SGE_FLAGS_END_OF_LIST; + /* Use the above information to set up the message frame + */ + pScsiReq->TargetID = target; + pScsiReq->Bus = hd->port; + pScsiReq->ChainOffset = 0; + pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; + pScsiReq->CDBLength = SCpnt->cmd_len; + pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; + pScsiReq->Reserved = 0; + pScsiReq->MsgFlags = mpt_msg_flags(); + pScsiReq->LUN[0] = 0; + pScsiReq->LUN[1] = lun; + pScsiReq->LUN[2] = 0; + pScsiReq->LUN[3] = 0; + pScsiReq->LUN[4] = 0; + pScsiReq->LUN[5] = 0; + pScsiReq->LUN[6] = 0; + pScsiReq->LUN[7] = 0; + pScsiReq->Control = cpu_to_le32(scsictl); - /* Add last SGE and set termination flags. - * Note: Last SGE may have a length of 0 - which should be ok. - */ - thisxfer = sg_dma_len(sg); + /* + * Write SCSI CDB into the message + * Should write from cmd_len up to 16, but skip for performance reasons. + */ + cmd_len = SCpnt->cmd_len; + for (ii=0; ii < cmd_len; ii++) + pScsiReq->CDB[ii] = SCpnt->cmnd[ii]; - v2 = sg_dma_address(sg); - mpt_add_sge(psge, sgflags | thisxfer, v2); - /* - sg++; - psge += (sizeof(u32) + sizeof(dma_addr_t)); - */ - sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); - sg_done++; + /* DataLength */ + pScsiReq->DataLength = cpu_to_le32(datalen); - if (chainSge) { - /* The current buffer is a chain buffer, - * but there is not another one. - * Update the chain element - * Offset and Length fields. - */ - mpt_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off); - } else { - /* The current buffer is the original MF - * and there is no Chain buffer. - */ - pReq->ChainOffset = 0; - } - } else { - /* At least one chain buffer is needed. - * Complete the first MF - * - last SGE element, set the LastElement bit - * - set ChainOffset (words) for orig MF - * (OR finish previous MF chain buffer) - * - update MFStructPtr ChainIndex - * - Populate chain element - * Also - * Loop until done. - */ + /* SenseBuffer low address */ + pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma + + (my_idx * MPT_SENSE_BUFFER_ALLOC)); - dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", - hd->ioc->name, sg_done)); + /* Now add the SG list + * Always have a SGE even if null length. + */ + rc = SUCCESS; + if (datalen == 0) { + /* Add a NULL SGE */ + mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0, + (dma_addr_t) -1); + } else { + /* Add a 32 or 64 bit SGE */ + rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx); + } - /* Set LAST_ELEMENT flag for last non-chain element - * in the buffer. Since psge points at the NEXT - * SGE element, go back one SGE element, update the flags - * and reset the pointer. (Note: sgflags & thisxfer are already - * set properly). - */ - if (sg_done) { - u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); - sgflags = le32_to_cpu(*ptmp); - sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; - *ptmp = cpu_to_le32(sgflags); - } - if (chainSge) { - /* The current buffer is a chain buffer. - * chainSge points to the previous Chain Element. - * Update its chain element Offset and Length (must - * include chain element size) fields. - * Old chain element is now complete. - */ - u8 nextChain = (u8) (sgeOffset >> 2); - sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); - mpt_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); - } else { - /* The original MF buffer requires a chain buffer - - * set the offset. - * Last element in this MF is a chain element. - */ - pReq->ChainOffset = (u8) (sgeOffset >> 2); - } + if (rc == SUCCESS) { + hd->ScsiLookup[my_idx] = SCpnt; + SCpnt->host_scribble = NULL; - sges_left -= sg_done; +#ifdef DROP_TEST + numTotCmds++; + /* If the IOC number and target match, increment + * counter. If counter matches DROP_THIS, do not + * issue command to FW to force a reset. + * Save the MF pointer so we can free resources + * when task mgmt completes. + */ + if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) { + dropCounter++; + if (dropCounter == DROP_THIS_CMD) { + dropCounter = 0; - /* NOTE: psge points to the beginning of the chain element - * in current buffer. Get a chain buffer. - */ - if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED) - return FAILED; + /* If global is set, then we are already + * doing something - so keep issuing commands. + */ + if (dropMfPtr == NULL) { + dropTestNum++; + dropMfPtr = mf; + atomic_inc(&queue_depth); + printk(MYIOC_s_INFO_FMT + "Dropped SCSI cmd (%p)\n", + hd->ioc->name, SCpnt); + printk("mf (%p) req (%4x) tot cmds (%d)\n", + mf, my_idx, numTotCmds); - /* Update the tracking arrays. - * If chainSge == NULL, update ReqToChain, else ChainToChain - */ - if (chainSge) { - hd->ChainToChain[chain_idx] = newIndex; - } else { - hd->ReqToChain[req_idx] = newIndex; + return 0; + } + } } - chain_idx = newIndex; - chain_dma_off = hd->ioc->req_sz * chain_idx; +#endif - /* Populate the chainSGE for the current buffer. - * - Set chain buffer pointer to psge and fill - * out the Address and Flags fields. - */ - chainSge = (char *) psge; - dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)", - psge, req_idx)); + /* SCSI specific processing */ + issueCmd = 1; + if (hd->is_spi) { + int dvStatus = hd->ioc->spi_data.dvStatus[target]; - /* Start the SGE for the next buffer - */ - psge = (char *) (hd->ChainBuffer + chain_dma_off); - sgeOffset = 0; - sg_done = 0; + if (dvStatus || hd->ioc->spi_data.forceDv) { - dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n", - psge, chain_idx)); + /* Write SDP1 on this I/O to this target */ + if (dvStatus & MPT_SCSICFG_NEGOTIATE) { + mptscsih_writeSDP1(hd, 0, target, hd->negoNvram); + dvStatus &= ~MPT_SCSICFG_NEGOTIATE; + hd->ioc->spi_data.dvStatus[target] = dvStatus; + } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) { + mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO); + dvStatus &= ~MPT_SCSICFG_BLK_NEGO; + hd->ioc->spi_data.dvStatus[target] = dvStatus; + } - /* Start the SGE for the next buffer - */ +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + if ((dvStatus & MPT_SCSICFG_NEED_DV) || + (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { + unsigned long lflags; + /* Schedule DV if necessary */ + spin_lock_irqsave(&dvtaskQ_lock, lflags); + if (!dvtaskQ_active) { + dvtaskQ_active = 1; + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); + MPT_INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd); - goto nextSGEset; - } + SCHEDULE_TASK(&mptscsih_dvTask); + } else { + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); + } + hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV; + } - return SUCCESS; -} + /* Trying to do DV to this target, extend timeout. + * Wait to issue intil flag is clear + */ + if (dvStatus & MPT_SCSICFG_DV_PENDING) { + mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); + issueCmd = 0; + } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * mptscsih_getFreeChainBuffes - Function to get a free chain - * from the MPT_SCSI_HOST FreeChainQ. - * @hd: Pointer to the MPT_SCSI_HOST instance - * @req_idx: Index of the SCSI IO request frame. (output) - * - * return SUCCESS or FAILED - */ -static int -mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex) -{ - MPT_FRAME_HDR *chainBuf = NULL; - unsigned long flags; - int rc = FAILED; - int chain_idx = MPT_HOST_NO_CHAIN; + /* Set the DV flags. + */ + if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) + mptscsih_set_dvflags(hd, pScsiReq); +#endif + } + } - //spin_lock_irqsave(&hd->FreeChainQlock, flags); - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - if (!Q_IS_EMPTY(&hd->FreeChainQ)) { + if (issueCmd) { + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", + hd->ioc->name, SCpnt, mf, my_idx)); + } else { + ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n", + hd->ioc->name, SCpnt, my_idx)); + /* Place this command on the pendingQ if possible */ + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->freeQ)) { + buffer = hd->freeQ.head; + Q_DEL_ITEM(buffer); - int offset; + /* Save the mf pointer + */ + buffer->argp = (void *)mf; - chainBuf = hd->FreeChainQ.head; - Q_DEL_ITEM(&chainBuf->u.frame.linkage); - offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer; - chain_idx = offset / hd->ioc->req_sz; - rc = SUCCESS; + /* Add to the pendingQ + */ + Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + } else { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + SCpnt->result = (DID_BUS_BUSY << 16); + SCpnt->scsi_done(SCpnt); + } + } + } else { + mptscsih_freeChainBuffers(hd, my_idx); + mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); + did_errcode = 3; + goto did_error; } - //spin_unlock_irqrestore(&hd->FreeChainQlock, flags); - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + return 0; + +did_error: + dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n", + hd->ioc->name, did_errcode, SCpnt)); + /* Just wish OS to issue a retry */ + SCpnt->result = (DID_BUS_BUSY << 16); + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->freeQ)) { + buffer = hd->freeQ.head; + Q_DEL_ITEM(buffer); - *retIndex = chain_idx; + /* Set the Scsi_Cmnd pointer + */ + buffer->argp = (void *)SCpnt; - dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", - hd->ioc->name, *retIndex, chainBuf)); + /* Add to the doneQ + */ + Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + } else { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + SCpnt->scsi_done(SCpnt); + } - return rc; + return 0; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2540,8 +2817,8 @@ #ifdef MPT_DEBUG_RESET if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) { - printk(MYIOC_s_WARN_FMT - "TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n", + printk(MYIOC_s_WARN_FMT + "TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n", hd->ioc->name, ioc_raw_state); } #endif @@ -2555,12 +2832,9 @@ hd->hard_resets++; rc = mptscsih_IssueTaskMgmt(hd, type, target, lun, ctx2abort, sleepFlag); if (rc) { -#ifdef MPT_SCSI_USE_NEW_EH - hd->tmState = TM_STATE_ERROR; -#endif printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name); } else { - printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name); + dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name)); } } #ifdef DROP_TEST @@ -2688,7 +2962,6 @@ { MPT_SCSI_HOST *hd; MPT_FRAME_HDR *mf; - unsigned long flags; u32 ctx2abort; int scpnt_idx; @@ -2703,10 +2976,8 @@ return FAILED; } - printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n", - hd->ioc->name, SCpnt); - printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n", - hd->ioc->name, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); if (hd->timeouts < -1) hd->timeouts++; @@ -2763,11 +3034,9 @@ ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext; hd->abortSCpnt = SCpnt; - if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, - SCpnt->target, SCpnt->lun, ctx2abort, CAN_SLEEP) - < 0 - || hd->tmState == TM_STATE_ERROR) { + SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP) + < 0) { /* The TM request failed and the subsequent FW-reload failed! * Fatal error case. @@ -2775,14 +3044,6 @@ printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n", hd->ioc->name, SCpnt); - /* If command not found, do not do callback, - * just return failed. CHECKME - */ - if (hd->ScsiLookup[scpnt_idx] != NULL) { - SCpnt->result = STS_BUSY; - SCpnt->scsi_done(SCpnt); - } - /* We must clear our pending flag before clearing our state. */ hd->tmPending = 0; @@ -2790,34 +3051,8 @@ return FAILED; } + return FAILED; - /* Our task management request will either complete or time out. So we - * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR, - * we encountered an error executing the task management request. - */ - while (hd->tmPending == 1){ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - } - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - if (hd->tmState == TM_STATE_ERROR){ - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " - "TM timeout error! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - return FAILED; - } - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " - "Abort was successful! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - - return SUCCESS; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2833,7 +3068,6 @@ mptscsih_dev_reset(Scsi_Cmnd * SCpnt) { MPT_SCSI_HOST *hd; - unsigned long flags; /* If we can't locate our host adapter structure, return FAILED status. */ @@ -2844,10 +3078,13 @@ return FAILED; } - printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n", - hd->ioc->name, SCpnt); - printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n", - hd->ioc->name, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + + /* Unsupported for SCSI. Suppored for FCP + */ + if (hd->is_spi) + return FAILED; /* Wait a fixed amount of time for the TM pending flag to be cleared. * If we time out, then we return a FAILED status to the caller. This @@ -2863,7 +3100,7 @@ } if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, - SCpnt->target, 0, 0, CAN_SLEEP) + SCpnt->target, 0, 0, NO_SLEEP) < 0){ /* The TM request failed and the subsequent FW-reload failed! * Fatal error case. @@ -2874,34 +3111,8 @@ hd->tmState = TM_STATE_NONE; return FAILED; } - - /* Our task management request will either complete or time out. So we - * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR, - * we encountered an error executing the task management request. - */ - while (hd->tmPending == 1){ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - } - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - if (hd->tmState == TM_STATE_ERROR){ - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: " - "TM timeout error! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - return FAILED; - } - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: " - "Device reset was successful! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - return SUCCESS; + } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2917,7 +3128,6 @@ mptscsih_bus_reset(Scsi_Cmnd * SCpnt) { MPT_SCSI_HOST *hd; - unsigned long flags; /* If we can't locate our host adapter structure, return FAILED status. */ @@ -2928,10 +3138,8 @@ return FAILED; } - printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n", - hd->ioc->name, SCpnt); - printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n", - hd->ioc->name, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); if (hd->timeouts < -1) hd->timeouts++; @@ -2945,19 +3153,19 @@ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: " "Timed out waiting for previous TM to complete! " "(sc = %p)\n", - hd->ioc->name, SCpnt ) ); + hd->ioc->name, SCpnt)); return FAILED; } /* We are now ready to execute the task management request. */ if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, - 0, 0, 0, CAN_SLEEP) + 0, 0, 0, NO_SLEEP) < 0){ /* The TM request failed and the subsequent FW-reload failed! * Fatal error case. */ - printk(MYIOC_s_WARN_FMT + printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n", hd->ioc->name, SCpnt); hd->tmPending = 0; @@ -2965,32 +3173,6 @@ return FAILED; } - /* Our task management request will either complete or time out. So we - * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR, - * we encountered an error executing the task management request. - */ - while (hd->tmPending == 1){ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - } - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - if (hd->tmState == TM_STATE_ERROR){ - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: " - "TM timeout error! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - return FAILED; - } - hd->tmState = TM_STATE_NONE; - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - - nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: " - "Bus reset was successful! (sc=%p)\n", - hd->ioc->name, - SCpnt)); - return SUCCESS; } @@ -3026,11 +3208,11 @@ /* If our attempts to reset the host failed, then return a failed * status. The host will be taken off line by the SCSI mid-layer. */ - if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){ + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0){ status = FAILED; } else { - /* Make sure TM pending is cleared and TM state is set to - * NONE. + /* Make sure TM pending is cleared and TM state is set to + * NONE. */ hd->tmPending = 0; hd->tmState = TM_STATE_NONE; @@ -3046,7 +3228,7 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** - * mptscsih_tm_pending_wait - wait for pending task management request to + * mptscsih_tm_pending_wait - wait for pending task management request to * complete. * @hd: Pointer to MPT host structure. * @@ -3069,9 +3251,7 @@ break; } spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - + mdelay(250); } while (--loop_count); return status; @@ -3182,7 +3362,7 @@ SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx); /* For the time being, force bus reset on any abort - * requests for the 1030 FW. + * requests for the 1030/1035 FW. */ if (hd->is_spi) mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; @@ -3204,7 +3384,7 @@ * (bottom/unused portion of) MPT request frame. */ ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo; - MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); + MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); SCHEDULE_TASK(ptaskfoo); } else { @@ -3335,7 +3515,7 @@ * (bottom/unused portion of) MPT request frame. */ ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo; - MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); + MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); SCHEDULE_TASK(ptaskfoo); } else { @@ -3602,9 +3782,6 @@ } } } -#ifdef MPT_SCSI_USE_NEW_EH - hd->tmState = TM_STATE_ERROR; -#endif } else { dtmprintk((KERN_INFO " SCSI TaskMgmt SUCCESS!\n")); @@ -3648,6 +3825,9 @@ spin_lock_irqsave(&ioc->FreeQlock, flags); hd->tmPending = 0; spin_unlock_irqrestore(&ioc->FreeQlock, flags); +#ifdef MPT_SCSI_USE_NEW_EH + hd->tmState = TM_STATE_NONE; +#endif return 1; } @@ -3657,15 +3837,22 @@ * This is anyones guess quite frankly. */ int -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) -mptscsih_bios_param(Disk * disk, struct block_device *dev, int *ip) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev, + sector_t capacity, int *ip) +{ +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) +mptscsih_bios_param(Disk * disk, struct block_device *bdev, int *ip) +{ + sector_t capacity = disk->capacity; #else mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip) -#endif { + unsigned capacity = disk->capacity; +#endif int size; - size = disk->capacity; + size = capacity; ip[0] = 64; /* heads */ ip[1] = 32; /* sectors */ if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ @@ -3682,13 +3869,49 @@ * Called once per device the bus scan. Use it to force the queue_depth * member to 1 if a device does not support Q tags. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) +int +mptscsih_slave_attach(Scsi_Device *device) +{ + struct Scsi_Host *host = device->host; + VirtDevice *pTarget; + MPT_SCSI_HOST *hd; + + hd = (MPT_SCSI_HOST *)host->hostdata; + if (hd && (hd->Targets != NULL)) { + pTarget = hd->Targets[device->id]; + if (pTarget) { + if (!device->tagged_supported || + !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) { + scsi_adjust_queue_depth(device, 0, 1); + + } else if ((pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) + && (pTarget->inq_data[0] & 0x1f) == 0x00 + && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)) { + scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG, + MPT_SCSI_CMD_PER_DEV_HIGH); + } else { + scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG, + MPT_SCSI_CMD_PER_DEV_LOW); + } +#if 0 + /* Original Code for 2.5 */ + } else { + scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG, + device->host->can_queue >> 1); + } +#endif + } + } + return 0; +} +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */ void mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList) { struct scsi_device *device; VirtDevice *pTarget; MPT_SCSI_HOST *hd; - int ii, max; for (device = sdList; device != NULL; device = device->next) { @@ -3700,119 +3923,44 @@ continue; if (hd->Targets != NULL) { - if (hd->is_spi) - max = MPT_MAX_SCSI_DEVICES; - else - max = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; + pTarget = NULL; + if (device->id > sh->max_id) { + /* error case, should never happen */ + device->queue_depth = 1; + continue; + } else { + pTarget = hd->Targets[device->id]; + } - for (ii=0; ii < max; ii++) { - pTarget = hd->Targets[ii]; - if (pTarget && !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) { + if (pTarget == NULL) { + /* error case - don't know about this device */ + device->queue_depth = 1; + } else if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) device->queue_depth = 1; - } + else if (((pTarget->inq_data[0] & 0x1f) == 0x00) + && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)){ + device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + } else + device->queue_depth = MPT_SCSI_CMD_PER_DEV_LOW; + + } else { + /* error case - No Inq. Data */ + device->queue_depth = 1; } + dprintk((MYIOC_s_INFO_FMT + "target = %d, sync factor = %x, queue depth = %d\n", + hd->ioc->name, pTarget->target_id, + pTarget->minSyncFactor, device->queue_depth)); } } } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * Private routines... */ -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* 19991030 -sralston - * Return absolute SCSI data direction: - * 1 = _DATA_OUT - * 0 = _DIR_NONE - * -1 = _DATA_IN - * - * Changed: 3-20-2002 pdelaney to use the default data - * direction and the defines set up in the - * 2.4 kernel series - * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1) - * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3) - * -1 = _DATA_IN changed to SCSI_DATA_READ (2) - * If the direction is unknown, fall through to original code. - * - * Mid-layer bug fix(): sg interface generates the wrong data - * direction in some cases. Set the direction the hard way for - * the most common commands. - */ -static int -mptscsih_io_direction(Scsi_Cmnd *cmd) -{ - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - return SCSI_DATA_WRITE; - break; - case READ_6: - case READ_10: - return SCSI_DATA_READ; - break; - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN) - return cmd->sc_data_direction; -#endif - switch (cmd->cmnd[0]) { - /* _DATA_OUT commands */ - case WRITE_6: case WRITE_10: case WRITE_12: - case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: - case WRITE_VERIFY: case WRITE_VERIFY_12: - case COMPARE: case COPY: case COPY_VERIFY: - case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: - case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: - case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: - case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: - case REASSIGN_BLOCKS: - case PERSISTENT_RESERVE_OUT: - case 0xea: - case 0xa3: - return SCSI_DATA_WRITE; - - /* No data transfer commands */ - case SEEK_6: case SEEK_10: - case RESERVE: case RELEASE: - case TEST_UNIT_READY: - case START_STOP: - case ALLOW_MEDIUM_REMOVAL: - return SCSI_DATA_NONE; - - /* Conditional data transfer commands */ - case FORMAT_UNIT: - if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */ - return SCSI_DATA_WRITE; - else - return SCSI_DATA_NONE; - - case VERIFY: - if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */ - return SCSI_DATA_WRITE; - else - return SCSI_DATA_NONE; - - case RESERVE_10: - if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */ - return SCSI_DATA_WRITE; - else - return SCSI_DATA_NONE; - -#if 0 - case REZERO_UNIT: /* (or REWIND) */ - case SPACE: - case ERASE: case ERASE_10: - case SYNCHRONIZE_CACHE: - case LOCK_UNLOCK_CACHE: -#endif - - /* Must be data _IN! */ - default: - return SCSI_DATA_READ; - } -} /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* Utility function to copy sense data from the scsi_cmnd buffer @@ -3848,7 +3996,7 @@ /* save sense data to the target device */ - if (target) { + if (hd->is_spi && target) { #ifdef MPT_SAVE_AUTOSENSE int sz; @@ -3861,7 +4009,7 @@ #ifdef ABORT_FIX if (sz >= SCSI_STD_SENSE_BYTES) { - if ((sense_data[02] == ABORTED_COMMAND) && + if ((sense_data[02] == ABORTED_COMMAND) && (sense_data[12] == 0x47) && (sense_data[13] == 0x00)){ target->numAborts++; if ((target->raidVolume == 0) && (target->numAborts > 5)) { @@ -3954,7 +4102,7 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* Search the pendingQ for a command with specific index. - * If found, delete and return mf pointer + * If found, delete and return mf pointer * If not found, return NULL */ static MPT_FRAME_HDR * @@ -4122,6 +4270,8 @@ ioc->name)); } else { + ScsiCfgData *pSpi = NULL; + dtmprintk((MYIOC_s_WARN_FMT "Do Post-Diag Reset handling\n", ioc->name)); @@ -4160,6 +4310,9 @@ spin_unlock_irqrestore(&ioc->FreeQlock, flags); hd->resetPending = 0; hd->numTMrequests = 0; +#ifdef MPT_SCSI_USE_NEW_EH + hd->tmState = TM_STATE_NONE; +#endif /* 6. If there was an internal command, * wake this process up. @@ -4181,6 +4334,14 @@ dtmprintk((MYIOC_s_WARN_FMT "Post-Reset handling complete.\n", ioc->name)); + + + /* 8. Set then flag to force DV and re-read IOC Page 3 + */ + pSpi = &ioc->spi_data; + pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; + ddvtprintk(("Set reload IOC Pg3 Flag\n")); + } return 1; /* currently means nothing really */ @@ -4203,10 +4364,11 @@ case MPI_EVENT_IOC_BUS_RESET: /* 04 */ case MPI_EVENT_EXT_BUS_RESET: /* 05 */ hd = NULL; - if (ioc->sh) + if (ioc->sh) { hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - if (hd && (hd->is_spi) && (hd->soft_resets < -1)) - hd->soft_resets++; + if (hd && (hd->is_spi) && (hd->soft_resets < -1)) + hd->soft_resets++; + } break; case MPI_EVENT_LOGOUT: /* 09 */ /* FIXME! */ @@ -4226,7 +4388,7 @@ case MPI_EVENT_INTEGRATED_RAID: /* 0B */ #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION - /* negoNvram set to 0 if DV enabled and to USE_NVRAM if + /* negoNvram set to 0 if DV enabled and to USE_NVRAM if * if DV disabled. Need to check for target mode. */ hd = NULL; @@ -4242,11 +4404,12 @@ reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16; if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) { - /* New or replaced disk. + /* New or replaced disk. * Set DV flag and schedule DV. */ pSpi = &ioc->spi_data; physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24; + ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum)); if (pSpi->pIocPg3) { pPDisk = pSpi->pIocPg3->PhysDisk; numPDisk =pSpi->pIocPg3->NumPhysDisks; @@ -4261,6 +4424,16 @@ pPDisk++; numPDisk--; } + + if (numPDisk == 0) { + /* The physical disk that needs DV was not found + * in the stored IOC Page 3. The driver must reload + * this page. DV routine will set the NEED_DV flag for + * all phys disks that have DV_NOT_DONE set. + */ + pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; + ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum)); + } } } } @@ -4724,7 +4897,7 @@ if (ioop->cdbPtr == NULL) { return 0; } else if ((ioop->cdbPtr[0] == CMD_TestUnitReady) || - (ioop->cdbPtr[0] == CMD_ReadCapacity) || + (ioop->cdbPtr[0] == CMD_ReadCapacity) || (ioop->cdbPtr[0] == 0x43)) { return 0; } @@ -4839,16 +5012,16 @@ } } - if (vdev) { + vdev->raidVolume = 0; + if (vdev && hd->is_spi) { if (hd->ioc->spi_data.isRaid & (1 << target_id)) { vdev->raidVolume = 1; ddvtprintk((KERN_INFO "RAID Volume @ id %d\n", target_id)); - } else - vdev->raidVolume = 0; + } } if (vdev && data) { - if ((!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) || + if ((!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) || ((dlen > 56) && (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56)))) { /* Copy the inquiry data - if we haven't yet. @@ -4868,7 +5041,7 @@ /* If LUN 0, tape and have not done DV, set the DV flag. */ - if ((lun == 0) && ((data[0] & 0x1F) == 0x01)) { + if (hd->is_spi && (lun == 0) && ((data[0] & 0x1F) == 0x01)) { ScsiCfgData *pSpi = &hd->ioc->spi_data; if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; @@ -4907,6 +5080,13 @@ ddvtprintk((KERN_INFO "set Target: (id %d) byte56 0x%x\n", id, byte56)); + if (!hd->is_spi) { + if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if (target->inq_data[7] & 0x02) + target->tflags |= MPT_TARGET_FLAGS_Q_YES; + } + return; + } /* Set flags based on Inquiry data */ if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { @@ -4931,7 +5111,7 @@ factor = MPT_ULTRA320; /* If RAID, never disable QAS - * else if non RAID, do not disable + * else if non RAID, do not disable * QAS if bit 1 is set * bit 1 QAS support, non-raid only * bit 0 IU support @@ -5054,8 +5234,8 @@ #endif /* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return. - * Else set the NEED_DV flag after Read Capacity Issued (disks) - * or Mode Sense (cdroms). + * Else set the NEED_DV flag after Read Capacity Issued (disks) + * or Mode Sense (cdroms). * * Tapes, initTarget will set this flag on completion of Inquiry command. * Called only if DV_NOT_DONE flag is set @@ -5091,7 +5271,7 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* - * If no Target, bus reset on 1st I/O. Set the flag to + * If no Target, bus reset on 1st I/O. Set the flag to * prevent any future negotiations to this device. */ static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id) @@ -5229,7 +5409,6 @@ maxfactor = MPT_ASYNC; } - /* Set the negotiation flags. */ negoFlags = ioc->spi_data.noQas; @@ -5341,9 +5520,9 @@ pData->Reserved = 0; pData->Configuration = cpu_to_le32(configuration); - dprintk((MYIOC_s_INFO_FMT + dprintk((MYIOC_s_INFO_FMT "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n", - ioc->name, id, (id | (bus<<8)), + ioc->name, id, (id | (bus<<8)), requested, configuration)); mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf); @@ -5371,13 +5550,6 @@ */ del_timer(&hd->TMtimer); -#ifdef MPT_SCSI_USE_NEW_EH - /* Set the error flag to 1 so that the function that started the - * task management request knows it timed out. - */ - hd->tmState = TM_STATE_ERROR; -#endif - /* Call the reset handler. Already had a TM request * timeout - so issue a diagnostic reset */ @@ -5389,8 +5561,8 @@ /* Because we have reset the IOC, no TM requests can be * pending. So let's make sure the tmPending flag is reset. */ - nehprintk((KERN_WARNING MYNAM - ": %s: mptscsih_taskmgmt_timeout\n", + nehprintk((KERN_WARNING MYNAM + ": %s: mptscsih_taskmgmt_timeout\n", hd->ioc->name)); hd->tmPending = 0; } @@ -5628,7 +5800,7 @@ if (hd->tmPending) { spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); return; - } else + } else hd->tmPending = 1; spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); @@ -5707,7 +5879,7 @@ pReq->ActionDataWord = 0; /* Reserved for this action */ //pReq->ActionDataSGE = 0; - mpt_add_sge((char *)&pReq->ActionDataSGE, + mpt_add_sge((char *)&pReq->ActionDataSGE, MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n", @@ -6036,7 +6208,7 @@ if (id == hostId) id++; - /* Write SDP1 for all SCSI devices + /* Write SDP1 for all SCSI devices * Alloc memory and set up config buffer */ if (hd->is_spi) { @@ -6159,7 +6331,7 @@ spin_unlock_irqrestore(&dvtaskQ_lock, flags); /* For this ioc, loop through all devices and do dv to each device. - * When complete with this ioc, search through the ioc list, and + * When complete with this ioc, search through the ioc list, and * for each scsi ioc found, do dv for all devices. Exit when no * device needs dv. */ @@ -6190,6 +6362,23 @@ if (hd == NULL) continue; + if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) { + mpt_read_ioc_pg_3(ioc); + if (ioc->spi_data.pIocPg3) { + Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; + int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + + while (numPDisk) { + if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE) + ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; + + pPDisk++; + numPDisk--; + } + } + ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3; + } + maxid = MIN (ioc->sh->max_id, MPT_MAX_SCSI_DEVICES); for (id = 0; id < maxid; id++) { @@ -6380,7 +6569,7 @@ lun = 0; bus = 0; - ddvtprintk((MYIOC_s_NOTE_FMT + ddvtprintk((MYIOC_s_NOTE_FMT "DV started: numIOs %d bus=%d, id %d dv @ %p\n", ioc->name, atomic_read(&queue_depth), bus, id, &dv)); @@ -6485,7 +6674,7 @@ /* Skip this ID? Set cfg.hdr to force config page write */ if ((ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID) && - (!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) { + (!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) { ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n", ioc->name, bus, id, lun)); @@ -6557,11 +6746,11 @@ /* Wide - narrow - wide workaround case */ - if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) { + if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) { /* Send an untagged command to reset disk Qs corrupted * when a parity error occurs on a Request Sense. */ - if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) || + if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) || ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) { @@ -6597,7 +6786,11 @@ rc = hd->pLocal->completion; if (rc == MPT_SCANDV_GOOD) { if (hd->pLocal->scsiStatus == STS_BUSY) { - retcode = 1; + if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0) + retcode = 1; + else + retcode = 0; + goto target_done; } } else if (rc == MPT_SCANDV_SENSE) { @@ -6669,7 +6862,7 @@ * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide * Resetart with a request for U160. */ - if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) { + if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) { doFallback = 1; } else { dv.cmd = MPT_UPDATE_MAX; @@ -6693,7 +6886,7 @@ } - } else if (rc == MPT_SCANDV_ISSUE_SENSE) + } else if (rc == MPT_SCANDV_ISSUE_SENSE) doFallback = 1; /* set fallback flag */ else if ((rc == MPT_SCANDV_DID_RESET) || (rc == MPT_SCANDV_SENSE)) doFallback = 1; /* set fallback flag */ @@ -6933,7 +7126,7 @@ mdelay (2000); notDone++; } else { - ddvprintk((MYIOC_s_INFO_FMT + ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.", ioc->name)); goto target_done; } @@ -6997,7 +7190,7 @@ patt = -1; continue; } - } + } goto target_done; } else @@ -7110,7 +7303,7 @@ if (hd->pLocal->completion == MPT_SCANDV_GOOD) iocmd.flags &= ~MPT_ICFLAG_RESERVED; } else { - printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", + printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", ioc->name, id); } } @@ -7128,7 +7321,7 @@ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); #if 0 - /* Double writes to SDP1 can cause problems, + /* Double writes to SDP1 can cause problems, * skip here since unnecessary */ /* Save the final negotiated settings to @@ -7284,7 +7477,7 @@ case MPT_SET_MIN: ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ", hd->ioc->name)); - /* Set page to asynchronous and narrow + /* Set page to asynchronous and narrow * Do not update now, breaks fallback routine. */ width = MPT_NARROW; offset = 0; @@ -7306,7 +7499,7 @@ case MPT_FALLBACK: ddvprintk((MYIOC_s_NOTE_FMT "Fallback: Start: offset %d, factor %x, width %d \n", - hd->ioc->name, dv->now.offset, + hd->ioc->name, dv->now.offset, dv->now.factor, dv->now.width)); width = dv->now.width; offset = dv->now.offset; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/message/fusion/mptscsih.h linux.21rc1-ac2/drivers/message/fusion/mptscsih.h --- linux.21rc1/drivers/message/fusion/mptscsih.h 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/message/fusion/mptscsih.h 2003-04-25 16:05:08.000000000 +0100 @@ -20,7 +20,7 @@ * (mailto:netscape.net) * (mailto:Pam.Delaney@lsil.com) * - * $Id: mptscsih.h,v 1.20 2002/10/17 20:16:00 pdelaney Exp $ + * $Id: mptscsih.h,v 1.22 2002/12/16 15:28:48 pdelaney Exp $ */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -73,9 +73,16 @@ * Try to keep these at 2^N-1 */ #define MPT_FC_CAN_QUEUE 63 -//#define MPT_SCSI_CAN_QUEUE 31 -#define MPT_SCSI_CAN_QUEUE MPT_FC_CAN_QUEUE -#define MPT_SCSI_CMD_PER_LUN 7 +#if defined MPT_SCSI_USE_NEW_EH + #define MPT_SCSI_CAN_QUEUE 127 +#else + #define MPT_SCSI_CAN_QUEUE 63 +#endif + +#define MPT_SCSI_CMD_PER_DEV_HIGH 31 +#define MPT_SCSI_CMD_PER_DEV_LOW 7 + +#define MPT_SCSI_CMD_PER_LUN 7 #define MPT_SCSI_MAX_SECTORS 8192 @@ -206,11 +213,16 @@ #define x_scsi_dev_reset mptscsih_dev_reset #define x_scsi_host_reset mptscsih_host_reset #define x_scsi_bios_param mptscsih_bios_param -#define x_scsi_select_queue_depths mptscsih_select_queue_depths #define x_scsi_taskmgmt_bh mptscsih_taskmgmt_bh #define x_scsi_old_abort mptscsih_old_abort #define x_scsi_old_reset mptscsih_old_reset +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) +#define x_scsi_slave_attach mptscsih_slave_attach +#else +#define x_scsi_select_queue_depths mptscsih_select_queue_depths +#endif +#define x_scsi_proc_info mptscsih_proc_info /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -229,13 +241,22 @@ extern int x_scsi_old_abort(Scsi_Cmnd *); extern int x_scsi_old_reset(Scsi_Cmnd *, unsigned int); #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +extern int x_scsi_bios_param(struct scsi_device * sdev, struct block_device *bdev, + sector_t capacity, int *ip); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) extern int x_scsi_bios_param(Disk *, struct block_device *, int *); #else extern int x_scsi_bios_param(Disk *, kdev_t, int *); #endif -extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *); extern void x_scsi_taskmgmt_bh(void *); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) +extern int x_scsi_slave_attach(Scsi_Device *); +#else +extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *); +#endif + +extern int x_scsi_proc_info(char *, char **, off_t, int, int, int); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) #define PROC_SCSI_DECL @@ -245,56 +266,84 @@ #ifdef MPT_SCSI_USE_NEW_EH -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) + +#define MPT_SCSIHOST { \ + PROC_SCSI_DECL \ + .proc_info = x_scsi_proc_info, \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .slave_attach = x_scsi_slave_attach, \ + .eh_strategy_handler = NULL, \ + .eh_abort_handler = x_scsi_abort, \ + .eh_device_reset_handler = x_scsi_dev_reset, \ + .eh_bus_reset_handler = x_scsi_bus_reset, \ + .eh_host_reset_handler = x_scsi_host_reset, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .max_sectors = MPT_SCSI_MAX_SECTORS, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ +} + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) #define MPT_SCSIHOST { \ - next: NULL, \ PROC_SCSI_DECL \ - name: "MPT SCSI Host", \ - detect: x_scsi_detect, \ - release: x_scsi_release, \ - info: x_scsi_info, \ - command: NULL, \ - queuecommand: x_scsi_queuecommand, \ - eh_strategy_handler: NULL, \ - eh_abort_handler: x_scsi_abort, \ - eh_device_reset_handler: x_scsi_dev_reset, \ - eh_bus_reset_handler: x_scsi_bus_reset, \ - eh_host_reset_handler: x_scsi_host_reset, \ - bios_param: x_scsi_bios_param, \ - can_queue: MPT_SCSI_CAN_QUEUE, \ - this_id: -1, \ - sg_tablesize: MPT_SCSI_SG_DEPTH, \ - max_sectors: MPT_SCSI_MAX_SECTORS, \ - cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING, \ + .proc_info = x_scsi_proc_info, \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .eh_strategy_handler = NULL, \ + .eh_abort_handler = x_scsi_abort, \ + .eh_device_reset_handler = x_scsi_dev_reset, \ + .eh_bus_reset_handler = x_scsi_bus_reset, \ + .eh_host_reset_handler = x_scsi_host_reset, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .max_sectors = MPT_SCSI_MAX_SECTORS, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ } #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */ #define MPT_SCSIHOST { \ - next: NULL, \ + .next = NULL, \ PROC_SCSI_DECL \ - name: "MPT SCSI Host", \ - detect: x_scsi_detect, \ - release: x_scsi_release, \ - info: x_scsi_info, \ - command: NULL, \ - queuecommand: x_scsi_queuecommand, \ - eh_strategy_handler: NULL, \ - eh_abort_handler: x_scsi_abort, \ - eh_device_reset_handler: x_scsi_dev_reset, \ - eh_bus_reset_handler: x_scsi_bus_reset, \ - eh_host_reset_handler: NULL, \ - bios_param: x_scsi_bios_param, \ - can_queue: MPT_SCSI_CAN_QUEUE, \ - this_id: -1, \ - sg_tablesize: MPT_SCSI_SG_DEPTH, \ - cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING, \ - use_new_eh_code: 1 \ + .proc_info = x_scsi_proc_info, \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .eh_strategy_handler = NULL, \ + .eh_abort_handler = x_scsi_abort, \ + .eh_device_reset_handler = x_scsi_dev_reset, \ + .eh_bus_reset_handler = x_scsi_bus_reset, \ + .eh_host_reset_handler = NULL, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ + .use_new_eh_code = 1 \ } #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */ @@ -302,23 +351,23 @@ #else /* MPT_SCSI_USE_NEW_EH */ #define MPT_SCSIHOST { \ - next: NULL, \ + .next = NULL, \ PROC_SCSI_DECL \ - name: "MPT SCSI Host", \ - detect: x_scsi_detect, \ - release: x_scsi_release, \ - info: x_scsi_info, \ - command: NULL, \ - queuecommand: x_scsi_queuecommand, \ - abort: x_scsi_old_abort, \ - reset: x_scsi_old_reset, \ - bios_param: x_scsi_bios_param, \ - can_queue: MPT_SCSI_CAN_QUEUE, \ - this_id: -1, \ - sg_tablesize: MPT_SCSI_SG_DEPTH, \ - cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .abort = x_scsi_old_abort, \ + .reset = x_scsi_old_reset, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING \ } #endif /* MPT_SCSI_USE_NEW_EH */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/mtd/maps/Config.in linux.21rc1-ac2/drivers/mtd/maps/Config.in --- linux.21rc1/drivers/mtd/maps/Config.in 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/mtd/maps/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -19,7 +19,7 @@ if [ "$CONFIG_X86" = "y" ]; then dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS - dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI $CONFIG_MTD_CONCAT dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/3c59x.c linux.21rc1-ac2/drivers/net/3c59x.c --- linux.21rc1/drivers/net/3c59x.c 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/3c59x.c 2003-04-22 16:44:37.000000000 +0100 @@ -166,6 +166,11 @@ - Rename wait_for_completion() to issue_and_wait() to avoid completion.h clash. + LK1.1.18ac 01Jul02 akpm + - Fix for undocumented transceiver power-up bit on some 3c566B's + (Donald Becker, Rahul Karnik) + + - See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.3 for more details. - Also see Documentation/networking/vortex.txt */ @@ -181,8 +186,8 @@ #define DRV_NAME "3c59x" -#define DRV_VERSION "LK1.1.16" -#define DRV_RELDATE "19 July 2001" +#define DRV_VERSION "LK1.1.18-ac" +#define DRV_RELDATE "1 July 2002" @@ -400,7 +405,7 @@ EEPROM_8BIT=0x10, /* AKPM: Uses 0x230 as the base bitmaps for EEPROM reads */ HAS_PWR_CTRL=0x20, HAS_MII=0x40, HAS_NWAY=0x80, HAS_CB_FNS=0x100, INVERT_MII_PWR=0x200, INVERT_LED_PWR=0x400, MAX_COLLISION_RESET=0x800, - EEPROM_OFFSET=0x1000, HAS_HWCKSM=0x2000 }; + EEPROM_OFFSET=0x1000, HAS_HWCKSM=0x2000, WNO_XCVR_PWR=0x4000 }; enum vortex_chips { CH_3C590 = 0, @@ -424,6 +429,7 @@ CH_3C905B_2, CH_3C905B_FX, CH_3C905C, + CH_3C905C2, CH_3C980, CH_3C9805, @@ -495,6 +501,8 @@ PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, }, {"3c905C Tornado", PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, }, + {"3c905C Tornado 2", + PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, }, {"3c980 Cyclone", PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, }, {"3c982 Dual Port Server Cyclone", @@ -509,7 +517,7 @@ HAS_HWCKSM, 128, }, {"3c556B Laptop Hurricane", PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR| - HAS_HWCKSM, 128, }, + WNO_XCVR_PWR|HAS_HWCKSM, 128, }, {"3c575 [Megahertz] 10/100 LAN CardBus", PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, }, @@ -561,6 +569,7 @@ { 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 }, { 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX }, { 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C }, + { 0x10B7, 0x9201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C2 }, { 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 }, { 0x10B7, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C9805 }, @@ -578,7 +587,6 @@ { 0x10B7, 0x6564, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656_1 }, { 0x10B7, 0x4500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C450 }, - { 0x10B7, 0x9201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C920 }, {0,} /* 0 terminated list. */ }; MODULE_DEVICE_TABLE(pci, vortex_pci_tbl); @@ -1190,6 +1198,10 @@ if (vp->drv_flags & INVERT_MII_PWR) n |= 0x4000; outw(n, ioaddr + Wn2_ResetOptions); + if (vp->drv_flags & WNO_XCVR_PWR) { + EL3WINDOW(0); + outw(0x0800, ioaddr); + } } /* Extract our information from the EEPROM data. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/aironet4500_core.c linux.21rc1-ac2/drivers/net/aironet4500_core.c --- linux.21rc1/drivers/net/aironet4500_core.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/aironet4500_core.c 2003-04-22 16:44:37.000000000 +0100 @@ -2676,10 +2676,8 @@ #endif //awc_dump_registers(dev); - if (adhoc & !max_mtu) - max_mtu= 2250; - else if (!max_mtu) - max_mtu= 1500; + if (!max_mtu) + max_mtu= adhoc ? 2250 : 1500; priv->sleeping_bap = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/arcnet/arcnet.c linux.21rc1-ac2/drivers/net/arcnet/arcnet.c --- linux.21rc1/drivers/net/arcnet/arcnet.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/arcnet/arcnet.c 2003-04-22 16:44:37.000000000 +0100 @@ -57,8 +57,8 @@ /* "do nothing" functions for protocol drivers */ static void null_rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); -static int null_build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr); +static int null_build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr); static int null_prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); @@ -512,7 +512,7 @@ arc_bcast_proto->suffix); proto = arc_bcast_proto; } - return proto->build_header(skb, type, _daddr); + return proto->build_header(skb, dev, type, _daddr); } @@ -528,6 +528,7 @@ int status = 0; /* default is failure */ unsigned short type; uint8_t daddr=0; + struct ArcProto *proto; if (skb->nh.raw - skb->mac.raw != 2) { BUGMSG(D_NORMAL, @@ -556,7 +557,8 @@ return 0; /* add the _real_ header this time! */ - arc_proto_map[lp->default_proto[daddr]]->build_header(skb, type, daddr); + proto = arc_proto_map[lp->default_proto[daddr]]; + proto->build_header(skb, dev, type, daddr); return 1; /* success */ } @@ -986,10 +988,9 @@ } -static int null_build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr) +static int null_build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr) { - struct net_device *dev = skb->dev; struct arcnet_local *lp = (struct arcnet_local *) dev->priv; BUGMSG(D_PROTO, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/arcnet/arc-rawmode.c linux.21rc1-ac2/drivers/net/arcnet/arc-rawmode.c --- linux.21rc1/drivers/net/arcnet/arc-rawmode.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/arcnet/arc-rawmode.c 2003-04-22 16:44:37.000000000 +0100 @@ -37,8 +37,8 @@ static void rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr); +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr); static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); @@ -137,10 +137,9 @@ * Create the ARCnet hard/soft headers for raw mode. * There aren't any soft headers in raw mode - not even the protocol id. */ -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr) +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr) { - struct net_device *dev = skb->dev; int hdr_size = ARC_HDR_SIZE; struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/arcnet/rfc1051.c linux.21rc1-ac2/drivers/net/arcnet/rfc1051.c --- linux.21rc1/drivers/net/arcnet/rfc1051.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/arcnet/rfc1051.c 2003-04-22 16:44:37.000000000 +0100 @@ -37,8 +37,8 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev); static void rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr); +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr); static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); @@ -170,10 +170,9 @@ /* * Create the ARCnet hard/soft headers for RFC1051. */ -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr) +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr) { - struct net_device *dev = skb->dev; struct arcnet_local *lp = (struct arcnet_local *) dev->priv; int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE; struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/arcnet/rfc1201.c linux.21rc1-ac2/drivers/net/arcnet/rfc1201.c --- linux.21rc1/drivers/net/arcnet/rfc1201.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/arcnet/rfc1201.c 2003-04-22 16:44:37.000000000 +0100 @@ -36,8 +36,8 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev); static void rx(struct net_device *dev, int bufnum, struct archdr *pkthdr, int length); -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr); +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr); static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, int bufnum); static int continue_tx(struct net_device *dev, int bufnum); @@ -375,10 +375,9 @@ /* Create the ARCnet hard/soft headers for RFC1201. */ -static int build_header(struct sk_buff *skb, unsigned short type, - uint8_t daddr) +static int build_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, uint8_t daddr) { - struct net_device *dev = skb->dev; struct arcnet_local *lp = (struct arcnet_local *) dev->priv; int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/Config.in linux.21rc1-ac2/drivers/net/Config.in --- linux.21rc1/drivers/net/Config.in 2003-04-22 16:39:35.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -185,7 +185,7 @@ dep_tristate ' Davicom DM910x/DM980x support' CONFIG_DM9102 $CONFIG_PCI dep_tristate ' EtherExpressPro/100 support (eepro100, original Becker driver)' CONFIG_EEPRO100 $CONFIG_PCI if [ "$CONFIG_VISWS" = "y" ]; then - define_mbool CONFIG_EEPRO100_PIO y + define_bool CONFIG_EEPRO100_PIO y else dep_mbool ' Use PIO instead of MMIO' CONFIG_EEPRO100_PIO $CONFIG_EEPRO100 fi diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/pcmcia/aironet4500_cs.c linux.21rc1-ac2/drivers/net/pcmcia/aironet4500_cs.c --- linux.21rc1/drivers/net/pcmcia/aironet4500_cs.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/pcmcia/aironet4500_cs.c 2003-04-23 14:31:24.000000000 +0100 @@ -282,7 +282,7 @@ }; memset(dev,0,sizeof(struct net_device)); dev->priv = kmalloc(sizeof(struct awc_private), GFP_KERNEL); - if (!dev->priv ) {printk(KERN_CRIT "out of mem on dev priv alloc \n"); return NULL;}; + if (!dev->priv ) {printk(KERN_CRIT "out of mem on dev priv alloc \n"); kfree(dev); return NULL;}; memset(dev->priv,0,sizeof(struct awc_private)); // link->dev->minor = dev->minor; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/pcnet32.c linux.21rc1-ac2/drivers/net/pcnet32.c --- linux.21rc1/drivers/net/pcnet32.c 2003-04-22 16:39:36.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/pcnet32.c 2003-04-22 16:44:37.000000000 +0100 @@ -1,5 +1,5 @@ -/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */ -/* +/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. + * * Copyright 1996-1999 Thomas Bogendoerfer * * Derived from the lance driver written 1993,1994,1995 by Donald Becker. @@ -543,7 +543,7 @@ /* initialize variables */ fdx = mii = fset = dxsuflo = ltint = 0; chip_version = (chip_version >> 12) & 0xffff; - + switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; /* PCI */ @@ -1175,19 +1175,12 @@ if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; if (err_status & 0x10000000) lp->stats.tx_window_errors++; -#ifndef DO_DXSUFLO if (err_status & 0x40000000) { lp->stats.tx_fifo_errors++; - /* Ackk! On FIFO errors the Tx unit is turned off! */ - /* Remove this verbosity later! */ - printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", - dev->name, csr0); - must_restart = 1; - } -#else - if (err_status & 0x40000000) { - lp->stats.tx_fifo_errors++; - if (! lp->dxsuflo) { /* If controller doesn't recover ... */ +#ifdef DO_DXSUFLO + if (! lp->dxsuflo) +#endif + { /* If controller doesn't recover ... */ /* Ackk! On FIFO errors the Tx unit is turned off! */ /* Remove this verbosity later! */ printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", @@ -1195,7 +1188,6 @@ must_restart = 1; } } -#endif } else { if (status & 0x1800) lp->stats.collisions++; @@ -1722,12 +1714,13 @@ } } + module_init(pcnet32_init_module); module_exit(pcnet32_cleanup_module); /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c pcnet32.c" + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include/linux -Wall -Wstrict-prototypes -O2 -m486 -c pcnet32.c" * c-indent-level: 4 * tab-width: 8 * End: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/r8169.c linux.21rc1-ac2/drivers/net/r8169.c --- linux.21rc1/drivers/net/r8169.c 2003-04-22 16:39:36.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/r8169.c 2003-04-22 16:44:37.000000000 +0100 @@ -1110,7 +1110,7 @@ .name = MODULENAME, .id_table = rtl8169_pci_tbl, .probe = rtl8169_init_one, - .remove = rtl8169_remove_one, + .remove = __devexit_p(rtl8169_remove_one), .suspend = NULL, .resume = NULL, }; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/rrunner.c linux.21rc1-ac2/drivers/net/rrunner.c --- linux.21rc1/drivers/net/rrunner.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/rrunner.c 2003-04-23 14:32:09.000000000 +0100 @@ -1216,7 +1216,6 @@ rrpriv->info = kmalloc(sizeof(struct rr_info), GFP_KERNEL); if (!rrpriv->info){ - rrpriv->rx_ctrl = NULL; ecode = -ENOMEM; goto error; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/sis900.c linux.21rc1-ac2/drivers/net/sis900.c --- linux.21rc1/drivers/net/sis900.c 2003-04-22 16:39:36.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/sis900.c 2003-04-22 19:10:48.000000000 +0100 @@ -125,6 +125,7 @@ { "ICS LAN PHY", 0x0015, 0xF440, LAN }, { "NS 83851 PHY", 0x2000, 0x5C20, MIX }, { "Realtek RTL8201 PHY", 0x0000, 0x8200, LAN }, + { "VIA 6103 PHY", 0x0101, 0x8f20, LAN }, {0,}, }; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/sk98lin/build_no.c linux.21rc1-ac2/drivers/net/sk98lin/build_no.c --- linux.21rc1/drivers/net/sk98lin/build_no.c 2003-04-22 16:39:36.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/sk98lin/build_no.c 2003-04-22 16:44:37.000000000 +0100 @@ -7,4 +7,3 @@ static const char SysKonnectBuildNumber[] = "@(#)SK-BUILD: 6.02 (20021219) PL: ALL.01"; -^Z \ No newline at end of file diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/tun.c linux.21rc1-ac2/drivers/net/tun.c --- linux.21rc1/drivers/net/tun.c 2003-04-22 16:38:38.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/tun.c 2003-04-22 16:44:37.000000000 +0100 @@ -188,7 +188,7 @@ size_t len = count; if (!(tun->flags & TUN_NO_PI)) { - if ((len -= sizeof(pi)) < 0) + if ((len -= sizeof(pi)) > len) return -EINVAL; memcpy_fromiovec((void *)&pi, iv, sizeof(pi)); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/8253x/8253xsyn.c linux.21rc1-ac2/drivers/net/wan/8253x/8253xsyn.c --- linux.21rc1/drivers/net/wan/8253x/8253xsyn.c 2003-04-22 16:38:37.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/8253x/8253xsyn.c 2003-04-22 16:44:37.000000000 +0100 @@ -1030,7 +1030,7 @@ } #if 0 - if ((tty->count == 1) && (port->count != 0)) + if ((atomic_read(&tty->count) == 1) && (port->count != 0)) { /* * Uh, oh. tty->count is 1, which means that the tty diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/c101.c linux.21rc1-ac2/drivers/net/wan/c101.c --- linux.21rc1/drivers/net/wan/c101.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/c101.c 2003-04-22 16:44:37.000000000 +0100 @@ -1,12 +1,11 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000-2002 Krzysztof Halasa + * Copyright (C) 2000-2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. * * For information see http://hq.pm.waw.pl/hdlc/ * @@ -31,7 +30,7 @@ #include "hd64570.h" -static const char* version = "Moxa C101 driver version: 1.10"; +static const char* version = "Moxa C101 driver version: 1.12"; static const char* devname = "C101"; #define C101_PAGE 0x1D00 @@ -78,7 +77,12 @@ #define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) #define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) #define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg)) -#define sca_outw(value, reg, card) writew(value, (card)->win0base + C101_SCA + (reg)) + +/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */ +#define sca_outw(value, reg, card) do { \ + writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \ + writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\ +} while(0) #define port_to_card(port) (port) #define log_node(port) (0) @@ -352,7 +356,7 @@ c101_run(irq, ram); if (*hw == '\x0') - return 0; + return first_card ? 0 : -ENOSYS; }while(*hw++ == ':'); printk(KERN_ERR "c101: invalid hardware parameters\n"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/Config.in linux.21rc1-ac2/drivers/net/wan/Config.in --- linux.21rc1/drivers/net/wan/Config.in 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -68,6 +68,7 @@ tristate ' Generic HDLC layer' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then bool ' Raw HDLC support' CONFIG_HDLC_RAW + bool ' Raw HDLC Ethernet device support' CONFIG_HDLC_RAW_ETH bool ' Cisco HDLC support' CONFIG_HDLC_CISCO bool ' Frame Relay support' CONFIG_HDLC_FR bool ' Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP @@ -76,6 +77,18 @@ else comment ' X.25/LAPB support is disabled' fi + if [ "$CONFIG_PCI" != "n" ]; then + dep_tristate ' Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)' CONFIG_PC300 $CONFIG_HDLC + if [ "$CONFIG_PC300" != "n" ]; then + if ["$CONFIG_PPP" != "n" -a "$CONFIG_PPP_MULTLINK" != "n" -a "$CONFIG_PPP_SYNCTTY" != "n" -a "$CONFIG_HDLC_PPP" = "y"]; + then + bool ' Cyclades-PC300 MLPPP support' CONFIG_PC300_MLPPP + else + comment ' Cyclades-PC300 MLPPP support is disabled. You have to enable PPP, PPP_MULTILINK' + comment ' PPP_SYNCTTY and HDLC_PPP to use this package.' + fi + fi + fi dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC dep_tristate ' FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/cosa.c linux.21rc1-ac2/drivers/net/wan/cosa.c --- linux.21rc1/drivers/net/wan/cosa.c 2003-04-22 16:38:37.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/cosa.c 2003-04-22 16:44:37.000000000 +0100 @@ -1071,7 +1071,8 @@ return -EPERM; } - if (get_user(addr, &(d->addr)) || + if (verify_area(VERIFY_READ, d, sizeof(*d)) || + __get_user(addr, &(d->addr)) || __get_user(len, &(d->len)) || __get_user(code, &(d->code))) return -EFAULT; @@ -1079,7 +1080,7 @@ /* If something fails, force the user to reset the card */ cosa->firmware_status &= ~COSA_FW_RESET; - if ((i=readmem(cosa, d->code, len, addr)) < 0) { + if ((i=readmem(cosa, code, len, addr)) < 0) { printk(KERN_NOTICE "cosa%d: reading memory failed: %d\n", cosa->num, i); return -EIO; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/dscc4.c linux.21rc1-ac2/drivers/net/wan/dscc4.c --- linux.21rc1/drivers/net/wan/dscc4.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/dscc4.c 2003-04-22 16:44:37.000000000 +0100 @@ -487,9 +487,9 @@ skb = dev_alloc_skb(len); dpriv->rx_skbuff[dirty] = skb; if (skb) { - skb->dev = dev; - skb->protocol = htons(ETH_P_HDLC); - skb->mac.raw = skb->data; + skb->dev = dev; + skb->protocol = hdlc_type_trans(skb, dev); + skb->mac.raw = skb->data; rx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, len, PCI_DMA_FROMDEVICE); } else { @@ -1757,7 +1757,7 @@ (++i%TX_RING_SIZE)*sizeof(*tx_fd)); } while (i < TX_RING_SIZE); - if (dscc4_init_dummy_skb(dpriv) < 0) + if (dscc4_init_dummy_skb(dpriv) == NULL) goto err_free_dma_tx; memset(dpriv->rx_skbuff, 0, sizeof(struct sk_buff *)*RX_RING_SIZE); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/farsync.c linux.21rc1-ac2/drivers/net/wan/farsync.c --- linux.21rc1/drivers/net/wan/farsync.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/farsync.c 2003-04-22 16:44:37.000000000 +0100 @@ -764,7 +764,7 @@ /* Push upstream */ skb->mac.raw = skb->data; skb->dev = hdlc_to_dev ( &port->hdlc ); - skb->protocol = htons ( ETH_P_HDLC ); + skb->protocol = hdlc_type_trans(skb, skb->dev); netif_rx ( skb ); port_to_dev ( port )->last_rx = jiffies; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hd64572.h linux.21rc1-ac2/drivers/net/wan/hd64572.h --- linux.21rc1/drivers/net/wan/hd64572.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hd64572.h 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,433 @@ +/* + * hd64572.h Description of the Hitachi HD64572 (SCA-II), valid for + * CPU modes 0 & 2. + * + * Author: Ivan Passos + * + * Copyright: (c) 2000-2001 Cyclades 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. + * + */ + +#ifndef _HD64572_H +#define _HD64572_H + +/* Illegal Access Register */ +#define ILAR 0x00 + +/* Wait Controller Registers */ +#define PABR0L 0x20 /* Physical Addr Boundary Register 0 L */ +#define PABR0H 0x21 /* Physical Addr Boundary Register 0 H */ +#define PABR1L 0x22 /* Physical Addr Boundary Register 1 L */ +#define PABR1H 0x23 /* Physical Addr Boundary Register 1 H */ +#define WCRL 0x24 /* Wait Control Register L */ +#define WCRM 0x25 /* Wait Control Register M */ +#define WCRH 0x26 /* Wait Control Register H */ + +/* Interrupt Registers */ +#define IVR 0x60 /* Interrupt Vector Register */ +#define IMVR 0x64 /* Interrupt Modified Vector Register */ +#define ITCR 0x68 /* Interrupt Control Register */ +#define ISR0 0x6c /* Interrupt Status Register 0 */ +#define ISR1 0x70 /* Interrupt Status Register 1 */ +#define IER0 0x74 /* Interrupt Enable Register 0 */ +#define IER1 0x78 /* Interrupt Enable Register 1 */ + +/* Register Access Macros (chan is 0 or 1 in _any_ case) */ +#define M_REG(reg, chan) (reg + 0x80*chan) /* MSCI */ +#define DRX_REG(reg, chan) (reg + 0x40*chan) /* DMA Rx */ +#define DTX_REG(reg, chan) (reg + 0x20*(2*chan + 1)) /* DMA Tx */ +#define TRX_REG(reg, chan) (reg + 0x20*chan) /* Timer Rx */ +#define TTX_REG(reg, chan) (reg + 0x10*(2*chan + 1)) /* Timer Tx */ +#define ST_REG(reg, chan) (reg + 0x80*chan) /* Status Cnt */ +#define IR0_DRX(val, chan) ((val)<<(8*(chan))) /* Int DMA Rx */ +#define IR0_DTX(val, chan) ((val)<<(4*(2*chan + 1))) /* Int DMA Tx */ +#define IR0_M(val, chan) ((val)<<(8*(chan))) /* Int MSCI */ + +/* MSCI Channel Registers */ +#define MD0 0x138 /* Mode reg 0 */ +#define MD1 0x139 /* Mode reg 1 */ +#define MD2 0x13a /* Mode reg 2 */ +#define MD3 0x13b /* Mode reg 3 */ +#define CTL 0x130 /* Control reg */ +#define RXS 0x13c /* RX clock source */ +#define TXS 0x13d /* TX clock source */ +#define EXS 0x13e /* External clock input selection */ +#define TMCT 0x144 /* Time constant (Tx) */ +#define TMCR 0x145 /* Time constant (Rx) */ +#define CMD 0x128 /* Command reg */ +#define ST0 0x118 /* Status reg 0 */ +#define ST1 0x119 /* Status reg 1 */ +#define ST2 0x11a /* Status reg 2 */ +#define ST3 0x11b /* Status reg 3 */ +#define ST4 0x11c /* Status reg 4 */ +#define FST 0x11d /* frame Status reg */ +#define IE0 0x120 /* Interrupt enable reg 0 */ +#define IE1 0x121 /* Interrupt enable reg 1 */ +#define IE2 0x122 /* Interrupt enable reg 2 */ +#define IE4 0x124 /* Interrupt enable reg 4 */ +#define FIE 0x125 /* Frame Interrupt enable reg */ +#define SA0 0x140 /* Syn Address reg 0 */ +#define SA1 0x141 /* Syn Address reg 1 */ +#define IDL 0x142 /* Idle register */ +#define TRBL 0x100 /* TX/RX buffer reg L */ +#define TRBK 0x101 /* TX/RX buffer reg K */ +#define TRBJ 0x102 /* TX/RX buffer reg J */ +#define TRBH 0x103 /* TX/RX buffer reg H */ +#define TRC0 0x148 /* TX Ready control reg 0 */ +#define TRC1 0x149 /* TX Ready control reg 1 */ +#define RRC 0x14a /* RX Ready control reg */ +#define CST0 0x108 /* Current Status Register 0 */ +#define CST1 0x109 /* Current Status Register 1 */ +#define CST2 0x10a /* Current Status Register 2 */ +#define CST3 0x10b /* Current Status Register 3 */ +#define GPO 0x131 /* General Purpose Output Pin Ctl Reg */ +#define TFS 0x14b /* Tx Start Threshold Ctl Reg */ +#define TFN 0x143 /* Inter-transmit-frame Time Fill Ctl Reg */ +#define TBN 0x110 /* Tx Buffer Number Reg */ +#define RBN 0x111 /* Rx Buffer Number Reg */ +#define TNR0 0x150 /* Tx DMA Request Ctl Reg 0 */ +#define TNR1 0x151 /* Tx DMA Request Ctl Reg 1 */ +#define TCR 0x152 /* Tx DMA Critical Request Reg */ +#define RNR 0x154 /* Rx DMA Request Ctl Reg */ +#define RCR 0x156 /* Rx DMA Critical Request Reg */ + +/* Timer Registers */ +#define TCNTL 0x200 /* Timer Upcounter L */ +#define TCNTH 0x201 /* Timer Upcounter H */ +#define TCONRL 0x204 /* Timer Constant Register L */ +#define TCONRH 0x205 /* Timer Constant Register H */ +#define TCSR 0x206 /* Timer Control/Status Register */ +#define TEPR 0x207 /* Timer Expand Prescale Register */ + +/* DMA registers */ +#define PCR 0x40 /* DMA priority control reg */ +#define DRR 0x44 /* DMA reset reg */ +#define DMER 0x07 /* DMA Master Enable reg */ +#define BTCR 0x08 /* Burst Tx Ctl Reg */ +#define BOLR 0x0c /* Back-off Length Reg */ +#define DSR_RX(chan) (0x48 + 2*chan) /* DMA Status Reg (Rx) */ +#define DSR_TX(chan) (0x49 + 2*chan) /* DMA Status Reg (Tx) */ +#define DIR_RX(chan) (0x4c + 2*chan) /* DMA Interrupt Enable Reg (Rx) */ +#define DIR_TX(chan) (0x4d + 2*chan) /* DMA Interrupt Enable Reg (Tx) */ +#define FCT_RX(chan) (0x50 + 2*chan) /* Frame End Interrupt Counter (Rx) */ +#define FCT_TX(chan) (0x51 + 2*chan) /* Frame End Interrupt Counter (Tx) */ +#define DMR_RX(chan) (0x54 + 2*chan) /* DMA Mode Reg (Rx) */ +#define DMR_TX(chan) (0x55 + 2*chan) /* DMA Mode Reg (Tx) */ +#define DCR_RX(chan) (0x58 + 2*chan) /* DMA Command Reg (Rx) */ +#define DCR_TX(chan) (0x59 + 2*chan) /* DMA Command Reg (Tx) */ + +/* DMA Channel Registers */ +#define DARL 0x80 /* Dest Addr Register L (single-block, RX only) */ +#define DARH 0x81 /* Dest Addr Register H (single-block, RX only) */ +#define DARB 0x82 /* Dest Addr Register B (single-block, RX only) */ +#define DARBH 0x83 /* Dest Addr Register BH (single-block, RX only) */ +#define SARL 0x80 /* Source Addr Register L (single-block, TX only) */ +#define SARH 0x81 /* Source Addr Register H (single-block, TX only) */ +#define SARB 0x82 /* Source Addr Register B (single-block, TX only) */ +#define DARBH 0x83 /* Source Addr Register BH (single-block, TX only) */ +#define BARL 0x80 /* Buffer Addr Register L (chained-block) */ +#define BARH 0x81 /* Buffer Addr Register H (chained-block) */ +#define BARB 0x82 /* Buffer Addr Register B (chained-block) */ +#define BARBH 0x83 /* Buffer Addr Register BH (chained-block) */ +#define CDAL 0x84 /* Current Descriptor Addr Register L */ +#define CDAH 0x85 /* Current Descriptor Addr Register H */ +#define CDAB 0x86 /* Current Descriptor Addr Register B */ +#define CDABH 0x87 /* Current Descriptor Addr Register BH */ +#define EDAL 0x88 /* Error Descriptor Addr Register L */ +#define EDAH 0x89 /* Error Descriptor Addr Register H */ +#define EDAB 0x8a /* Error Descriptor Addr Register B */ +#define EDABH 0x8b /* Error Descriptor Addr Register BH */ +#define BFLL 0x90 /* RX Buffer Length L (only RX) */ +#define BFLH 0x91 /* RX Buffer Length H (only RX) */ +#define BCRL 0x8c /* Byte Count Register L */ +#define BCRH 0x8d /* Byte Count Register H */ + +/* Block Descriptor Structure */ +typedef struct { + unsigned long next; /* pointer to next block descriptor */ + unsigned long ptbuf; /* buffer pointer */ + unsigned short len; /* data length */ + unsigned char status; /* status */ + unsigned char filler[5]; /* alignment filler (16 bytes) */ +} pcsca_bd_t; + +/* + Descriptor Status definitions: + + Bit Transmission Reception + + 7 EOM EOM + 6 - Short Frame + 5 - Abort + 4 - Residual bit + 3 Underrun Overrun + 2 - CRC + 1 Ownership Ownership + 0 EOT - +*/ +#define DST_EOT 0x01 /* End of transmit command */ +#define DST_OSB 0x02 /* Ownership bit */ +#define DST_CRC 0x04 /* CRC Error */ +#define DST_OVR 0x08 /* Overrun */ +#define DST_UDR 0x08 /* Underrun */ +#define DST_RBIT 0x10 /* Residual bit */ +#define DST_ABT 0x20 /* Abort */ +#define DST_SHRT 0x40 /* Short Frame */ +#define DST_EOM 0x80 /* End of Message */ + +/* Status Counter Registers */ +#define CMCR 0x158 /* Counter Master Ctl Reg */ +#define TECNTL 0x160 /* Tx EOM Counter L */ +#define TECNTM 0x161 /* Tx EOM Counter M */ +#define TECNTH 0x162 /* Tx EOM Counter H */ +#define TECCR 0x163 /* Tx EOM Counter Ctl Reg */ +#define URCNTL 0x164 /* Underrun Counter L */ +#define URCNTH 0x165 /* Underrun Counter H */ +#define URCCR 0x167 /* Underrun Counter Ctl Reg */ +#define RECNTL 0x168 /* Rx EOM Counter L */ +#define RECNTM 0x169 /* Rx EOM Counter M */ +#define RECNTH 0x16a /* Rx EOM Counter H */ +#define RECCR 0x16b /* Rx EOM Counter Ctl Reg */ +#define ORCNTL 0x16c /* Overrun Counter L */ +#define ORCNTH 0x16d /* Overrun Counter H */ +#define ORCCR 0x16f /* Overrun Counter Ctl Reg */ +#define CECNTL 0x170 /* CRC Counter L */ +#define CECNTH 0x171 /* CRC Counter H */ +#define CECCR 0x173 /* CRC Counter Ctl Reg */ +#define ABCNTL 0x174 /* Abort frame Counter L */ +#define ABCNTH 0x175 /* Abort frame Counter H */ +#define ABCCR 0x177 /* Abort frame Counter Ctl Reg */ +#define SHCNTL 0x178 /* Short frame Counter L */ +#define SHCNTH 0x179 /* Short frame Counter H */ +#define SHCCR 0x17b /* Short frame Counter Ctl Reg */ +#define RSCNTL 0x17c /* Residual bit Counter L */ +#define RSCNTH 0x17d /* Residual bit Counter H */ +#define RSCCR 0x17f /* Residual bit Counter Ctl Reg */ + +/* Register Programming Constants */ + +#define IR0_DMIC 0x00000001 +#define IR0_DMIB 0x00000002 +#define IR0_DMIA 0x00000004 +#define IR0_EFT 0x00000008 +#define IR0_DMAREQ 0x00010000 +#define IR0_TXINT 0x00020000 +#define IR0_RXINTB 0x00040000 +#define IR0_RXINTA 0x00080000 +#define IR0_TXRDY 0x00100000 +#define IR0_RXRDY 0x00200000 + +#define MD0_CRC16_0 0x00 +#define MD0_CRC16_1 0x01 +#define MD0_CRC32 0x02 +#define MD0_CRC_CCITT 0x03 +#define MD0_CRCC0 0x04 +#define MD0_CRCC1 0x08 +#define MD0_AUTO_ENA 0x10 +#define MD0_ASYNC 0x00 +#define MD0_BY_MSYNC 0x20 +#define MD0_BY_BISYNC 0x40 +#define MD0_BY_EXT 0x60 +#define MD0_BIT_SYNC 0x80 +#define MD0_TRANSP 0xc0 + +#define MD1_NOADDR 0x00 +#define MD1_SADDR1 0x40 +#define MD1_SADDR2 0x80 +#define MD1_DADDR 0xc0 + +#define MD2_F_DUPLEX 0x00 +#define MD2_AUTO_ECHO 0x01 +#define MD2_LOOP_HI_Z 0x02 +#define MD2_LOOP_MIR 0x03 +#define MD2_ADPLL_X8 0x00 +#define MD2_ADPLL_X16 0x08 +#define MD2_ADPLL_X32 0x10 +#define MD2_NRZ 0x00 +#define MD2_NRZI 0x20 +#define MD2_NRZ_IEEE 0x40 +#define MD2_MANCH 0x00 +#define MD2_FM1 0x20 +#define MD2_FM0 0x40 +#define MD2_FM 0x80 + +#define CTL_RTS 0x01 +#define CTL_DTR 0x02 +#define CTL_SYN 0x04 +#define CTL_IDLC 0x10 +#define CTL_UDRNC 0x20 +#define CTL_URSKP 0x40 +#define CTL_URCT 0x80 + +#define RXS_BR0 0x01 +#define RXS_BR1 0x02 +#define RXS_BR2 0x04 +#define RXS_BR3 0x08 +#define RXS_ECLK 0x00 +#define RXS_ECLK_NS 0x20 +#define RXS_IBRG 0x40 +#define RXS_PLL1 0x50 +#define RXS_PLL2 0x60 +#define RXS_PLL3 0x70 +#define RXS_DRTXC 0x80 + +#define TXS_BR0 0x01 +#define TXS_BR1 0x02 +#define TXS_BR2 0x04 +#define TXS_BR3 0x08 +#define TXS_ECLK 0x00 +#define TXS_IBRG 0x40 +#define TXS_RCLK 0x60 +#define TXS_DTRXC 0x80 + +#define EXS_RES0 0x01 +#define EXS_RES1 0x02 +#define EXS_RES2 0x04 +#define EXS_TES0 0x10 +#define EXS_TES1 0x20 +#define EXS_TES2 0x40 + +#define CMD_RX_RST 0x11 +#define CMD_RX_ENA 0x12 +#define CMD_RX_DIS 0x13 +#define CMD_RX_CRC_INIT 0x14 +#define CMD_RX_MSG_REJ 0x15 +#define CMD_RX_MP_SRCH 0x16 +#define CMD_RX_CRC_EXC 0x17 +#define CMD_RX_CRC_FRC 0x18 +#define CMD_TX_RST 0x01 +#define CMD_TX_ENA 0x02 +#define CMD_TX_DISA 0x03 +#define CMD_TX_CRC_INIT 0x04 +#define CMD_TX_CRC_EXC 0x05 +#define CMD_TX_EOM 0x06 +#define CMD_TX_ABORT 0x07 +#define CMD_TX_MP_ON 0x08 +#define CMD_TX_BUF_CLR 0x09 +#define CMD_TX_DISB 0x0b +#define CMD_CH_RST 0x21 +#define CMD_SRCH_MODE 0x31 +#define CMD_NOP 0x00 + +#define ST0_RXRDY 0x01 +#define ST0_TXRDY 0x02 +#define ST0_RXINTB 0x20 +#define ST0_RXINTA 0x40 +#define ST0_TXINT 0x80 + +#define ST1_IDLE 0x01 +#define ST1_ABORT 0x02 +#define ST1_CDCD 0x04 +#define ST1_CCTS 0x08 +#define ST1_SYN_FLAG 0x10 +#define ST1_CLMD 0x20 +#define ST1_TXIDLE 0x40 +#define ST1_UDRN 0x80 + +#define ST2_CRCE 0x04 +#define ST2_ONRN 0x08 +#define ST2_RBIT 0x10 +#define ST2_ABORT 0x20 +#define ST2_SHORT 0x40 +#define ST2_EOM 0x80 + +#define ST3_RX_ENA 0x01 +#define ST3_TX_ENA 0x02 +#define ST3_DCD 0x04 +#define ST3_CTS 0x08 +#define ST3_SRCH_MODE 0x10 +#define ST3_SLOOP 0x20 +#define ST3_GPI 0x80 + +#define ST4_RDNR 0x01 +#define ST4_RDCR 0x02 +#define ST4_TDNR 0x04 +#define ST4_TDCR 0x08 +#define ST4_OCLM 0x20 +#define ST4_CFT 0x40 +#define ST4_CGPI 0x80 + +#define FST_CRCEF 0x04 +#define FST_OVRNF 0x08 +#define FST_RBIF 0x10 +#define FST_ABTF 0x20 +#define FST_SHRTF 0x40 +#define FST_EOMF 0x80 + +#define IE0_RXRDY 0x01 +#define IE0_TXRDY 0x02 +#define IE0_RXINTB 0x20 +#define IE0_RXINTA 0x40 +#define IE0_TXINT 0x80 + +#define IE1_IDLD 0x01 +#define IE1_ABTD 0x02 +#define IE1_CDCD 0x04 +#define IE1_CCTS 0x08 +#define IE1_SYNCD 0x10 +#define IE1_CLMD 0x20 +#define IE1_IDL 0x40 +#define IE1_UDRN 0x80 + +#define IE2_CRCE 0x04 +#define IE2_OVRN 0x08 +#define IE2_RBIT 0x10 +#define IE2_ABT 0x20 +#define IE2_SHRT 0x40 +#define IE2_EOM 0x80 + +#define IE4_RDNR 0x01 +#define IE4_RDCR 0x02 +#define IE4_TDNR 0x04 +#define IE4_TDCR 0x08 +#define IE4_OCLM 0x20 +#define IE4_CFT 0x40 +#define IE4_CGPI 0x80 + +#define FIE_CRCEF 0x04 +#define FIE_OVRNF 0x08 +#define FIE_RBIF 0x10 +#define FIE_ABTF 0x20 +#define FIE_SHRTF 0x40 +#define FIE_EOMF 0x80 + +#define DSR_DWE 0x01 +#define DSR_DE 0x02 +#define DSR_REF 0x04 +#define DSR_UDRF 0x04 +#define DSR_COA 0x08 +#define DSR_COF 0x10 +#define DSR_BOF 0x20 +#define DSR_EOM 0x40 +#define DSR_EOT 0x80 + +#define DIR_REF 0x04 +#define DIR_UDRF 0x04 +#define DIR_COA 0x08 +#define DIR_COF 0x10 +#define DIR_BOF 0x20 +#define DIR_EOM 0x40 +#define DIR_EOT 0x80 + +#define DMR_CNTE 0x02 +#define DMR_NF 0x04 +#define DMR_SEOME 0x08 +#define DMR_TMOD 0x10 + +#define DCR_SW_ABT 0x01 +#define DCR_FCT_CLR 0x02 + +#define PCR_PR0 0x01 +#define PCR_PR1 0x02 +#define PCR_PR2 0x04 +#define PCR_CCC 0x08 +#define PCR_BRC 0x10 +#define PCR_OSB 0x40 +#define PCR_BURST 0x80 + +#endif /* (_HD64572_H) */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hd6457x.c linux.21rc1-ac2/drivers/net/wan/hd6457x.c --- linux.21rc1/drivers/net/wan/hd6457x.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hd6457x.c 2003-04-22 16:44:37.000000000 +0100 @@ -1,12 +1,11 @@ /* * Hitachi SCA HD64570 and HD64572 common driver for Linux * - * Copyright (C) 1998-2000 Krzysztof Halasa + * Copyright (C) 1998-2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. * * Sources of information: * Hitachi HD64570 SCA User's Manual @@ -42,7 +41,7 @@ #error Either hd64570.h or hd64572.h must be included #endif -static char sca_version[]="1.09"; +static char sca_version[]="1.12"; #define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET) @@ -294,7 +293,7 @@ skb->mac.raw = skb->data; skb->dev = hdlc_to_dev(&port->hdlc); skb->dev->last_rx = jiffies; - skb->protocol = htons(ETH_P_HDLC); + skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc)); netif_rx(skb); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_cisco.c linux.21rc1-ac2/drivers/net/wan/hdlc_cisco.c --- linux.21rc1/drivers/net/wan/hdlc_cisco.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_cisco.c 2003-04-22 16:44:37.000000000 +0100 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * Cisco HDLC support * - * Copyright (C) 2000 - 2001 Krzysztof Halasa + * Copyright (C) 2000 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -85,12 +84,37 @@ skb_put(skb, sizeof(cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); + skb->nh.raw = skb->data; dev_queue_xmit(skb); } +static unsigned short cisco_type_trans(struct sk_buff *skb, + struct net_device *dev) +{ + hdlc_header *data = (hdlc_header*)skb->data; + + if (skb->len < sizeof(hdlc_header)) + return __constant_htons(ETH_P_HDLC); + + if (data->address != CISCO_MULTICAST && + data->address != CISCO_UNICAST) + return __constant_htons(ETH_P_HDLC); + + switch(data->protocol) { + case __constant_htons(ETH_P_IP): + case __constant_htons(ETH_P_IPX): + case __constant_htons(ETH_P_IPV6): + skb_pull(skb, sizeof(hdlc_header)); + return data->protocol; + default: + return __constant_htons(ETH_P_HDLC); + } +} + + static void cisco_rx(struct sk_buff *skb) { hdlc_device *hdlc = dev_to_hdlc(skb->dev); @@ -109,14 +133,6 @@ skb_pull(skb, sizeof(hdlc_header)); switch(ntohs(data->protocol)) { - case ETH_P_IP: - case ETH_P_IPX: - case ETH_P_IPV6: - skb->protocol = data->protocol; - skb->dev = hdlc_to_dev(hdlc); - netif_rx(skb); - return; - case CISCO_SYS_INFO: /* Packet is not needed, drop it. */ dev_kfree_skb_any(skb); @@ -288,6 +304,7 @@ hdlc->open = cisco_open; hdlc->stop = cisco_close; hdlc->netif_rx = cisco_rx; + hdlc->type_trans = cisco_type_trans; hdlc->proto = IF_PROTO_CISCO; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = cisco_hard_header; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_fr.c linux.21rc1-ac2/drivers/net/wan/hdlc_fr.c --- linux.21rc1/drivers/net/wan/hdlc_fr.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_fr.c 2003-04-22 16:44:37.000000000 +0100 @@ -2,13 +2,22 @@ * Generic HDLC support routines for Linux * Frame Relay support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. - */ + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + + Theory of PVC state in DCE mode: + + (exist,new) -> 0,0 when "PVC create" or if "link unreliable" + 0,x -> 1,1 if "link reliable" when sending FULL STATUS + 1,1 -> 1,0 if received FULL STATUS ACK + + (active) -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create" + -> 1 when "PVC up" and (exist,new) = 1,0 +*/ #include #include @@ -20,19 +29,23 @@ #include #include #include +#include #include #include #include +#include #include __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) { - pvc_device *pvc=hdlc->state.fr.first_pvc; + pvc_device *pvc = hdlc->state.fr.first_pvc; - while (pvc) { - if (netdev_dlci(&pvc->netdev) == dlci) + while(pvc) { + if (pvc->dlci == dlci) return pvc; + if (pvc->dlci > dlci) + return NULL; /* the listed is sorted */ pvc = pvc->next; } @@ -40,18 +53,72 @@ } +__inline__ pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci) +{ + pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc; + + while(*pvc_p) { + if ((*pvc_p)->dlci == dlci) + return *pvc_p; + if ((*pvc_p)->dlci > dlci) + break; /* the listed is sorted */ + pvc_p = &(*pvc_p)->next; + } + + pvc = kmalloc(sizeof(pvc_device), GFP_KERNEL); + if (!pvc) + return NULL; + + memset(pvc, 0, sizeof(pvc_device)); + pvc->dlci = dlci; + pvc->master = hdlc; + pvc->next = *pvc_p; /* Put it in the chain */ + *pvc_p = pvc; + return pvc; +} + + +__inline__ int pvc_is_used(pvc_device *pvc) +{ + return pvc->main != NULL || pvc->ether != NULL; +} + + +__inline__ void delete_unused_pvcs(hdlc_device *hdlc) +{ + pvc_device **pvc_p = &hdlc->state.fr.first_pvc; + + while(*pvc_p) { + if (!pvc_is_used(*pvc_p)) { + pvc_device *pvc = *pvc_p; + *pvc_p = pvc->next; + kfree(pvc); + continue; + } + pvc_p = &(*pvc_p)->next; + } +} + -__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, - int *active, int *new) +__inline__ struct net_device** get_dev_p(pvc_device *pvc, int type) { - *new = (status[2] & 0x08); - *active = (!*new && (status[2] & 0x02)); + if (type == ARPHRD_ETHER) + return &pvc->ether; + else + return &pvc->main; +} + + +__inline__ u16 status_to_dlci(u8 *status, int *active, int *new) +{ + *new = (status[2] & 0x08) ? 1 : 0; + *active = (status[2] & 0x02) ? 1 : 0; return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); } -__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, +__inline__ void dlci_to_status(u16 dlci, u8 *status, int active, int new) { status[0] = (dlci>>4) & 0x3F; @@ -66,37 +133,50 @@ -static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, - u16 type, void *daddr, void *saddr, unsigned int len) +static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) { u16 head_len; + struct sk_buff *skb = *skb_p; - if (!daddr) - daddr = dev->broadcast; - -#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER - printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name); -#endif - - switch(type) { - case ETH_P_IP: + switch(skb->protocol) { + case __constant_ntohs(ETH_P_IP): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IP; break; - case ETH_P_IPV6: + case __constant_ntohs(ETH_P_IPV6): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IPV6; break; - case LMI_PROTO: + case __constant_ntohs(LMI_PROTO): head_len = 4; skb_push(skb, head_len); skb->data[3] = LMI_PROTO; break; + case __constant_ntohs(ETH_P_802_3): + head_len = 10; + if (skb_headroom(skb) < head_len) { + struct sk_buff *skb2 = skb_realloc_headroom(skb, + head_len); + if (!skb2) + return -ENOBUFS; + dev_kfree_skb(skb); + skb = *skb_p = skb2; + } + skb_push(skb, head_len); + skb->data[3] = FR_PAD; + skb->data[4] = NLPID_SNAP; + skb->data[5] = FR_PAD; + skb->data[6] = 0x80; + skb->data[7] = 0xC2; + skb->data[8] = 0x00; + skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */ + break; + default: head_len = 10; skb_push(skb, head_len); @@ -105,14 +185,12 @@ skb->data[5] = FR_PAD; skb->data[6] = FR_PAD; skb->data[7] = FR_PAD; - skb->data[8] = type>>8; - skb->data[9] = (u8)type; + *(u16*)(skb->data + 8) = skb->protocol; } - memcpy(skb->data, daddr, 2); + dlci_to_q922(skb->data, dlci); skb->data[2] = FR_UI; - - return head_len; + return 0; } @@ -124,13 +202,12 @@ if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) return -EIO; /* Master must be UP in order to activate PVC */ - if (pvc->master->state.fr.settings.lmi != LMI_NONE) - pvc->state.active = 0; - else - pvc->state.active = 1; + if (pvc->open_count++ == 0) { + if (pvc->master->state.fr.settings.lmi == LMI_NONE) + pvc->state.active = 1; - pvc->state.new = 0; - pvc->master->state.fr.changed = 1; + pvc->master->state.fr.dce_changed = 1; + } return 0; } @@ -139,38 +216,94 @@ static int pvc_close(struct net_device *dev) { pvc_device *pvc = dev_to_pvc(dev); - pvc->state.active = pvc->state.new = 0; - pvc->master->state.fr.changed = 1; + + if (--pvc->open_count == 0) { + if (pvc->master->state.fr.settings.lmi == LMI_NONE) + pvc->state.active = 0; + + if (pvc->master->state.fr.settings.dce) { + pvc->master->state.fr.dce_changed = 1; + pvc->state.active = 0; + } + } return 0; } -static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) +int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { pvc_device *pvc = dev_to_pvc(dev); + fr_proto_pvc_info info; - if (pvc->state.active) { - skb->dev = hdlc_to_dev(pvc->master); - pvc->stats.tx_bytes += skb->len; - pvc->stats.tx_packets++; - if (pvc->state.fecn) - pvc->stats.tx_compressed++; /* TX Congestion counter */ - dev_queue_xmit(skb); - } else { - pvc->stats.tx_dropped++; - dev_kfree_skb(skb); + if (ifr->ifr_settings.type == IF_GET_PROTO) { + if (dev->type == ARPHRD_ETHER) + ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC; + else + ifr->ifr_settings.type = IF_PROTO_FR_PVC; + + if (ifr->ifr_settings.size < sizeof(info)) { + /* data size wanted */ + ifr->ifr_settings.size = sizeof(info); + return -ENOBUFS; + } + + info.dlci = pvc->dlci; + memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ); + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info, + &info, sizeof(info))) + return -EFAULT; + return 0; } - return 0; + return -EINVAL; +} + + +__inline__ struct net_device_stats *pvc_get_stats(struct net_device *dev) +{ + return (struct net_device_stats *) + ((char *)dev + sizeof(struct net_device)); } -static struct net_device_stats *pvc_get_stats(struct net_device *dev) +static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) { pvc_device *pvc = dev_to_pvc(dev); - return &pvc->stats; + struct net_device_stats *stats = pvc_get_stats(dev); + + if (pvc->state.active) { + if (dev->type == ARPHRD_ETHER) { + int pad = ETH_ZLEN - skb->len; + if (pad > 0) { /* Pad the frame with zeros */ + int len = skb->len; + if (skb_tailroom(skb) < pad) + if (pskb_expand_head(skb, 0, pad, + GFP_ATOMIC)) { + stats->tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + skb_put(skb, pad); + memset(skb->data + len, 0, pad); + } + skb->protocol = __constant_htons(ETH_P_802_3); + } + if (!fr_hard_header(&skb, pvc->dlci)) { + stats->tx_bytes += skb->len; + stats->tx_packets++; + if (pvc->state.fecn) /* TX Congestion counter */ + stats->tx_compressed++; + skb->dev = hdlc_to_dev(pvc->master); + dev_queue_xmit(skb); + return 0; + } + } + + stats->tx_dropped++; + dev_kfree_skb(skb); + return 0; } @@ -187,9 +320,15 @@ static inline void fr_log_dlci_active(pvc_device *pvc) { - printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), - pvc->state.active ? "" : "in", - pvc->state.new ? " new" : ""); + printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n", + hdlc_to_name(pvc->master), + pvc->dlci, + pvc->main ? pvc->main->name : "", + pvc->main && pvc->ether ? " " : "", + pvc->ether ? pvc->ether->name : "", + pvc->state.new ? " new" : "", + !pvc->state.exist ? "deleted" : + pvc->state.active ? "active" : "inactive"); } @@ -213,8 +352,8 @@ int i = 0; if (hdlc->state.fr.settings.dce && fullrep) { - len += hdlc->state.fr.pvc_count * (2 + stat_len); - if (len > HDLC_MAX_MTU) { + len += hdlc->state.fr.dce_pvc_count * (2 + stat_len); + if (len > HDLC_MAX_MRU) { printk(KERN_WARNING "%s: Too many PVCs while sending " "LMI full report\n", hdlc_to_name(hdlc)); return; @@ -224,12 +363,13 @@ skb = dev_alloc_skb(len); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", - hdlc_to_name(hdlc)); + hdlc_to_name(hdlc)); return; } memset(skb->data, 0, len); skb_reserve(skb, 4); - fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); + skb->protocol = __constant_htons(LMI_PROTO); + fr_hard_header(&skb, LMI_DLCI); data = skb->tail; data[i++] = LMI_CALLREF; data[i++] = hdlc->state.fr.settings.dce @@ -253,16 +393,20 @@ ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; data[i++] = stat_len; - if (hdlc->state.fr.reliable && - (pvc->netdev.flags & IFF_UP) && - !pvc->state.active && - !pvc->state.new) { - pvc->state.new = 1; + /* LMI start/restart */ + if (hdlc->state.fr.reliable && !pvc->state.exist) { + pvc->state.exist = pvc->state.new = 1; + fr_log_dlci_active(pvc); + } + + /* ifconfig PVC up */ + if (pvc->open_count && !pvc->state.active && + pvc->state.exist && !pvc->state.new) { + pvc->state.active = 1; fr_log_dlci_active(pvc); } - dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), - data + i, + dlci_to_status(pvc->dlci, data + i, pvc->state.active, pvc->state.new); i += stat_len; pvc = pvc->next; @@ -272,6 +416,7 @@ skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); + skb->nh.raw = skb->data; dev_queue_xmit(skb); } @@ -312,10 +457,11 @@ if (reliable) { hdlc->state.fr.n391cnt = 0; /* Request full status */ - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; } else { while (pvc) { /* Deactivate all PVCs */ - pvc->state.new = pvc->state.active = 0; + pvc->state.exist = 0; + pvc->state.active = pvc->state.new = 0; pvc = pvc->next; } } @@ -346,7 +492,7 @@ { int stat_len; pvc_device *pvc; - int reptype = -1, error; + int reptype = -1, error, no_ram; u8 rxseq, txseq; int i; @@ -420,20 +566,18 @@ while (pvc) { if (pvc->state.new) { pvc->state.new = 0; - pvc->state.active = 1; - fr_log_dlci_active(pvc); /* Tell DTE that new PVC is now active */ - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; } pvc = pvc->next; } } - if (hdlc->state.fr.changed) { + if (hdlc->state.fr.dce_changed) { reptype = LMI_FULLREP; hdlc->state.fr.fullrep_sent = 1; - hdlc->state.fr.changed = 0; + hdlc->state.fr.dce_changed = 0; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); @@ -449,13 +593,14 @@ pvc = hdlc->state.fr.first_pvc; while (pvc) { - pvc->state.deleted = pvc->state.active; /* mark active PVCs */ + pvc->state.deleted = 1; pvc = pvc->next; } + no_ram = 0; while (skb->len >= i + 2 + stat_len) { u16 dlci; - int active, new; + unsigned int active, new; if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { @@ -472,21 +617,28 @@ } i++; - dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); - pvc = find_pvc(hdlc, dlci); + dlci = status_to_dlci(skb->data + i, &active, &new); + + pvc = add_pvc(hdlc, dlci); + + if (!pvc && !no_ram) { + printk(KERN_WARNING + "%s: Memory squeeze on fr_lmi_recv()\n", + hdlc_to_name(hdlc)); + no_ram = 1; + } - active |= new; if (pvc) { - if (active && !pvc->state.active && - (pvc->netdev.flags & IFF_UP)) { + pvc->state.exist = 1; + pvc->state.deleted = 0; + if (active != pvc->state.active || + new != pvc->state.new || + !pvc->state.exist) { + pvc->state.new = new; pvc->state.active = active; fr_log_dlci_active(pvc); } - pvc->state.deleted = 0; } - else if (new) - printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", - hdlc_to_name(hdlc), dlci); i += stat_len; } @@ -494,10 +646,10 @@ pvc = hdlc->state.fr.first_pvc; while (pvc) { - if (pvc->state.deleted) { + if (pvc->state.deleted && pvc->state.exist) { pvc->state.active = pvc->state.new = 0; + pvc->state.exist = 0; fr_log_dlci_active(pvc); - pvc->state.deleted = 0; } pvc = pvc->next; } @@ -517,8 +669,9 @@ u8 *data = skb->data; u16 dlci; pvc_device *pvc; + struct net_device *dev = NULL; - if (skb->len<4 || fh->ea1 || data[2] != FR_UI) + if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI) goto rx_error; dlci = q922_to_dlci(skb->data); @@ -550,57 +703,39 @@ printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", hdlc_to_name(hdlc), dlci); #endif - goto rx_error; - } - - if ((pvc->netdev.flags & IFF_UP) == 0) { -#ifdef CONFIG_HDLC_DEBUG_PKT - printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", - hdlc_to_name(hdlc), dlci); -#endif - goto rx_error; + dev_kfree_skb_any(skb); + return; } - pvc->stats.rx_packets++; /* PVC traffic */ - pvc->stats.rx_bytes += skb->len; - - if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) { + if (pvc->state.fecn != fh->fecn) { #ifdef CONFIG_HDLC_DEBUG_ECN - printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), - fh->fecn ? "N" : "FF"); + printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc), + dlci, fh->fecn ? "N" : "FF"); #endif pvc->state.fecn ^= 1; } - if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) { + if (pvc->state.becn != fh->becn) { #ifdef CONFIG_HDLC_DEBUG_ECN - printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), - fh->becn ? "N" : "FF"); + printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc), + dlci, fh->becn ? "N" : "FF"); #endif pvc->state.becn ^= 1; } - if (pvc->state.becn) - pvc->stats.rx_compressed++; - - skb->dev = &pvc->netdev; if (data[3] == NLPID_IP) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + dev = pvc->main; skb->protocol = htons(ETH_P_IP); - netif_rx(skb); - return; - } - - if (data[3] == NLPID_IPV6) { + } else if (data[3] == NLPID_IPV6) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ + dev = pvc->main; skb->protocol = htons(ETH_P_IPV6); - netif_rx(skb); - return; - } - if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { + } else if (skb->len > 10 && data[3] == FR_PAD && + data[4] == NLPID_SNAP && data[5] == FR_PAD) { u16 oui = ntohs(*(u16*)(data + 6)); u16 pid = ntohs(*(u16*)(data + 8)); skb_pull(skb, 10); @@ -610,23 +745,39 @@ case ETH_P_IPX: case ETH_P_IP: /* a long variant */ case ETH_P_IPV6: + dev = pvc->main; skb->protocol = htons(pid); break; + case 0x80C20007: /* bridged Ethernet frame */ + if ((dev = pvc->ether) != NULL) + skb->protocol = eth_type_trans(skb, dev); + break; + default: printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " "PID=%x\n", hdlc_to_name(hdlc), oui, pid); dev_kfree_skb_any(skb); return; } - - netif_rx(skb); + } else { + printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x " + "length = %i\n", hdlc_to_name(hdlc), data[3], skb->len); + dev_kfree_skb_any(skb); return; } - printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", - hdlc_to_name(hdlc), data[3]); - dev_kfree_skb_any(skb); + if (dev) { + struct net_device_stats *stats = pvc_get_stats(dev); + stats->rx_packets++; /* PVC traffic */ + stats->rx_bytes += skb->len; + if (pvc->state.becn) + stats->rx_compressed++; + skb->dev = dev; + netif_rx(skb); + } else + dev_kfree_skb_any(skb); + return; rx_error: @@ -641,7 +792,7 @@ if (hdlc->state.fr.settings.lmi != LMI_NONE) { hdlc->state.fr.last_poll = 0; hdlc->state.fr.reliable = 0; - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_changed = 1; hdlc->state.fr.request = 0; hdlc->state.fr.fullrep_sent = 0; hdlc->state.fr.last_errors = 0xFFFFFFFF; @@ -669,90 +820,119 @@ if (hdlc->state.fr.settings.lmi != LMI_NONE) del_timer_sync(&hdlc->state.fr.timer); - while(pvc) { - dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ + while(pvc) { /* Shutdown all PVCs for this FRAD */ + if (pvc->main) + dev_close(pvc->main); + if (pvc->ether) + dev_close(pvc->ether); + pvc->state.active = pvc->state.new = pvc->state.fecn = + pvc->state.becn = 0; + pvc->state.exist = 0; pvc = pvc->next; } } - -static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create) +static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type) { - pvc_device **pvc_p = &hdlc->state.fr.first_pvc; - pvc_device *pvc; - int result; + pvc_device *pvc = NULL; + struct net_device *dev; + int result, used; + char * prefix = "pvc%d"; - if(dlci <= 0 || dlci >= 1024) - return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ + if (type == ARPHRD_ETHER) + prefix = "pvceth%d"; - while(*pvc_p) { - if (netdev_dlci(&(*pvc_p)->netdev) == dlci) - break; - pvc_p = &(*pvc_p)->next; + if ((pvc = add_pvc(hdlc, dlci)) == NULL) { + printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n", + hdlc_to_name(hdlc)); + return -ENOBUFS; } - if (create) { /* Create PVC */ - if (*pvc_p != NULL) - return -EEXIST; - - pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); - if (!pvc) { - printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", - hdlc_to_name(hdlc)); - return -ENOBUFS; - } - memset(pvc, 0, sizeof(pvc_device)); + if (*get_dev_p(pvc, type)) + return -EEXIST; - pvc->netdev.hard_start_xmit = pvc_xmit; - pvc->netdev.get_stats = pvc_get_stats; - pvc->netdev.open = pvc_open; - pvc->netdev.stop = pvc_close; - pvc->netdev.change_mtu = pvc_change_mtu; - pvc->netdev.mtu = HDLC_MAX_MTU; - - pvc->netdev.type = ARPHRD_DLCI; - pvc->netdev.hard_header_len = 16; - pvc->netdev.hard_header = fr_hard_header; - pvc->netdev.tx_queue_len = 0; - pvc->netdev.flags = IFF_POINTOPOINT; - - pvc->master = hdlc; - *(u16*)pvc->netdev.dev_addr = htons(dlci); - dlci_to_q922(pvc->netdev.broadcast, dlci); - pvc->netdev.addr_len = 2; + used = pvc_is_used(pvc); - result = dev_alloc_name(&pvc->netdev, "pvc%d"); - if (result < 0) { - kfree(pvc); - *pvc_p = NULL; - return result; - } - - if (register_netdevice(&pvc->netdev) != 0) { - kfree(pvc); - *pvc_p = NULL; - return -EIO; - } + dev = kmalloc(sizeof(struct net_device) + + sizeof(struct net_device_stats), GFP_KERNEL); + if (!dev) { + printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", + hdlc_to_name(hdlc)); + delete_unused_pvcs(hdlc); + return -ENOBUFS; + } + memset(dev, 0, sizeof(struct net_device) + + sizeof(struct net_device_stats)); - hdlc->state.fr.changed = 1; - hdlc->state.fr.pvc_count++; - return 0; + if (type == ARPHRD_ETHER) { + ether_setup(dev); + memcpy(dev->dev_addr, "\x00\x01", 2); + get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); + } else { + dev->type = ARPHRD_DLCI; + dev->flags = IFF_POINTOPOINT; + dev->hard_header_len = 10; + dev->addr_len = 2; + *(u16*)dev->dev_addr = htons(dlci); + dlci_to_q922(dev->broadcast, dlci); + } + dev->hard_start_xmit = pvc_xmit; + dev->get_stats = pvc_get_stats; + dev->open = pvc_open; + dev->stop = pvc_close; + dev->do_ioctl = pvc_ioctl; + dev->change_mtu = pvc_change_mtu; + dev->mtu = HDLC_MAX_MTU; + dev->tx_queue_len = 0; + dev->priv = pvc; + + result = dev_alloc_name(dev, prefix); + if (result < 0) { + kfree(dev); + delete_unused_pvcs(hdlc); + return result; + } + + if (register_netdevice(dev) != 0) { + kfree(dev); + delete_unused_pvcs(hdlc); + return -EIO; + } + + *get_dev_p(pvc, type) = dev; + if (!used) { + hdlc->state.fr.dce_changed = 1; + hdlc->state.fr.dce_pvc_count++; } + return 0; +} + + - if (*pvc_p == NULL) /* Delete PVC */ +static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type) +{ + pvc_device *pvc; + struct net_device *dev; + + if ((pvc = find_pvc(hdlc, dlci)) == NULL) return -ENOENT; - pvc = *pvc_p; + if ((dev = *get_dev_p(pvc, type)) == NULL) + return -ENOENT; - if (pvc->netdev.flags & IFF_UP) + if (dev->flags & IFF_UP) return -EBUSY; /* PVC in use */ - hdlc->state.fr.changed = 1; - hdlc->state.fr.pvc_count--; - *pvc_p = pvc->next; - unregister_netdevice(&pvc->netdev); - kfree(pvc); + unregister_netdevice(dev); + kfree(dev); + *get_dev_p(pvc, type) = NULL; + + if (!pvc_is_used(pvc)) { + hdlc->state.fr.dce_pvc_count--; + hdlc->state.fr.dce_changed = 1; + } + delete_unused_pvcs(hdlc); return 0; } @@ -763,14 +943,21 @@ pvc_device *pvc = hdlc->state.fr.first_pvc; while(pvc) { pvc_device *next = pvc->next; - unregister_netdev(&pvc->netdev); + if (pvc->main) { + unregister_netdevice(pvc->main); + kfree(pvc->main); + } + if (pvc->ether) { + unregister_netdevice(pvc->ether); + kfree(pvc->ether); + } kfree(pvc); pvc = next; } hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ - hdlc->state.fr.pvc_count = 0; - hdlc->state.fr.changed = 1; + hdlc->state.fr.dce_pvc_count = 0; + hdlc->state.fr.dce_changed = 1; } @@ -828,25 +1015,27 @@ if (hdlc->proto != IF_PROTO_FR) { hdlc_proto_detach(hdlc); hdlc->state.fr.first_pvc = NULL; - hdlc->state.fr.pvc_count = 0; + hdlc->state.fr.dce_pvc_count = 0; } memcpy(&hdlc->state.fr.settings, &new_settings, size); hdlc->open = fr_open; hdlc->stop = fr_close; hdlc->netif_rx = fr_rx; + hdlc->type_trans = NULL; hdlc->proto_detach = fr_destroy; hdlc->proto = IF_PROTO_FR; dev->hard_start_xmit = hdlc->xmit; - dev->hard_header = fr_hard_header; + dev->hard_header = NULL; dev->type = ARPHRD_FRAD; - dev->addr_len = 2; - *(u16*)dev->dev_addr = htons(LMI_DLCI); - dlci_to_q922(dev->broadcast, LMI_DLCI); + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->addr_len = 0; return 0; case IF_PROTO_FR_ADD_PVC: case IF_PROTO_FR_DEL_PVC: + case IF_PROTO_FR_ADD_ETH_PVC: + case IF_PROTO_FR_DEL_ETH_PVC: if(!capable(CAP_NET_ADMIN)) return -EPERM; @@ -854,8 +1043,20 @@ sizeof(fr_proto_pvc))) return -EFAULT; - return fr_pvc(hdlc, pvc.dlci, - ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC); + if (pvc.dlci <= 0 || pvc.dlci >= 1024) + return -EINVAL; /* Only 10 bits, DLCI 0 reserved */ + + if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC || + ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC) + result = ARPHRD_ETHER; /* bridged Ethernet device */ + else + result = ARPHRD_DLCI; + + if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC || + ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC) + return fr_add_pvc(hdlc, pvc.dlci, result); + else + return fr_del_pvc(hdlc, pvc.dlci, result); } return -EINVAL; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_generic.c linux.21rc1-ac2/drivers/net/wan/hdlc_generic.c --- linux.21rc1/drivers/net/wan/hdlc_generic.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_generic.c 2003-04-22 16:44:37.000000000 +0100 @@ -1,17 +1,13 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. * - * Current status: - * - this is work in progress - * - not heavily tested on SMP - * - currently supported: + * Currently supported: * * raw IP-in-HDLC * * Cisco HDLC * * Frame Relay with ANSI or CCITT LMI (both user and network side) @@ -37,7 +33,7 @@ #include -static const char* version = "HDLC support module revision 1.11"; +static const char* version = "HDLC support module revision 1.12"; static int hdlc_change_mtu(struct net_device *dev, int new_mtu) @@ -60,7 +56,13 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p) { - dev_to_hdlc(dev)->netif_rx(skb); + hdlc_device *hdlc = dev_to_hdlc(dev); + if (hdlc->netif_rx) + hdlc->netif_rx(skb); + else { + hdlc->stats.rx_dropped++; /* Shouldn't happen */ + dev_kfree_skb(skb); + } return 0; } @@ -69,6 +71,10 @@ #define hdlc_raw_ioctl(hdlc, ifr) -ENOSYS #endif +#ifndef CONFIG_HDLC_RAW_ETH +#define hdlc_raw_eth_ioctl(hdlc, ifr) -ENOSYS +#endif + #ifndef CONFIG_HDLC_PPP #define hdlc_ppp_ioctl(hdlc, ifr) -ENOSYS #endif @@ -96,6 +102,7 @@ switch(ifr->ifr_settings.type) { case IF_PROTO_HDLC: + case IF_PROTO_HDLC_ETH: case IF_PROTO_PPP: case IF_PROTO_CISCO: case IF_PROTO_FR: @@ -109,6 +116,7 @@ switch(proto) { case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr); + case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(hdlc, ifr); case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr); case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr); case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_ppp.c linux.21rc1-ac2/drivers/net/wan/hdlc_ppp.c --- linux.21rc1/drivers/net/wan/hdlc_ppp.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_ppp.c 2003-04-22 16:44:37.000000000 +0100 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * Point-to-point protocol support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -68,10 +67,10 @@ -static void ppp_rx(struct sk_buff *skb) +static unsigned short ppp_type_trans(struct sk_buff *skb, + struct net_device *dev) { - skb->protocol = htons(ETH_P_WAN_PPP); - netif_rx(skb); + return __constant_htons(ETH_P_WAN_PPP); } @@ -103,7 +102,8 @@ hdlc->open = ppp_open; hdlc->stop = ppp_close; - hdlc->netif_rx = ppp_rx; + hdlc->netif_rx = NULL; + hdlc->type_trans = ppp_type_trans; hdlc->proto = IF_PROTO_PPP; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = NULL; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_raw.c linux.21rc1-ac2/drivers/net/wan/hdlc_raw.c --- linux.21rc1/drivers/net/wan/hdlc_raw.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_raw.c 2003-04-22 16:44:37.000000000 +0100 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * HDLC support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -26,10 +25,10 @@ #include -static void raw_rx(struct sk_buff *skb) +static unsigned short raw_type_trans(struct sk_buff *skb, + struct net_device *dev) { - skb->protocol = htons(ETH_P_IP); - netif_rx(skb); + return __constant_htons(ETH_P_IP); } @@ -67,7 +66,7 @@ new_settings.encoding = ENCODING_NRZ; if (new_settings.parity == PARITY_DEFAULT) - new_settings.parity = PARITY_NONE; + new_settings.parity = PARITY_CRC16_PR1_CCITT; result = hdlc->attach(hdlc, new_settings.encoding, new_settings.parity); @@ -79,11 +78,13 @@ hdlc->open = NULL; hdlc->stop = NULL; - hdlc->netif_rx = raw_rx; + hdlc->netif_rx = NULL; + hdlc->type_trans = raw_type_trans; hdlc->proto = IF_PROTO_HDLC; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = NULL; dev->type = ARPHRD_RAWHDLC; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->addr_len = 0; return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_raw_eth.c linux.21rc1-ac2/drivers/net/wan/hdlc_raw_eth.c --- linux.21rc1/drivers/net/wan/hdlc_raw_eth.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_raw_eth.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,110 @@ +/* + * Generic HDLC support routines for Linux + * HDLC Ethernet emulation support + * + * Copyright (C) 2002-2003 Krzysztof Halasa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int eth_tx(struct sk_buff *skb, struct net_device *dev) +{ + int pad = ETH_ZLEN - skb->len; + if (pad > 0) { /* Pad the frame with zeros */ + int len = skb->len; + if (skb_tailroom(skb) < pad) + if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { + dev_to_hdlc(dev)->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + skb_put(skb, pad); + memset(skb->data + len, 0, pad); + } + return dev_to_hdlc(dev)->xmit(skb, dev); +} + + +int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr) +{ + raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc; + const size_t size = sizeof(raw_hdlc_proto); + raw_hdlc_proto new_settings; + struct net_device *dev = hdlc_to_dev(hdlc); + int result; + void *old_ch_mtu; + int old_qlen; + + switch (ifr->ifr_settings.type) { + case IF_GET_PROTO: + ifr->ifr_settings.type = IF_PROTO_HDLC_ETH; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } + if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size)) + return -EFAULT; + return 0; + + case IF_PROTO_HDLC_ETH: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (dev->flags & IFF_UP) + return -EBUSY; + + if (copy_from_user(&new_settings, raw_s, size)) + return -EFAULT; + + if (new_settings.encoding == ENCODING_DEFAULT) + new_settings.encoding = ENCODING_NRZ; + + if (new_settings.parity == PARITY_DEFAULT) + new_settings.parity = PARITY_CRC16_PR1_CCITT; + + result = hdlc->attach(hdlc, new_settings.encoding, + new_settings.parity); + if (result) + return result; + + hdlc_proto_detach(hdlc); + memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size); + + hdlc->open = NULL; + hdlc->stop = NULL; + hdlc->netif_rx = NULL; + hdlc->type_trans = eth_type_trans; + hdlc->proto = IF_PROTO_HDLC_ETH; + dev->hard_start_xmit = eth_tx; + old_ch_mtu = dev->change_mtu; + old_qlen = dev->tx_queue_len; + ether_setup(dev); + dev->change_mtu = old_ch_mtu; + dev->tx_queue_len = old_qlen; + memcpy(dev->dev_addr, "\x00\x01", 2); + get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); + return 0; + } + + return -EINVAL; +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/hdlc_x25.c linux.21rc1-ac2/drivers/net/wan/hdlc_x25.c --- linux.21rc1/drivers/net/wan/hdlc_x25.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/hdlc_x25.c 2003-04-22 16:44:37.000000000 +0100 @@ -2,12 +2,11 @@ * Generic HDLC support routines for Linux * X.25 support * - * Copyright (C) 1999 - 2001 Krzysztof Halasa + * Copyright (C) 1999 - 2003 Krzysztof Halasa * * 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. + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. */ #include @@ -204,6 +203,7 @@ hdlc->open = x25_open; hdlc->stop = x25_close; hdlc->netif_rx = x25_rx; + hdlc->type_trans = NULL; hdlc->proto = IF_PROTO_X25; dev->hard_start_xmit = x25_xmit; dev->hard_header = NULL; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/Makefile linux.21rc1-ac2/drivers/net/wan/Makefile --- linux.21rc1/drivers/net/wan/Makefile 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -9,7 +9,7 @@ O_TARGET := wan.o -export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o +export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o pc300_drv.o list-multi = wanpipe.o cyclomx.o wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y) @@ -24,12 +24,15 @@ hdlc-y := hdlc_generic.o hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o +hdlc-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o hdlc-objs := $(hdlc-y) +pc300-$(CONFIG_PC300_MLPPP) += pc300_tty.o + obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o obj-$(CONFIG_COMX) += comx.o @@ -70,6 +73,7 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o +obj-$(CONFIG_PC300) += pc300.o obj-$(CONFIG_HDLC) += hdlc.o ifeq ($(CONFIG_HDLC_PPP),y) obj-$(CONFIG_HDLC) += syncppp.o @@ -79,6 +83,9 @@ include $(TOPDIR)/Rules.make +pc300.o: pc300_drv.o $(pc300-y) + $(LD) -r -o $@ pc300_drv.o $(pc300-y) + hdlc.o: $(hdlc-objs) $(LD) -r -o $@ $(hdlc-objs) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/pc300_drv.c linux.21rc1-ac2/drivers/net/wan/pc300_drv.c --- linux.21rc1/drivers/net/wan/pc300_drv.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/pc300_drv.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,3489 @@ +/* + * pc300.c Cyclades-PC300(tm) Driver. + * + * Author: Ivan Passos + * Maintainer: Henrique Gobbi + * + * Copyright: (c) 1999-2002 Cyclades 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. + * + */ + +#define USE_PCI_CLOCK + +static char rcsid[] = +"Revision: 3.4.8 Date: 2002/07/19 "; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pc300.h" + +#define CPC_LOCK(card,flags) \ + do { \ + spin_lock_irqsave(&card->card_lock, flags); \ + } while (0) + +#define CPC_UNLOCK(card,flags) \ + do { \ + spin_unlock_irqrestore(&card->card_lock, flags); \ + } while (0) + +#undef PC300_DEBUG_PCI +#undef PC300_DEBUG_INTR +#undef PC300_DEBUG_TX +#undef PC300_DEBUG_RX +#undef PC300_DEBUG_OTHER + +static struct pci_device_id cpc_pci_dev_id[] __devinitdata = { + /* PC300/RSV or PC300/X21, 2 chan */ + {0x120e, 0x300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x300}, + /* PC300/RSV or PC300/X21, 1 chan */ + {0x120e, 0x301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x301}, + /* PC300/TE, 2 chan */ + {0x120e, 0x310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x310}, + /* PC300/TE, 1 chan */ + {0x120e, 0x311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x311}, + /* PC300/TE-M, 2 chan */ + {0x120e, 0x320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x320}, + /* PC300/TE-M, 1 chan */ + {0x120e, 0x321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x321}, + /* End of table */ + {0,}, +}; +MODULE_DEVICE_TABLE(pci, cpc_pci_dev_id); + +#ifndef cpc_min +#define cpc_min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef cpc_max +#define cpc_max(a,b) (((a)>(b))?(a):(b)) +#endif + +/* prototypes */ +static void tx_dma_buf_pt_init(pc300_t *, int); +static void tx_dma_buf_init(pc300_t *, int); +static void rx_dma_buf_pt_init(pc300_t *, int); +static void rx_dma_buf_init(pc300_t *, int); +static void tx_dma_buf_check(pc300_t *, int); +static void rx_dma_buf_check(pc300_t *, int); +static void cpc_intr(int, void *, struct pt_regs *); +static struct net_device_stats *cpc_get_stats(struct net_device *); +static int clock_rate_calc(uclong, uclong, int *); +static uclong detect_ram(pc300_t *); +static void plx_init(pc300_t *); +static void cpc_trace(struct net_device *, struct sk_buff *, char); +static int cpc_attach(hdlc_device *, unsigned short, unsigned short); + +#ifdef CONFIG_PC300_MLPPP +void cpc_tty_init(pc300dev_t * dev); +void cpc_tty_unregister_service(pc300dev_t * pc300dev); +void cpc_tty_receive(pc300dev_t * pc300dev); +void cpc_tty_trigger_poll(pc300dev_t * pc300dev); +void cpc_tty_reset_var(void); +#endif + +/************************/ +/*** DMA Routines ***/ +/************************/ +static void tx_dma_buf_pt_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_TX_BUF; + volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { + cpc_writel(&ptdescr->next, (uclong) (DMA_TX_BD_BASE + + (ch_factor + ((i + 1) & (N_DMA_TX_BUF - 1))) * sizeof(pcsca_bd_t))); + cpc_writel(&ptdescr->ptbuf, + (uclong) (DMA_TX_BASE + (ch_factor + i) * BD_DEF_LEN)); + } +} + +static void tx_dma_buf_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_TX_BUF; + volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { + memset_io(ptdescr, 0, sizeof(pcsca_bd_t)); + cpc_writew(&ptdescr->len, 0); + cpc_writeb(&ptdescr->status, DST_OSB); + } + tx_dma_buf_pt_init(card, ch); +} + +static void rx_dma_buf_pt_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_RX_BUF; + volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { + cpc_writel(&ptdescr->next, (uclong) (DMA_RX_BD_BASE + + (ch_factor + ((i + 1) & (N_DMA_RX_BUF - 1))) * sizeof(pcsca_bd_t))); + cpc_writel(&ptdescr->ptbuf, + (uclong) (DMA_RX_BASE + (ch_factor + i) * BD_DEF_LEN)); + } +} + +static void rx_dma_buf_init(pc300_t * card, int ch) +{ + int i; + int ch_factor = ch * N_DMA_RX_BUF; + volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + + for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { + memset_io(ptdescr, 0, sizeof(pcsca_bd_t)); + cpc_writew(&ptdescr->len, 0); + cpc_writeb(&ptdescr->status, 0); + } + rx_dma_buf_pt_init(card, ch); +} + +static void tx_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t *ptdescr; + int i; + ucshort first_bd = card->chan[ch].tx_first_bd; + ucshort next_bd = card->chan[ch].tx_next_bd; + + printk("#CH%d: f_bd = %d(0x%08x), n_bd = %d(0x%08x)\n", ch, + first_bd, TX_BD_ADDR(ch, first_bd), + next_bd, TX_BD_ADDR(ch, next_bd)); + for (i = first_bd, + ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, first_bd)); + i != ((next_bd + 1) & (N_DMA_TX_BUF - 1)); + i = (i + 1) & (N_DMA_TX_BUF - 1), + ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, i))) { + printk("\n CH%d TX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d", + ch, i, (uclong) cpc_readl(&ptdescr->next), + (uclong) cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), cpc_readw(&ptdescr->len)); + } + printk("\n"); +} + +#ifdef PC300_DEBUG_OTHER +/* Show all TX buffer descriptors */ +static void tx1_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t *ptdescr; + int i; + ucshort first_bd = card->chan[ch].tx_first_bd; + ucshort next_bd = card->chan[ch].tx_next_bd; + uclong scabase = card->hw.scabase; + + printk ("\nnfree_tx_bd = %d \n", card->chan[ch].nfree_tx_bd); + printk("#CH%d: f_bd = %d(0x%08x), n_bd = %d(0x%08x)\n", ch, + first_bd, TX_BD_ADDR(ch, first_bd), + next_bd, TX_BD_ADDR(ch, next_bd)); + printk("TX_CDA=0x%08lx, TX_EDA=0x%08lx\n", + (uclong) cpc_readl(scabase + DTX_REG(CDAL, ch)), + (uclong) cpc_readl(scabase + DTX_REG(EDAL, ch))); + for (i = 0; i < N_DMA_TX_BUF; i++) { + ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, i)); + printk("\n CH%d TX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d", + ch, i, (uclong) cpc_readl(&ptdescr->next), + (uclong) cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), cpc_readw(&ptdescr->len)); + } + printk("\n"); +} +#endif + +static void rx_dma_buf_check(pc300_t * card, int ch) +{ + volatile pcsca_bd_t *ptdescr; + int i; + ucshort first_bd = card->chan[ch].rx_first_bd; + ucshort last_bd = card->chan[ch].rx_last_bd; + int ch_factor; + + ch_factor = ch * N_DMA_RX_BUF; + printk("#CH%d: f_bd = %d, l_bd = %d\n", ch, first_bd, last_bd); + for (i = 0, ptdescr = (pcsca_bd_t *) (card->hw.rambase + + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); + i < N_DMA_RX_BUF; i++, ptdescr++) { + if (cpc_readb(&ptdescr->status) & DST_OSB) + printk ("\n CH%d RX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d", + ch, i, (uclong) cpc_readl(&ptdescr->next), + (uclong) cpc_readl(&ptdescr->ptbuf), + cpc_readb(&ptdescr->status), + cpc_readw(&ptdescr->len)); + } + printk("\n"); +} + +int dma_get_rx_frame_size(pc300_t * card, int ch) +{ + volatile pcsca_bd_t *ptdescr; + ucshort first_bd = card->chan[ch].rx_first_bd; + int rcvd = 0; + volatile ucchar status; + + ptdescr = (pcsca_bd_t *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd)); + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + rcvd += cpc_readw(&ptdescr->len); + first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1); + if ((status & DST_EOM) || (first_bd == card->chan[ch].rx_last_bd)) { + /* Return the size of a good frame or incomplete bad frame + * (dma_buf_read will clean the buffer descriptors in this case). */ + return (rcvd); + } + ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next)); + } + return (-1); +} + +/* + * dma_buf_write: writes a frame to the Tx DMA buffers + * NOTE: this function writes one frame at a time. + */ +int dma_buf_write(pc300_t * card, int ch, ucchar * ptdata, int len) +{ + int i, nchar; + volatile pcsca_bd_t *ptdescr; + int tosend = len; + ucchar nbuf = ((len - 1) / BD_DEF_LEN) + 1; + + if (nbuf >= card->chan[ch].nfree_tx_bd) { + return -ENOMEM; + } + + for (i = 0; i < nbuf; i++) { + ptdescr = (pcsca_bd_t *) (card->hw.rambase + + TX_BD_ADDR(ch, card->chan[ch].tx_next_bd)); + nchar = cpc_min(BD_DEF_LEN, tosend); + if (cpc_readb(&ptdescr->status) & DST_OSB) { + memcpy_toio((void *)(card->hw.rambase + cpc_readl(&ptdescr->ptbuf)), + &ptdata[len - tosend], nchar); + cpc_writew(&ptdescr->len, nchar); + card->chan[ch].nfree_tx_bd--; + if ((i + 1) == nbuf) { + /* This must be the last BD to be used */ + cpc_writeb(&ptdescr->status, DST_EOM); + } else { + cpc_writeb(&ptdescr->status, 0); + } + } else { + return -ENOMEM; + } + tosend -= nchar; + card->chan[ch].tx_next_bd = + (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1); + } + /* If it gets to here, it means we have sent the whole frame */ + return 0; +} + +/* + * dma_buf_read: reads a frame from the Rx DMA buffers + * NOTE: this function reads one frame at a time. + */ +int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) +{ + int nchar; + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + volatile pcsca_bd_t *ptdescr; + int rcvd = 0; + volatile ucchar status; + + ptdescr = (pcsca_bd_t *) (card->hw.rambase + + RX_BD_ADDR(ch, chan->rx_first_bd)); + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + nchar = cpc_readw(&ptdescr->len); + if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) + || (nchar > BD_DEF_LEN)) { + + if (nchar > BD_DEF_LEN) + status |= DST_RBIT; + rcvd = -status; + /* Discard remaining descriptors used by the bad frame */ + while (chan->rx_first_bd != chan->rx_last_bd) { + cpc_writeb(&ptdescr->status, 0); + chan->rx_first_bd = (chan->rx_first_bd+1) & (N_DMA_RX_BUF-1); + if (status & DST_EOM) + break; + ptdescr = (pcsca_bd_t *) (card->hw.rambase + + cpc_readl(&ptdescr->next)); + status = cpc_readb(&ptdescr->status); + } + break; + } + if (nchar != 0) { + if (skb) { + memcpy_fromio(skb_put(skb, nchar), + (void *)(card->hw.rambase+cpc_readl(&ptdescr->ptbuf)),nchar); + } + rcvd += nchar; + } + cpc_writeb(&ptdescr->status, 0); + cpc_writeb(&ptdescr->len, 0); + chan->rx_first_bd = (chan->rx_first_bd + 1) & (N_DMA_RX_BUF - 1); + + if (status & DST_EOM) + break; + + ptdescr = (pcsca_bd_t *) (card->hw.rambase + cpc_readl(&ptdescr->next)); + } + + if (rcvd != 0) { + /* Update pointer */ + chan->rx_last_bd = (chan->rx_first_bd - 1) & (N_DMA_RX_BUF - 1); + /* Update EDA */ + cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, chan->rx_last_bd)); + } + return (rcvd); +} + +void tx_dma_stop(pc300_t * card, int ch) +{ + uclong scabase = card->hw.scabase; + ucchar drr_ena_bit = 1 << (5 + 2 * ch); + ucchar drr_rst_bit = 1 << (1 + 2 * ch); + + /* Disable DMA */ + cpc_writeb(scabase + DRR, drr_ena_bit); + cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit); +} + +void rx_dma_stop(pc300_t * card, int ch) +{ + uclong scabase = card->hw.scabase; + ucchar drr_ena_bit = 1 << (4 + 2 * ch); + ucchar drr_rst_bit = 1 << (2 * ch); + + /* Disable DMA */ + cpc_writeb(scabase + DRR, drr_ena_bit); + cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit); +} + +void rx_dma_start(pc300_t * card, int ch) +{ + uclong scabase = card->hw.scabase; + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + + /* Start DMA */ + cpc_writel(scabase + DRX_REG(CDAL, ch), + RX_BD_ADDR(ch, chan->rx_first_bd)); + if (cpc_readl(scabase + DRX_REG(CDAL,ch)) != + RX_BD_ADDR(ch, chan->rx_first_bd)) { + cpc_writel(scabase + DRX_REG(CDAL, ch), + RX_BD_ADDR(ch, chan->rx_first_bd)); + } + cpc_writel(scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, chan->rx_last_bd)); + cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN); + cpc_writeb(scabase + DSR_RX(ch), DSR_DE); + if (!(cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) { + cpc_writeb(scabase + DSR_RX(ch), DSR_DE); + } +} + +/*************************/ +/*** FALC Routines ***/ +/*************************/ +void falc_issue_cmd(pc300_t * card, int ch, ucchar cmd) +{ + uclong falcbase = card->hw.falcbase; + unsigned long i = 0; + + while (cpc_readb(falcbase + F_REG(SIS, ch)) & SIS_CEC) { + if (i++ >= PC300_FALC_MAXLOOP) { + printk("%s: FALC command locked(cmd=0x%x).\n", + card->chan[ch].d.name, cmd); + break; + } + } + cpc_writeb(falcbase + F_REG(CMDR, ch), cmd); +} + +void falc_intr_enable(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + /* Interrupt pins are open-drain */ + cpc_writeb(falcbase + F_REG(IPC, ch), + cpc_readb(falcbase + F_REG(IPC, ch)) & ~IPC_IC0); + /* Conters updated each second */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_ECM); + /* Enable SEC and ES interrupts */ + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) & ~(IMR3_SEC | IMR3_ES)); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(IMR4, ch), + cpc_readb(falcbase + F_REG(IMR4, ch)) & ~(IMR4_LOS)); + } else { + cpc_writeb(falcbase + F_REG(IMR4, ch), + cpc_readb(falcbase + F_REG(IMR4, ch)) & + ~(IMR4_LFA | IMR4_AIS | IMR4_LOS | IMR4_SLIP)); + } + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) & ~IMR3_LLBSC); + } else { + cpc_writeb(falcbase + F_REG(IPC, ch), + cpc_readb(falcbase + F_REG(IPC, ch)) | IPC_SCI); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & ~(IMR2_LOS)); + } else { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & + ~(IMR2_FAR | IMR2_LFA | IMR2_AIS | IMR2_LOS)); + if (pfalc->multiframe_mode) { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) & + ~(IMR2_T400MS | IMR2_MFAR)); + } else { + cpc_writeb(falcbase + F_REG(IMR2, ch), + cpc_readb(falcbase + F_REG(IMR2, ch)) | + IMR2_T400MS | IMR2_MFAR); + } + } + } +} + +void falc_open_timeslot(pc300_t * card, int ch, int timeslot) +{ + uclong falcbase = card->hw.falcbase; + ucchar tshf = card->chan[ch].falc.offset; + + cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), + cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch)) & + ~(0x80 >> ((timeslot - tshf) & 0x07))); + cpc_writeb(falcbase + F_REG((TTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((TTR1 + timeslot / 8), ch)) | + (0x80 >> (timeslot & 0x07))); + cpc_writeb(falcbase + F_REG((RTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((RTR1 + timeslot / 8), ch)) | + (0x80 >> (timeslot & 0x07))); +} + +void falc_close_timeslot(pc300_t * card, int ch, int timeslot) +{ + uclong falcbase = card->hw.falcbase; + ucchar tshf = card->chan[ch].falc.offset; + + cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), + cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch)) | + (0x80 >> ((timeslot - tshf) & 0x07))); + cpc_writeb(falcbase + F_REG((TTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((TTR1 + timeslot / 8), ch)) & + ~(0x80 >> (timeslot & 0x07))); + cpc_writeb(falcbase + F_REG((RTR1 + timeslot / 8), ch), + cpc_readb(falcbase + F_REG((RTR1 + timeslot / 8), ch)) & + ~(0x80 >> (timeslot & 0x07))); +} + +void falc_close_all_timeslots(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + uclong falcbase = card->hw.falcbase; + + cpc_writeb(falcbase + F_REG(ICB1, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR1, ch), 0); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0); + cpc_writeb(falcbase + F_REG(ICB2, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR2, ch), 0); + cpc_writeb(falcbase + F_REG(RTR2, ch), 0); + cpc_writeb(falcbase + F_REG(ICB3, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR3, ch), 0); + cpc_writeb(falcbase + F_REG(RTR3, ch), 0); + if (conf->media == IF_IFACE_E1) { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0); + } +} + +void falc_open_all_timeslots(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + uclong falcbase = card->hw.falcbase; + + cpc_writeb(falcbase + F_REG(ICB1, ch), 0); + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(TTR1, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0xff); + } else { + /* Timeslot 0 is never enabled */ + cpc_writeb(falcbase + F_REG(TTR1, ch), 0x7f); + cpc_writeb(falcbase + F_REG(RTR1, ch), 0x7f); + } + cpc_writeb(falcbase + F_REG(ICB2, ch), 0); + cpc_writeb(falcbase + F_REG(TTR2, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR2, ch), 0xff); + cpc_writeb(falcbase + F_REG(ICB3, ch), 0); + cpc_writeb(falcbase + F_REG(TTR3, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR3, ch), 0xff); + if (conf->media == IF_IFACE_E1) { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0xff); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0xff); + } else { + cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff); + cpc_writeb(falcbase + F_REG(TTR4, ch), 0x80); + cpc_writeb(falcbase + F_REG(RTR4, ch), 0x80); + } +} + +void falc_init_timeslot(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + int tslot; + + for (tslot = 0; tslot < pfalc->num_channels; tslot++) { + if (conf->tslot_bitmap & (1 << tslot)) { + // Channel enabled + falc_open_timeslot(card, ch, tslot + 1); + } else { + // Channel disabled + falc_close_timeslot(card, ch, tslot + 1); + } + } +} + +void falc_enable_comm(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + + if (pfalc->full_bandwidth) { + falc_open_all_timeslots(card, ch); + } else { + falc_init_timeslot(card, ch); + } + // CTS/DCD ON + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) & + ~((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2 * ch))); +} + +void falc_disable_comm(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + + if (pfalc->loop_active != 2) { + falc_close_all_timeslots(card, ch); + } + // CTS/DCD OFF + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) | + ((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2 * ch))); +} + +void falc_init_t1(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); + + /* Switch to T1 mode (PCM 24) */ + cpc_writeb(falcbase + F_REG(FMR1, ch), FMR1_PMOD); + + /* Wait 20 us for setup */ + udelay(20); + + /* Transmit Buffer Size (1 frame) */ + cpc_writeb(falcbase + F_REG(SIC1, ch), SIC1_XBS0); + + /* Clock mode */ + if (conf->phys_settings.clock_type == CLOCK_INT) { /* Master mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS); + } else { /* Slave mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS); + cpc_writeb(falcbase + F_REG(LOOP, ch), + cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_RTM); + } + + cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & + ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1)); + + switch (conf->lcode) { + case PC300_LC_AMI: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC1 | FMR0_RC1); + /* Clear Channel register to ON for all channels */ + cpc_writeb(falcbase + F_REG(CCB1, ch), 0xff); + cpc_writeb(falcbase + F_REG(CCB2, ch), 0xff); + cpc_writeb(falcbase + F_REG(CCB3, ch), 0xff); + break; + + case PC300_LC_B8ZS: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1); + break; + + case PC300_LC_NRZ: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | 0x00); + break; + } + + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_ELOS); + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~(LIM0_SCL1 | LIM0_SCL0)); + /* Set interface mode to 2 MBPS */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_IMOD); + + switch (conf->fr_mode) { + case PC300_FR_ESF: + pfalc->multiframe_mode = 0; + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_FM1); + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | + FMR1_CRC | FMR1_EDL); + cpc_writeb(falcbase + F_REG(XDL1, ch), 0); + cpc_writeb(falcbase + F_REG(XDL2, ch), 0); + cpc_writeb(falcbase + F_REG(XDL3, ch), 0); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & ~FMR0_SRAF); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2,ch)) | FMR2_MCSP | FMR2_SSP); + break; + + case PC300_FR_D4: + pfalc->multiframe_mode = 1; + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) & + ~(FMR4_FM1 | FMR4_FM0)); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | FMR0_SRAF); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_SSP); + break; + } + + /* Enable Automatic Resynchronization */ + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_AUTO); + + /* Transmit Automatic Remote Alarm */ + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA); + + /* Channel translation mode 1 : one to one */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_CTM); + + /* No signaling */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_SIGM); + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) & + ~(FMR5_EIBR | FMR5_SRS)); + cpc_writeb(falcbase + F_REG(CCR1, ch), 0); + + cpc_writeb(falcbase + F_REG(LIM1, ch), + cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RIL0 | LIM1_RIL1); + + switch (conf->lbo) { + /* Provides proper Line Build Out */ + case PC300_LBO_0_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x5a); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x8f); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_7_5_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0x40 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x11); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x02); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_15_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0x80 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x8e); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + case PC300_LBO_22_5_DB: + cpc_writeb(falcbase + F_REG(LIM2, ch), (0xc0 | LIM2_LOS1 | dja)); + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x09); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20); + break; + } + + /* Transmit Clock-Slot Offset */ + cpc_writeb(falcbase + F_REG(XC0, ch), + cpc_readb(falcbase + F_REG(XC0, ch)) | 0x01); + /* Transmit Time-slot Offset */ + cpc_writeb(falcbase + F_REG(XC1, ch), 0x3e); + /* Receive Clock-Slot offset */ + cpc_writeb(falcbase + F_REG(RC0, ch), 0x05); + /* Receive Time-slot offset */ + cpc_writeb(falcbase + F_REG(RC1, ch), 0x00); + + /* LOS Detection after 176 consecutive 0s */ + cpc_writeb(falcbase + F_REG(PCDR, ch), 0x0a); + /* LOS Recovery after 22 ones in the time window of PCD */ + cpc_writeb(falcbase + F_REG(PCRR, ch), 0x15); + + cpc_writeb(falcbase + F_REG(IDLE, ch), 0x7f); + + if (conf->fr_mode == PC300_FR_ESF_JAPAN) { + cpc_writeb(falcbase + F_REG(RC1, ch), + cpc_readb(falcbase + F_REG(RC1, ch)) | 0x80); + } + + falc_close_all_timeslots(card, ch); +} + +void falc_init_e1(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); + + /* Switch to E1 mode (PCM 30) */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_PMOD); + + /* Clock mode */ + if (conf->phys_settings.clock_type == CLOCK_INT) { /* Master mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS); + } else { /* Slave mode */ + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS); + } + cpc_writeb(falcbase + F_REG(LOOP, ch), + cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_SFM); + + cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI); + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) & + ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1)); + + switch (conf->lcode) { + case PC300_LC_AMI: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC1 | FMR0_RC1); + break; + + case PC300_LC_HDB3: + cpc_writeb(falcbase + F_REG(FMR0, ch), + cpc_readb(falcbase + F_REG(FMR0, ch)) | + FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1); + break; + + case PC300_LC_NRZ: + break; + } + + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~(LIM0_SCL1 | LIM0_SCL0)); + /* Set interface mode to 2 MBPS */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_IMOD); + + cpc_writeb(falcbase + F_REG(XPM0, ch), 0x18); + cpc_writeb(falcbase + F_REG(XPM1, ch), 0x03); + cpc_writeb(falcbase + F_REG(XPM2, ch), 0x00); + + switch (conf->fr_mode) { + case PC300_FR_MF_CRC4: + pfalc->multiframe_mode = 1; + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_XFS); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_RFS1); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_RFS0); + cpc_writeb(falcbase + F_REG(FMR3, ch), + cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_EXTIW); + + /* MultiFrame Resynchronization */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_MFCS); + + /* Automatic Loss of Multiframe > 914 CRC errors */ + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_ALMF); + + /* S1 and SI1/SI2 spare Bits set to 1 */ + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) & ~XSP_AXS); + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_EBP); + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_XS13 | XSP_XS15); + + /* Automatic Force Resynchronization */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_AFR); + + /* Transmit Automatic Remote Alarm */ + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA); + + /* Transmit Spare Bits for National Use (Y, Sn, Sa) */ + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) | + XSW_XY0 | XSW_XY1 | XSW_XY2 | XSW_XY3 | XSW_XY4); + break; + + case PC300_FR_MF_NON_CRC4: + case PC300_FR_D4: + pfalc->multiframe_mode = 0; + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_XFS); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & + ~(FMR2_RFS1 | FMR2_RFS0)); + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) | XSW_XSIS); + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_XSIF); + + /* Automatic Force Resynchronization */ + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_AFR); + + /* Transmit Automatic Remote Alarm */ + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA); + + /* Transmit Spare Bits for National Use (Y, Sn, Sa) */ + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) | + XSW_XY0 | XSW_XY1 | XSW_XY2 | XSW_XY3 | XSW_XY4); + break; + + case PC300_FR_UNFRAMED: + pfalc->multiframe_mode = 0; + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_XFS); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & + ~(FMR2_RFS1 | FMR2_RFS0)); + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_TT0); + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) & + ~(XSW_XTM|XSW_XY0|XSW_XY1|XSW_XY2|XSW_XY3|XSW_XY4)); + cpc_writeb(falcbase + F_REG(TSWM, ch), 0xff); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | + (FMR2_RTM | FMR2_DAIS)); + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_AXRA); + cpc_writeb(falcbase + F_REG(FMR1, ch), + cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_AFR); + pfalc->sync = 1; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) | + (CPLD_REG2_FALC_LED2 << (2 * ch))); + break; + } + + /* No signaling */ + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) & ~XSP_CASEN); + cpc_writeb(falcbase + F_REG(CCR1, ch), 0); + + cpc_writeb(falcbase + F_REG(LIM1, ch), + cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RIL0 | LIM1_RIL1); + cpc_writeb(falcbase + F_REG(LIM2, ch), (LIM2_LOS1 | dja)); + + /* Transmit Clock-Slot Offset */ + cpc_writeb(falcbase + F_REG(XC0, ch), + cpc_readb(falcbase + F_REG(XC0, ch)) | 0x01); + /* Transmit Time-slot Offset */ + cpc_writeb(falcbase + F_REG(XC1, ch), 0x3e); + /* Receive Clock-Slot offset */ + cpc_writeb(falcbase + F_REG(RC0, ch), 0x05); + /* Receive Time-slot offset */ + cpc_writeb(falcbase + F_REG(RC1, ch), 0x00); + + /* LOS Detection after 176 consecutive 0s */ + cpc_writeb(falcbase + F_REG(PCDR, ch), 0x0a); + /* LOS Recovery after 22 ones in the time window of PCD */ + cpc_writeb(falcbase + F_REG(PCRR, ch), 0x15); + + cpc_writeb(falcbase + F_REG(IDLE, ch), 0x7f); + + falc_close_all_timeslots(card, ch); +} + +void falc_init_hdlc(pc300_t * card, int ch) +{ + uclong falcbase = card->hw.falcbase; + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + + /* Enable transparent data transfer */ + if (conf->fr_mode == PC300_FR_UNFRAMED) { + cpc_writeb(falcbase + F_REG(MODE, ch), 0); + } else { + cpc_writeb(falcbase + F_REG(MODE, ch), + cpc_readb(falcbase + F_REG(MODE, ch)) | + (MODE_HRAC | MODE_MDS2)); + cpc_writeb(falcbase + F_REG(RAH2, ch), 0xff); + cpc_writeb(falcbase + F_REG(RAH1, ch), 0xff); + cpc_writeb(falcbase + F_REG(RAL2, ch), 0xff); + cpc_writeb(falcbase + F_REG(RAL1, ch), 0xff); + } + + /* Tx/Rx reset */ + falc_issue_cmd(card, ch, CMDR_RRES | CMDR_XRES | CMDR_SRES); + + /* Enable interrupt sources */ + falc_intr_enable(card, ch); +} + +void te_config(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucchar dummy; + unsigned long flags; + + memset(pfalc, 0, sizeof(falc_t)); + switch (conf->media) { + case IF_IFACE_T1: + pfalc->num_channels = NUM_OF_T1_CHANNELS; + pfalc->offset = 1; + break; + case IF_IFACE_E1: + pfalc->num_channels = NUM_OF_E1_CHANNELS; + pfalc->offset = 0; + break; + } + if (conf->tslot_bitmap == 0xffffffffUL) + pfalc->full_bandwidth = 1; + else + pfalc->full_bandwidth = 0; + + CPC_LOCK(card, flags); + /* Reset the FALC chip */ + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) | + (CPLD_REG1_FALC_RESET << (2 * ch))); + udelay(10000); + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) & + ~(CPLD_REG1_FALC_RESET << (2 * ch))); + + if (conf->media == IF_IFACE_T1) { + falc_init_t1(card, ch); + } else { + falc_init_e1(card, ch); + } + falc_init_hdlc(card, ch); + if (conf->rx_sens == PC300_RX_SENS_SH) { + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_EQON); + } else { + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_EQON); + } + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) | + ((CPLD_REG2_FALC_TX_CLK | CPLD_REG2_FALC_RX_CLK) << (2 * ch))); + + /* Clear all interrupt registers */ + dummy = cpc_readb(falcbase + F_REG(FISR0, ch)) + + cpc_readb(falcbase + F_REG(FISR1, ch)) + + cpc_readb(falcbase + F_REG(FISR2, ch)) + + cpc_readb(falcbase + F_REG(FISR3, ch)); + CPC_UNLOCK(card, flags); +} + +void falc_check_status(pc300_t * card, int ch, unsigned char frs0) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + /* Verify LOS */ + if (frs0 & FRS0_LOS) { + if (!pfalc->red_alarm) { + pfalc->red_alarm = 1; + pfalc->los++; + if (!pfalc->blue_alarm) { + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise interfere + * with other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) + | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + } + } + } else { + if (pfalc->red_alarm) { + pfalc->red_alarm = 0; + pfalc->losr++; + } + } + + if (conf->fr_mode != PC300_FR_UNFRAMED) { + /* Verify AIS alarm */ + if (frs0 & FRS0_AIS) { + if (!pfalc->blue_alarm) { + pfalc->blue_alarm = 1; + pfalc->ais++; + // EVENT_AIS + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise interfere with other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_AIS + } + } else { + pfalc->blue_alarm = 0; + } + + /* Verify LFA */ + if (frs0 & FRS0_LFA) { + if (!pfalc->loss_fa) { + pfalc->loss_fa = 1; + pfalc->lfa++; + if (!pfalc->blue_alarm && !pfalc->red_alarm) { + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise + * interfere with other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) + | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + } + } + } else { + if (pfalc->loss_fa) { + pfalc->loss_fa = 0; + pfalc->farec++; + } + } + + /* Verify LMFA */ + if (pfalc->multiframe_mode && (frs0 & FRS0_LMFA)) { + /* D4 or CRC4 frame mode */ + if (!pfalc->loss_mfa) { + pfalc->loss_mfa = 1; + pfalc->lmfa++; + if (!pfalc->blue_alarm && !pfalc->red_alarm && + !pfalc->loss_fa) { + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise + * interfere with other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) + | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + } + } + } else { + pfalc->loss_mfa = 0; + } + + /* Verify Remote Alarm */ + if (frs0 & FRS0_RRA) { + if (!pfalc->yellow_alarm) { + pfalc->yellow_alarm = 1; + pfalc->rai++; + if (pfalc->sync) { + // EVENT_RAI + falc_disable_comm(card, ch); + // EVENT_RAI + } + } + } else { + pfalc->yellow_alarm = 0; + } + } /* if !PC300_UNFRAMED */ + + if (pfalc->red_alarm || pfalc->loss_fa || + pfalc->loss_mfa || pfalc->blue_alarm) { + if (pfalc->sync) { + pfalc->sync = 0; + chan->d.line_off++; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED2 << (2 * ch))); + } + } else { + if (!pfalc->sync) { + pfalc->sync = 1; + chan->d.line_on++; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) | + (CPLD_REG2_FALC_LED2 << (2 * ch))); + } + } + + if (pfalc->sync && !pfalc->yellow_alarm) { + if (!pfalc->active) { + // EVENT_FALC_NORMAL + if (pfalc->loop_active) { + return; + } + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) & ~IMR0_PDEN); + } + falc_enable_comm(card, ch); + // EVENT_FALC_NORMAL + pfalc->active = 1; + } + } else { + if (pfalc->active) { + pfalc->active = 0; + } + } +} + +void falc_update_stats(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucshort counter; + + counter = cpc_readb(falcbase + F_REG(FECL, ch)); + counter |= cpc_readb(falcbase + F_REG(FECH, ch)) << 8; + pfalc->fec += counter; + + counter = cpc_readb(falcbase + F_REG(CVCL, ch)); + counter |= cpc_readb(falcbase + F_REG(CVCH, ch)) << 8; + pfalc->cvc += counter; + + counter = cpc_readb(falcbase + F_REG(CECL, ch)); + counter |= cpc_readb(falcbase + F_REG(CECH, ch)) << 8; + pfalc->cec += counter; + + counter = cpc_readb(falcbase + F_REG(EBCL, ch)); + counter |= cpc_readb(falcbase + F_REG(EBCH, ch)) << 8; + pfalc->ebc += counter; + + if (cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) { + mdelay(10); + counter = cpc_readb(falcbase + F_REG(BECL, ch)); + counter |= cpc_readb(falcbase + F_REG(BECH, ch)) << 8; + pfalc->bec += counter; + + if (((conf->media == IF_IFACE_T1) && + (cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_LLBAD) && + (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN))) + || + ((conf->media == IF_IFACE_E1) && + (cpc_readb(falcbase + F_REG(RSP, ch)) & RSP_LLBAD))) { + pfalc->prbs = 2; + } else { + pfalc->prbs = 1; + } + } +} + +/*---------------------------------------------------------------------------- + * falc_remote_loop + *---------------------------------------------------------------------------- + * Description: In the remote loopback mode the clock and data recovered + * from the line inputs RL1/2 or RDIP/RDIN are routed back + * to the line outputs XL1/2 or XDOP/XDON via the analog + * transmitter. As in normal mode they are processsed by + * the synchronizer and then sent to the system interface. + *---------------------------------------------------------------------------- + */ +void falc_remote_loop(pc300_t * card, int ch, int loop_on) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (loop_on) { + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise interfere with + * other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + cpc_writeb(falcbase + F_REG(LIM1, ch), + cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RL); + pfalc->loop_active = 1; + } else { + cpc_writeb(falcbase + F_REG(LIM1, ch), + cpc_readb(falcbase + F_REG(LIM1, ch)) & ~LIM1_RL); + pfalc->sync = 0; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED2 << (2 * ch))); + pfalc->active = 0; + falc_issue_cmd(card, ch, CMDR_XRES); + pfalc->loop_active = 0; + } +} + +/*---------------------------------------------------------------------------- + * falc_local_loop + *---------------------------------------------------------------------------- + * Description: The local loopback mode disconnects the receive lines + * RL1/RL2 resp. RDIP/RDIN from the receiver. Instead of the + * signals coming from the line the data provided by system + * interface are routed through the analog receiver back to + * the system interface. The unipolar bit stream will be + * undisturbed transmitted on the line. Receiver and transmitter + * coding must be identical. + *---------------------------------------------------------------------------- + */ +void falc_local_loop(pc300_t * card, int ch, int loop_on) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (loop_on) { + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_LL); + pfalc->loop_active = 1; + } else { + cpc_writeb(falcbase + F_REG(LIM0, ch), + cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_LL); + pfalc->loop_active = 0; + } +} + +/*---------------------------------------------------------------------------- + * falc_payload_loop + *---------------------------------------------------------------------------- + * Description: This routine allows to enable/disable payload loopback. + * When the payload loop is activated, the received 192 bits + * of payload data will be looped back to the transmit + * direction. The framing bits, CRC6 and DL bits are not + * looped. They are originated by the FALC-LH transmitter. + *---------------------------------------------------------------------------- + */ +void falc_payload_loop(pc300_t * card, int ch, int loop_on) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (loop_on) { + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise interfere with + * other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_PLB); + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_TM); + } else { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) | XSP_TT0); + } + falc_open_all_timeslots(card, ch); + pfalc->loop_active = 2; + } else { + cpc_writeb(falcbase + F_REG(FMR2, ch), + cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_PLB); + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR4, ch), + cpc_readb(falcbase + F_REG(FMR4, ch)) & ~FMR4_TM); + } else { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) & ~XSP_TT0); + } + pfalc->sync = 0; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED2 << (2 * ch))); + pfalc->active = 0; + falc_issue_cmd(card, ch, CMDR_XRES); + pfalc->loop_active = 0; + } +} + +/*---------------------------------------------------------------------------- + * turn_off_xlu + *---------------------------------------------------------------------------- + * Description: Turns XLU bit off in the proper register + *---------------------------------------------------------------------------- + */ +void turn_off_xlu(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + uclong falcbase = card->hw.falcbase; + + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) & ~FMR5_XLU); + } else { + cpc_writeb(falcbase + F_REG(FMR3, ch), + cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_XLU); + } +} + +/*---------------------------------------------------------------------------- + * turn_off_xld + *---------------------------------------------------------------------------- + * Description: Turns XLD bit off in the proper register + *---------------------------------------------------------------------------- + */ +void turn_off_xld(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + uclong falcbase = card->hw.falcbase; + + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) & ~FMR5_XLD); + } else { + cpc_writeb(falcbase + F_REG(FMR3, ch), + cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_XLD); + } +} + +/*---------------------------------------------------------------------------- + * falc_generate_loop_up_code + *---------------------------------------------------------------------------- + * Description: This routine writes the proper FALC chip register in order + * to generate a LOOP activation code over a T1/E1 line. + *---------------------------------------------------------------------------- + */ +void falc_generate_loop_up_code(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) | FMR5_XLU); + } else { + cpc_writeb(falcbase + F_REG(FMR3, ch), + cpc_readb(falcbase + F_REG(FMR3, ch)) | FMR3_XLU); + } + // EVENT_FALC_ABNORMAL + if (conf->media == IF_IFACE_T1) { + /* Disable this interrupt as it may otherwise interfere with + * other working boards. */ + cpc_writeb(falcbase + F_REG(IMR0, ch), + cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN); + } + falc_disable_comm(card, ch); + // EVENT_FALC_ABNORMAL + pfalc->loop_gen = 1; +} + +/*---------------------------------------------------------------------------- + * falc_generate_loop_down_code + *---------------------------------------------------------------------------- + * Description: This routine writes the proper FALC chip register in order + * to generate a LOOP deactivation code over a T1/E1 line. + *---------------------------------------------------------------------------- + */ +void falc_generate_loop_down_code(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (conf->media == IF_IFACE_T1) { + cpc_writeb(falcbase + F_REG(FMR5, ch), + cpc_readb(falcbase + F_REG(FMR5, ch)) | FMR5_XLD); + } else { + cpc_writeb(falcbase + F_REG(FMR3, ch), + cpc_readb(falcbase + F_REG(FMR3, ch)) | FMR3_XLD); + } + pfalc->sync = 0; + cpc_writeb(falcbase + card->hw.cpld_reg2, + cpc_readb(falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED2 << (2 * ch))); + pfalc->active = 0; +//? falc_issue_cmd(card, ch, CMDR_XRES); + pfalc->loop_gen = 0; +} + +/*---------------------------------------------------------------------------- + * falc_pattern_test + *---------------------------------------------------------------------------- + * Description: This routine generates a pattern code and checks + * it on the reception side. + *---------------------------------------------------------------------------- + */ +void falc_pattern_test(pc300_t * card, int ch, unsigned int activate) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (activate) { + pfalc->prbs = 1; + pfalc->bec = 0; + if (conf->media == IF_IFACE_T1) { + /* Disable local loop activation/deactivation detect */ + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) | IMR3_LLBSC); + } else { + /* Disable local loop activation/deactivation detect */ + cpc_writeb(falcbase + F_REG(IMR1, ch), + cpc_readb(falcbase + F_REG(IMR1, ch)) | IMR1_LLBSC); + } + /* Activates generation and monitoring of PRBS + * (Pseudo Random Bit Sequence) */ + cpc_writeb(falcbase + F_REG(LCR1, ch), + cpc_readb(falcbase + F_REG(LCR1, ch)) | LCR1_EPRM | LCR1_XPRBS); + } else { + pfalc->prbs = 0; + /* Deactivates generation and monitoring of PRBS + * (Pseudo Random Bit Sequence) */ + cpc_writeb(falcbase + F_REG(LCR1, ch), + cpc_readb(falcbase+F_REG(LCR1,ch)) & ~(LCR1_EPRM | LCR1_XPRBS)); + if (conf->media == IF_IFACE_T1) { + /* Enable local loop activation/deactivation detect */ + cpc_writeb(falcbase + F_REG(IMR3, ch), + cpc_readb(falcbase + F_REG(IMR3, ch)) & ~IMR3_LLBSC); + } else { + /* Enable local loop activation/deactivation detect */ + cpc_writeb(falcbase + F_REG(IMR1, ch), + cpc_readb(falcbase + F_REG(IMR1, ch)) & ~IMR1_LLBSC); + } + } +} + +/*---------------------------------------------------------------------------- + * falc_pattern_test_error + *---------------------------------------------------------------------------- + * Description: This routine returns the bit error counter value + *---------------------------------------------------------------------------- + */ +ucshort falc_pattern_test_error(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + + return (pfalc->bec); +} + +/**********************************/ +/*** Net Interface Routines ***/ +/**********************************/ + +static void +cpc_trace(struct net_device *dev, struct sk_buff *skb_main, char rx_tx) +{ + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(10 + skb_main->len)) == NULL) { + printk("%s: out of memory\n", dev->name); + return; + } + skb_put(skb, 10 + skb_main->len); + + skb->dev = dev; + skb->protocol = htons(ETH_P_CUST); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + skb->len = 10 + skb_main->len; + + memcpy(skb->data, dev->name, 5); + skb->data[5] = '['; + skb->data[6] = rx_tx; + skb->data[7] = ']'; + skb->data[8] = ':'; + skb->data[9] = ' '; + memcpy(&skb->data[10], skb_main->data, skb_main->len); + + netif_rx(skb); +} + +void cpc_tx_timeout(struct net_device *dev) +{ + pc300dev_t *d = (pc300dev_t *) dev->priv; + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + struct net_device_stats *stats = &d->hdlc->stats; + int ch = chan->channel; + uclong flags; + ucchar ilar; + + stats->tx_errors++; + stats->tx_aborted_errors++; + CPC_LOCK(card, flags); + if ((ilar = cpc_readb(card->hw.scabase + ILAR)) != 0) { + printk("%s: ILAR=0x%x\n", dev->name, ilar); + cpc_writeb(card->hw.scabase + ILAR, ilar); + cpc_writeb(card->hw.scabase + DMER, 0x80); + } + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED1 << (2 * ch))); + } + dev->trans_start = jiffies; + CPC_UNLOCK(card, flags); + netif_wake_queue(dev); +} + +int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) +{ + pc300dev_t *d = (pc300dev_t *) dev->priv; + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + struct net_device_stats *stats = &d->hdlc->stats; + int ch = chan->channel; + uclong flags; +#ifdef PC300_DEBUG_TX + int i; +#endif + + if (chan->conf.monitor) { + /* In monitor mode no Tx is done: ignore packet */ + dev_kfree_skb(skb); + return 0; + } else if (!netif_carrier_ok(dev)) { + /* DCD must be OFF: drop packet */ + dev_kfree_skb(skb); + stats->tx_errors++; + stats->tx_carrier_errors++; + return 0; + } else if (cpc_readb(card->hw.scabase + M_REG(ST3, ch)) & ST3_DCD) { + printk("%s: DCD is OFF. Going administrative down.\n", dev->name); + stats->tx_errors++; + stats->tx_carrier_errors++; + dev_kfree_skb(skb); + netif_carrier_off(dev); + CPC_LOCK(card, flags); + cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR); + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED1 << (2 * ch))); + } + CPC_UNLOCK(card, flags); + netif_wake_queue(dev); + return 0; + } + + /* Write buffer to DMA buffers */ + if (dma_buf_write(card, ch, (ucchar *) skb->data, skb->len) != 0) { +// printk("%s: write error. Dropping TX packet.\n", dev->name); + netif_stop_queue(dev); + dev_kfree_skb(skb); + stats->tx_errors++; + stats->tx_dropped++; + return 0; + } +#ifdef PC300_DEBUG_TX + printk("%s T:", dev->name); + for (i = 0; i < skb->len; i++) + printk(" %02x", *(skb->data + i)); + printk("\n"); +#endif + + if (d->trace_on) { + cpc_trace(dev, skb, 'T'); + } + dev->trans_start = jiffies; + + /* Start transmission */ + CPC_LOCK(card, flags); + /* verify if it has more than one free descriptor */ + if (card->chan[ch].nfree_tx_bd <= 1) { + /* don't have so stop the queue */ + netif_stop_queue(dev); + } + cpc_writel(card->hw.scabase + DTX_REG(EDAL, ch), + TX_BD_ADDR(ch, chan->tx_next_bd)); + cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_ENA); + cpc_writeb(card->hw.scabase + DSR_TX(ch), DSR_DE); + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) | + (CPLD_REG2_FALC_LED1 << (2 * ch))); + } + CPC_UNLOCK(card, flags); + dev_kfree_skb(skb); + + return 0; +} + +void cpc_net_rx(hdlc_device * hdlc) +{ + struct net_device *dev = hdlc_to_dev(hdlc); + pc300dev_t *d = (pc300dev_t *) dev->priv; + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + struct net_device_stats *stats = &d->hdlc->stats; + int ch = chan->channel; +#ifdef PC300_DEBUG_RX + int i; +#endif + int rxb; + struct sk_buff *skb; + + while (1) { + if ((rxb = dma_get_rx_frame_size(card, ch)) == -1) + return; + + if (!netif_carrier_ok(dev)) { + /* DCD must be OFF: drop packet */ + printk("%s : DCD is OFF - drop %d rx bytes\n", dev->name, rxb); + skb = NULL; + } else { + if (rxb > (dev->mtu + 40)) { /* add headers */ + printk("%s : MTU exceeded %d\n", dev->name, rxb); + skb = NULL; + } else { + skb = dev_alloc_skb(rxb); + if (skb == NULL) { + printk("%s: Memory squeeze!!\n", dev->name); + return; + } + skb->dev = dev; + } + } + + if (((rxb = dma_buf_read(card, ch, skb)) <= 0) || (skb == NULL)) { +#ifdef PC300_DEBUG_RX + printk("%s: rxb = %x\n", dev->name, rxb); +#endif + if ((skb == NULL) && (rxb > 0)) { + /* rxb > dev->mtu */ + stats->rx_errors++; + stats->rx_length_errors++; + continue; + } + + if (rxb < 0) { /* Invalid frame */ + rxb = -rxb; + if (rxb & DST_OVR) { + stats->rx_errors++; + stats->rx_fifo_errors++; + } + if (rxb & DST_CRC) { + stats->rx_errors++; + stats->rx_crc_errors++; + } + if (rxb & (DST_RBIT | DST_SHRT | DST_ABT)) { + stats->rx_errors++; + stats->rx_frame_errors++; + } + } + if (skb) { + dev_kfree_skb_irq(skb); + } + continue; + } + + stats->rx_bytes += rxb; + +#ifdef PC300_DEBUG_RX + printk("%s R:", dev->name); + for (i = 0; i < skb->len; i++) + printk(" %02x", *(skb->data + i)); + printk("\n"); +#endif + if (d->trace_on) { + cpc_trace(dev, skb, 'R'); + } + stats->rx_packets++; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_HDLC); + netif_rx(skb); + } +} + +/************************************/ +/*** PC300 Interrupt Routines ***/ +/************************************/ +static void sca_tx_intr(pc300dev_t *dev) +{ + pc300ch_t *chan = (pc300ch_t *)dev->chan; + pc300_t *card = (pc300_t *)chan->card; + int ch = chan->channel; + volatile pcsca_bd_t * ptdescr; + struct net_device_stats *stats = &dev->hdlc->stats; + + /* Clean up descriptors from previous transmission */ + ptdescr = (pcsca_bd_t *)(card->hw.rambase + + TX_BD_ADDR(ch,chan->tx_first_bd)); + while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != + TX_BD_ADDR(ch,chan->tx_first_bd)) && + (cpc_readb(&ptdescr->status) & DST_OSB)) { + stats->tx_packets++; + stats->tx_bytes += cpc_readw(&ptdescr->len); + cpc_writeb(&ptdescr->status, DST_OSB); + cpc_writew(&ptdescr->len, 0); + chan->nfree_tx_bd++; + chan->tx_first_bd = (chan->tx_first_bd + 1) & (N_DMA_TX_BUF - 1); + ptdescr = (pcsca_bd_t *)(card->hw.rambase + + TX_BD_ADDR(ch,chan->tx_first_bd)); + } + +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto == PC300_PROTO_MLPPP) { + cpc_tty_trigger_poll(dev); + } else { +#endif + /* Tell the upper layer we are ready to transmit more packets */ + netif_wake_queue((struct net_device*)dev->hdlc); +#ifdef CONFIG_PC300_MLPPP + } +#endif +} + +static void sca_intr(pc300_t * card) +{ + uclong scabase = card->hw.scabase; + volatile uclong status; + int ch; + int intr_count = 0; + unsigned char dsr_rx; + + while ((status = cpc_readl(scabase + ISR0)) != 0) { + for (ch = 0; ch < card->hw.nchan; ch++) { + pc300ch_t *chan = &card->chan[ch]; + pc300dev_t *d = &chan->d; + hdlc_device *hdlc = d->hdlc; + struct net_device *dev = hdlc_to_dev(hdlc); + + spin_lock(&card->card_lock); + + /**** Reception ****/ + if (status & IR0_DRX((IR0_DMIA | IR0_DMIB), ch)) { + ucchar drx_stat = cpc_readb(scabase + DSR_RX(ch)); + + /* Clear RX interrupts */ + cpc_writeb(scabase + DSR_RX(ch), drx_stat | DSR_DWE); + +#ifdef PC300_DEBUG_INTR + printk ("sca_intr: RX intr chan[%d] (st=0x%08lx, dsr=0x%02x)\n", + ch, status, drx_stat); +#endif + if (status & IR0_DRX(IR0_DMIA, ch)) { + if (drx_stat & DSR_BOF) { +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto == PC300_PROTO_MLPPP) { + /* verify if driver is TTY */ + if ((cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) { + rx_dma_stop(card, ch); + } + cpc_tty_receive(d); + rx_dma_start(card, ch); + } else +#endif + { + if ((cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) { + rx_dma_stop(card, ch); + } + cpc_net_rx(hdlc); + /* Discard invalid frames */ + hdlc->stats.rx_errors++; + hdlc->stats.rx_over_errors++; + chan->rx_first_bd = 0; + chan->rx_last_bd = N_DMA_RX_BUF - 1; + rx_dma_start(card, ch); + } + } + } + if (status & IR0_DRX(IR0_DMIB, ch)) { + if (drx_stat & DSR_EOM) { + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + + card->hw.cpld_reg2, + cpc_readb (card->hw.falcbase + + card->hw.cpld_reg2) | + (CPLD_REG2_FALC_LED1 << (2 * ch))); + } +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto == PC300_PROTO_MLPPP) { + /* verify if driver is TTY */ + cpc_tty_receive(d); + } else { + cpc_net_rx(hdlc); + } +#else + cpc_net_rx(hdlc); +#endif + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + + card->hw.cpld_reg2, + cpc_readb (card->hw.falcbase + + card->hw.cpld_reg2) & + ~ (CPLD_REG2_FALC_LED1 << (2 * ch))); + } + } + } + if (!(dsr_rx = cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) { + +printk("%s: RX intr chan[%d] (st=0x%08lx, dsr=0x%02x, dsr2=0x%02x)\n", + dev->name, ch, status, drx_stat, dsr_rx); + cpc_writeb(scabase + DSR_RX(ch), (dsr_rx | DSR_DE) & 0xfe); + } + } + + /**** Transmission ****/ + if (status & IR0_DTX((IR0_EFT | IR0_DMIA | IR0_DMIB), ch)) { + ucchar dtx_stat = cpc_readb(scabase + DSR_TX(ch)); + + /* Clear TX interrupts */ + cpc_writeb(scabase + DSR_TX(ch), dtx_stat | DSR_DWE); + +#ifdef PC300_DEBUG_INTR + printk ("sca_intr: TX intr chan[%d] (st=0x%08lx, dsr=0x%02x)\n", + ch, status, dtx_stat); +#endif + if (status & IR0_DTX(IR0_EFT, ch)) { + if (dtx_stat & DSR_UDRF) { + if (cpc_readb (scabase + M_REG(TBN, ch)) != 0) { + cpc_writeb(scabase + M_REG(CMD,ch), CMD_TX_BUF_CLR); + } + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb (card->hw.falcbase + + card->hw.cpld_reg2) & + ~ (CPLD_REG2_FALC_LED1 << (2 * ch))); + } + hdlc->stats.tx_errors++; + hdlc->stats.tx_fifo_errors++; + sca_tx_intr(d); + } + } + if (status & IR0_DTX(IR0_DMIA, ch)) { + if (dtx_stat & DSR_BOF) { + } + } + if (status & IR0_DTX(IR0_DMIB, ch)) { + if (dtx_stat & DSR_EOM) { + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb (card->hw.falcbase + + card->hw.cpld_reg2) & + ~ (CPLD_REG2_FALC_LED1 << (2 * ch))); + } + sca_tx_intr(d); + } + } + } + + /**** MSCI ****/ + if (status & IR0_M(IR0_RXINTA, ch)) { + ucchar st1 = cpc_readb(scabase + M_REG(ST1, ch)); + + /* Clear MSCI interrupts */ + cpc_writeb(scabase + M_REG(ST1, ch), st1); + +#ifdef PC300_DEBUG_INTR + printk("sca_intr: MSCI intr chan[%d] (st=0x%08lx, st1=0x%02x)\n", + ch, status, st1); +#endif + if (st1 & ST1_CDCD) { /* DCD changed */ + if (cpc_readb(scabase + M_REG(ST3, ch)) & ST3_DCD) { + printk ("%s: DCD is OFF. Going administrative down.\n", + dev->name); +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto != PC300_PROTO_MLPPP) { + netif_carrier_off(dev); + } +#else + netif_carrier_off(dev); + +#endif + card->chan[ch].d.line_off++; + } else { /* DCD = 1 */ + printk ("%s: DCD is ON. Going administrative up.\n", + dev->name); +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto != PC300_PROTO_MLPPP) + /* verify if driver is not TTY */ +#endif + netif_carrier_on(dev); + card->chan[ch].d.line_on++; + } + } + } + spin_unlock(&card->card_lock); + } + if (++intr_count == 10) + /* Too much work at this board. Force exit */ + break; + } +} + +static void falc_t1_loop_detection(pc300_t * card, int ch, ucchar frs1) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) && + !pfalc->loop_gen) { + if (frs1 & FRS1_LLBDD) { + // A Line Loop Back Deactivation signal detected + if (pfalc->loop_active) { + falc_remote_loop(card, ch, 0); + } + } else { + if ((frs1 & FRS1_LLBAD) && + ((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) == 0)) { + // A Line Loop Back Activation signal detected + if (!pfalc->loop_active) { + falc_remote_loop(card, ch, 1); + } + } + } + } +} + +static void falc_e1_loop_detection(pc300_t * card, int ch, ucchar rsp) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + + if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) && + !pfalc->loop_gen) { + if (rsp & RSP_LLBDD) { + // A Line Loop Back Deactivation signal detected + if (pfalc->loop_active) { + falc_remote_loop(card, ch, 0); + } + } else { + if ((rsp & RSP_LLBAD) && + ((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) == 0)) { + // A Line Loop Back Activation signal detected + if (!pfalc->loop_active) { + falc_remote_loop(card, ch, 1); + } + } + } + } +} + +static void falc_t1_intr(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucchar isr0, isr3, gis; + ucchar dummy; + + while ((gis = cpc_readb(falcbase + F_REG(GIS, ch))) != 0) { + if (gis & GIS_ISR0) { + isr0 = cpc_readb(falcbase + F_REG(FISR0, ch)); + if (isr0 & FISR0_PDEN) { + /* Read the bit to clear the situation */ + if (cpc_readb(falcbase + F_REG(FRS1, ch)) & + FRS1_PDEN) { + pfalc->pden++; + } + } + } + + if (gis & GIS_ISR1) { + dummy = cpc_readb(falcbase + F_REG(FISR1, ch)); + } + + if (gis & GIS_ISR2) { + dummy = cpc_readb(falcbase + F_REG(FISR2, ch)); + } + + if (gis & GIS_ISR3) { + isr3 = cpc_readb(falcbase + F_REG(FISR3, ch)); + if (isr3 & FISR3_SEC) { + pfalc->sec++; + falc_update_stats(card, ch); + falc_check_status(card, ch, + cpc_readb(falcbase + F_REG(FRS0, ch))); + } + if (isr3 & FISR3_ES) { + pfalc->es++; + } + if (isr3 & FISR3_LLBSC) { + falc_t1_loop_detection(card, ch, + cpc_readb(falcbase + F_REG(FRS1, ch))); + } + } + } +} + +static void falc_e1_intr(pc300_t * card, int ch) +{ + pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong falcbase = card->hw.falcbase; + ucchar isr1, isr2, isr3, gis, rsp; + ucchar dummy; + + while ((gis = cpc_readb(falcbase + F_REG(GIS, ch))) != 0) { + rsp = cpc_readb(falcbase + F_REG(RSP, ch)); + + if (gis & GIS_ISR0) { + dummy = cpc_readb(falcbase + F_REG(FISR0, ch)); + } + if (gis & GIS_ISR1) { + isr1 = cpc_readb(falcbase + F_REG(FISR1, ch)); + if (isr1 & FISR1_XMB) { + if ((pfalc->xmb_cause & 2) + && pfalc->multiframe_mode) { + if (cpc_readb (falcbase + F_REG(FRS0, ch)) & + (FRS0_LOS | FRS0_AIS | FRS0_LFA)) { + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) + & ~XSP_AXS); + } else { + cpc_writeb(falcbase + F_REG(XSP, ch), + cpc_readb(falcbase + F_REG(XSP, ch)) + | XSP_AXS); + } + } + pfalc->xmb_cause = 0; + cpc_writeb(falcbase + F_REG(IMR1, ch), + cpc_readb(falcbase + F_REG(IMR1, ch)) | IMR1_XMB); + } + if (isr1 & FISR1_LLBSC) { + falc_e1_loop_detection(card, ch, rsp); + } + } + if (gis & GIS_ISR2) { + isr2 = cpc_readb(falcbase + F_REG(FISR2, ch)); + if (isr2 & FISR2_T400MS) { + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) | XSW_XRA); + } + if (isr2 & FISR2_MFAR) { + cpc_writeb(falcbase + F_REG(XSW, ch), + cpc_readb(falcbase + F_REG(XSW, ch)) & ~XSW_XRA); + } + if (isr2 & (FISR2_FAR | FISR2_LFA | FISR2_AIS | FISR2_LOS)) { + pfalc->xmb_cause |= 2; + cpc_writeb(falcbase + F_REG(IMR1, ch), + cpc_readb(falcbase + F_REG(IMR1, ch)) & ~IMR1_XMB); + } + } + if (gis & GIS_ISR3) { + isr3 = cpc_readb(falcbase + F_REG(FISR3, ch)); + if (isr3 & FISR3_SEC) { + pfalc->sec++; + falc_update_stats(card, ch); + falc_check_status(card, ch, + cpc_readb(falcbase + F_REG(FRS0, ch))); + } + if (isr3 & FISR3_ES) { + pfalc->es++; + } + } + } +} + +static void falc_intr(pc300_t * card) +{ + int ch; + + for (ch = 0; ch < card->hw.nchan; ch++) { + pc300ch_t *chan = &card->chan[ch]; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + + if (conf->media == IF_IFACE_T1) { + falc_t1_intr(card, ch); + } else { + falc_e1_intr(card, ch); + } + } +} + +static void cpc_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + pc300_t *card; + volatile ucchar plx_status; + + if ((card = (pc300_t *) dev_id) == 0) { +#ifdef PC300_DEBUG_INTR + printk("cpc_intr: spurious intr %d\n", irq); +#endif + return; /* spurious intr */ + } + + if (card->hw.rambase == 0) { +#ifdef PC300_DEBUG_INTR + printk("cpc_intr: spurious intr2 %d\n", irq); +#endif + return; /* spurious intr */ + } + + switch (card->hw.type) { + case PC300_RSV: + case PC300_X21: + sca_intr(card); + break; + + case PC300_TE: + while ( (plx_status = (cpc_readb(card->hw.plxbase + card->hw.intctl_reg) & + (PLX_9050_LINT1_STATUS | PLX_9050_LINT2_STATUS))) != 0) { + if (plx_status & PLX_9050_LINT1_STATUS) { /* SCA Interrupt */ + sca_intr(card); + } + if (plx_status & PLX_9050_LINT2_STATUS) { /* FALC Interrupt */ + falc_intr(card); + } + } + break; + } +} + +void cpc_sca_status(pc300_t * card, int ch) +{ + ucchar ilar; + uclong scabase = card->hw.scabase; + uclong flags; + + tx_dma_buf_check(card, ch); + rx_dma_buf_check(card, ch); + ilar = cpc_readb(scabase + ILAR); + printk ("ILAR=0x%02x, WCRL=0x%02x, PCR=0x%02x, BTCR=0x%02x, BOLR=0x%02x\n", + ilar, cpc_readb(scabase + WCRL), cpc_readb(scabase + PCR), + cpc_readb(scabase + BTCR), cpc_readb(scabase + BOLR)); + printk("TX_CDA=0x%08lx, TX_EDA=0x%08lx\n", + (uclong) cpc_readl(scabase + DTX_REG(CDAL, ch)), + (uclong) cpc_readl(scabase + DTX_REG(EDAL, ch))); + printk("RX_CDA=0x%08lx, RX_EDA=0x%08lx, BFL=0x%04x\n", + (uclong) cpc_readl(scabase + DRX_REG(CDAL, ch)), + (uclong) cpc_readl(scabase + DRX_REG(EDAL, ch)), + cpc_readw(scabase + DRX_REG(BFLL, ch))); + printk("DMER=0x%02x, DSR_TX=0x%02x, DSR_RX=0x%02x\n", + cpc_readb(scabase + DMER), cpc_readb(scabase + DSR_TX(ch)), + cpc_readb(scabase + DSR_RX(ch))); + printk("DMR_TX=0x%02x, DMR_RX=0x%02x, DIR_TX=0x%02x, DIR_RX=0x%02x\n", + cpc_readb(scabase + DMR_TX(ch)), cpc_readb(scabase + DMR_RX(ch)), + cpc_readb(scabase + DIR_TX(ch)), + cpc_readb(scabase + DIR_RX(ch))); + printk("DCR_TX=0x%02x, DCR_RX=0x%02x, FCT_TX=0x%02x, FCT_RX=0x%02x\n", + cpc_readb(scabase + DCR_TX(ch)), cpc_readb(scabase + DCR_RX(ch)), + cpc_readb(scabase + FCT_TX(ch)), + cpc_readb(scabase + FCT_RX(ch))); + printk("MD0=0x%02x, MD1=0x%02x, MD2=0x%02x, MD3=0x%02x, IDL=0x%02x\n", + cpc_readb(scabase + M_REG(MD0, ch)), + cpc_readb(scabase + M_REG(MD1, ch)), + cpc_readb(scabase + M_REG(MD2, ch)), + cpc_readb(scabase + M_REG(MD3, ch)), + cpc_readb(scabase + M_REG(IDL, ch))); + printk("CMD=0x%02x, SA0=0x%02x, SA1=0x%02x, TFN=0x%02x, CTL=0x%02x\n", + cpc_readb(scabase + M_REG(CMD, ch)), + cpc_readb(scabase + M_REG(SA0, ch)), + cpc_readb(scabase + M_REG(SA1, ch)), + cpc_readb(scabase + M_REG(TFN, ch)), + cpc_readb(scabase + M_REG(CTL, ch))); + printk("ST0=0x%02x, ST1=0x%02x, ST2=0x%02x, ST3=0x%02x, ST4=0x%02x\n", + cpc_readb(scabase + M_REG(ST0, ch)), + cpc_readb(scabase + M_REG(ST1, ch)), + cpc_readb(scabase + M_REG(ST2, ch)), + cpc_readb(scabase + M_REG(ST3, ch)), + cpc_readb(scabase + M_REG(ST4, ch))); + printk ("CST0=0x%02x, CST1=0x%02x, CST2=0x%02x, CST3=0x%02x, FST=0x%02x\n", + cpc_readb(scabase + M_REG(CST0, ch)), + cpc_readb(scabase + M_REG(CST1, ch)), + cpc_readb(scabase + M_REG(CST2, ch)), + cpc_readb(scabase + M_REG(CST3, ch)), + cpc_readb(scabase + M_REG(FST, ch))); + printk("TRC0=0x%02x, TRC1=0x%02x, RRC=0x%02x, TBN=0x%02x, RBN=0x%02x\n", + cpc_readb(scabase + M_REG(TRC0, ch)), + cpc_readb(scabase + M_REG(TRC1, ch)), + cpc_readb(scabase + M_REG(RRC, ch)), + cpc_readb(scabase + M_REG(TBN, ch)), + cpc_readb(scabase + M_REG(RBN, ch))); + printk("TFS=0x%02x, TNR0=0x%02x, TNR1=0x%02x, RNR=0x%02x\n", + cpc_readb(scabase + M_REG(TFS, ch)), + cpc_readb(scabase + M_REG(TNR0, ch)), + cpc_readb(scabase + M_REG(TNR1, ch)), + cpc_readb(scabase + M_REG(RNR, ch))); + printk("TCR=0x%02x, RCR=0x%02x, TNR1=0x%02x, RNR=0x%02x\n", + cpc_readb(scabase + M_REG(TCR, ch)), + cpc_readb(scabase + M_REG(RCR, ch)), + cpc_readb(scabase + M_REG(TNR1, ch)), + cpc_readb(scabase + M_REG(RNR, ch))); + printk("TXS=0x%02x, RXS=0x%02x, EXS=0x%02x, TMCT=0x%02x, TMCR=0x%02x\n", + cpc_readb(scabase + M_REG(TXS, ch)), + cpc_readb(scabase + M_REG(RXS, ch)), + cpc_readb(scabase + M_REG(EXS, ch)), + cpc_readb(scabase + M_REG(TMCT, ch)), + cpc_readb(scabase + M_REG(TMCR, ch))); + printk("IE0=0x%02x, IE1=0x%02x, IE2=0x%02x, IE4=0x%02x, FIE=0x%02x\n", + cpc_readb(scabase + M_REG(IE0, ch)), + cpc_readb(scabase + M_REG(IE1, ch)), + cpc_readb(scabase + M_REG(IE2, ch)), + cpc_readb(scabase + M_REG(IE4, ch)), + cpc_readb(scabase + M_REG(FIE, ch))); + printk("IER0=0x%08lx\n", (uclong) cpc_readl(scabase + IER0)); + + if (ilar != 0) { + CPC_LOCK(card, flags); + cpc_writeb(scabase + ILAR, ilar); + cpc_writeb(scabase + DMER, 0x80); + CPC_UNLOCK(card, flags); + } +} + +void cpc_falc_status(pc300_t * card, int ch) +{ + pc300ch_t *chan = &card->chan[ch]; + falc_t *pfalc = (falc_t *) & chan->falc; + uclong flags; + + CPC_LOCK(card, flags); + printk("CH%d: %s %s %d channels\n", + ch, (pfalc->sync ? "SYNC" : ""), (pfalc->active ? "ACTIVE" : ""), + pfalc->num_channels); + + printk(" pden=%d, los=%d, losr=%d, lfa=%d, farec=%d\n", + pfalc->pden, pfalc->los, pfalc->losr, pfalc->lfa, pfalc->farec); + printk(" lmfa=%d, ais=%d, sec=%d, es=%d, rai=%d\n", + pfalc->lmfa, pfalc->ais, pfalc->sec, pfalc->es, pfalc->rai); + printk(" bec=%d, fec=%d, cvc=%d, cec=%d, ebc=%d\n", + pfalc->bec, pfalc->fec, pfalc->cvc, pfalc->cec, pfalc->ebc); + + printk("\n"); + printk(" STATUS: %s %s %s %s %s %s\n", + (pfalc->red_alarm ? "RED" : ""), + (pfalc->blue_alarm ? "BLU" : ""), + (pfalc->yellow_alarm ? "YEL" : ""), + (pfalc->loss_fa ? "LFA" : ""), + (pfalc->loss_mfa ? "LMF" : ""), (pfalc->prbs ? "PRB" : "")); + CPC_UNLOCK(card, flags); +} + +int cpc_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 128) || (new_mtu > PC300_DEF_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + pc300dev_t *d = (pc300dev_t *) dev->priv; + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + pc300conf_t conf_aux; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + int ch = chan->channel; + void *arg = (void *) ifr->ifr_data; + uclong scabase = card->hw.scabase; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case SIOCGPC300CONF: +#ifdef CONFIG_PC300_MLPPP + if (conf->proto != PC300_PROTO_MLPPP) { + conf->proto = hdlc->proto; + } +#else + conf->proto = hdlc->proto; +#endif + memcpy(&conf_aux.conf, conf, sizeof(pc300chconf_t)); + memcpy(&conf_aux.hw, &card->hw, sizeof(pc300hw_t)); + if (!arg || + copy_to_user(arg, &conf_aux, sizeof(pc300conf_t))) + return -EINVAL; + return 0; + case SIOCSPC300CONF: + if (!suser()) + return -EPERM; + if (!arg || + copy_from_user(&conf_aux.conf, arg, sizeof(pc300chconf_t))) + return -EINVAL; + if (card->hw.cpld_id < 0x02 && + conf_aux.conf.fr_mode == PC300_FR_UNFRAMED) { + /* CPLD_ID < 0x02 doesn't support Unframed E1 */ + return -EINVAL; + } +#ifdef CONFIG_PC300_MLPPP + if (conf_aux.conf.proto == PC300_PROTO_MLPPP) { + if (conf->proto != PC300_PROTO_MLPPP) { + memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t)); + cpc_tty_init(d); /* init TTY driver */ + } + } else { + if (conf_aux.conf.proto == 0xffff) { + if (conf->proto == PC300_PROTO_MLPPP){ + /* ifdown interface */ + cpc_close(dev); + } + } else { + memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t)); + hdlc->proto = conf->proto; + } + } +#else + memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t)); + hdlc->proto = conf->proto; +#endif + return 0; + case SIOCGPC300STATUS: + cpc_sca_status(card, ch); + return 0; + case SIOCGPC300FALCSTATUS: + cpc_falc_status(card, ch); + return 0; + + case SIOCGPC300UTILSTATS: + { + if (!arg) { /* clear statistics */ + memset(&hdlc->stats, 0, sizeof(struct net_device_stats)); + if (card->hw.type == PC300_TE) { + memset(&chan->falc, 0, sizeof(falc_t)); + } + } else { + pc300stats_t pc300stats; + + memset(&pc300stats, 0, sizeof(pc300stats_t)); + pc300stats.hw_type = card->hw.type; + pc300stats.line_on = card->chan[ch].d.line_on; + pc300stats.line_off = card->chan[ch].d.line_off; + memcpy(&pc300stats.gen_stats, &hdlc->stats, + sizeof(struct net_device_stats)); + if (card->hw.type == PC300_TE) + memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t)); + copy_to_user(arg, &pc300stats, sizeof(pc300stats_t)); + } + return 0; + } + + case SIOCGPC300UTILSTATUS: + { + struct pc300status pc300status; + + pc300status.hw_type = card->hw.type; + if (card->hw.type == PC300_TE) { + pc300status.te_status.sync = chan->falc.sync; + pc300status.te_status.red_alarm = chan->falc.red_alarm; + pc300status.te_status.blue_alarm = chan->falc.blue_alarm; + pc300status.te_status.loss_fa = chan->falc.loss_fa; + pc300status.te_status.yellow_alarm =chan->falc.yellow_alarm; + pc300status.te_status.loss_mfa = chan->falc.loss_mfa; + pc300status.te_status.prbs = chan->falc.prbs; + } else { + pc300status.gen_status.dcd = + !(cpc_readb (scabase + M_REG(ST3, ch)) & ST3_DCD); + pc300status.gen_status.cts = + !(cpc_readb (scabase + M_REG(ST3, ch)) & ST3_CTS); + pc300status.gen_status.rts = + !(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_RTS); + pc300status.gen_status.dtr = + !(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_DTR); + /* There is no DSR in HD64572 */ + } + if (!arg + || copy_to_user(arg, &pc300status, sizeof(pc300status_t))) + return -EINVAL; + return 0; + } + + case SIOCSPC300TRACE: + /* Sets/resets a trace_flag for the respective device */ + if (!arg || copy_from_user(&d->trace_on, arg,sizeof(unsigned char))) + return -EINVAL; + return 0; + + case SIOCSPC300LOOPBACK: + { + struct pc300loopback pc300loop; + + /* TE boards only */ + if (card->hw.type != PC300_TE) + return -EINVAL; + + if (!arg || + copy_from_user(&pc300loop, arg, sizeof(pc300loopback_t))) + return -EINVAL; + switch (pc300loop.loop_type) { + case PC300LOCLOOP: /* Turn the local loop on/off */ + falc_local_loop(card, ch, pc300loop.loop_on); + return 0; + + case PC300REMLOOP: /* Turn the remote loop on/off */ + falc_remote_loop(card, ch, pc300loop.loop_on); + return 0; + + case PC300PAYLOADLOOP: /* Turn the payload loop on/off */ + falc_payload_loop(card, ch, pc300loop.loop_on); + return 0; + + case PC300GENLOOPUP: /* Generate loop UP */ + if (pc300loop.loop_on) { + falc_generate_loop_up_code (card, ch); + } else { + turn_off_xlu(card, ch); + } + return 0; + + case PC300GENLOOPDOWN: /* Generate loop DOWN */ + if (pc300loop.loop_on) { + falc_generate_loop_down_code (card, ch); + } else { + turn_off_xld(card, ch); + } + return 0; + + default: + return -EINVAL; + } + } + + case SIOCSPC300PATTERNTEST: + /* Turn the pattern test on/off and show the errors counter */ + { + struct pc300patterntst pc300patrntst; + + /* TE boards only */ + if (card->hw.type != PC300_TE) + return -EINVAL; + + if (card->hw.cpld_id < 0x02) { + /* CPLD_ID < 0x02 doesn't support pattern test */ + return -EINVAL; + } + + if (!arg || + copy_from_user(&pc300patrntst,arg,sizeof(pc300patterntst_t))) + return -EINVAL; + if (pc300patrntst.patrntst_on == 2) { + if (chan->falc.prbs == 0) { + falc_pattern_test(card, ch, 1); + } + pc300patrntst.num_errors = + falc_pattern_test_error(card, ch); + if (!arg + || copy_to_user(arg, &pc300patrntst, + sizeof (pc300patterntst_t))) + return -EINVAL; + } else { + falc_pattern_test(card, ch, pc300patrntst.patrntst_on); + } + return 0; + } + + case SIOCWANDEV: + switch (ifr->ifr_settings.type) { + case IF_GET_IFACE: + { + const size_t size = sizeof(sync_serial_settings); + ifr->ifr_settings.type = conf->media; + if (ifr->ifr_settings.size == 0) { + return 0; //return interface type only + } + if (ifr->ifr_settings.size < size) { + return -ENOMEM; //small buffer + } + if (copy_to_user(& ifr->ifr_settings.ifs_ifsu, + &conf->phys_settings, size)) { + return -EFAULT; + } + ifr->ifr_settings.size = size; + return 0; + } + + case IF_IFACE_V35: + case IF_IFACE_V24: + case IF_IFACE_X21: + { + const size_t size = sizeof(sync_serial_settings); + + if (!capable(CAP_NET_ADMIN)) { + return -EPERM; + } + if (ifr->ifr_settings.size != size) { + return -ENOMEM; //incorrect data len + } + if (copy_from_user(&conf->phys_settings, + &ifr->ifr_settings.ifs_ifsu, size)) { + return -EFAULT; + } + if (conf->phys_settings.loopback) { + cpc_writeb(card->hw.scabase + M_REG(MD2, ch), + cpc_readb(card->hw.scabase + M_REG(MD2, ch)) | + MD2_LOOP_MIR); + } + conf->media = ifr->ifr_settings.type; + return 0; + } + + case IF_IFACE_T1: + case IF_IFACE_E1: + { + const size_t te_size = sizeof(te1_settings); + const size_t size = sizeof(sync_serial_settings); + + if (!capable(CAP_NET_ADMIN)) { + return -EPERM; + } + if (ifr->ifr_settings.size != te_size) { + return -ENOMEM; //incorrect data len + } + if (copy_from_user(&conf->phys_settings, + &ifr->ifr_settings.ifs_ifsu, size)) { + return -EFAULT; + }/* Ignoring HDLC slot_map for a while */ + if (conf->phys_settings.loopback) { + cpc_writeb(card->hw.scabase + M_REG(MD2, ch), + cpc_readb(card->hw.scabase + M_REG(MD2, ch)) | + MD2_LOOP_MIR); + } + conf->media = ifr->ifr_settings.type; + return 0; + } + + default: + return hdlc_ioctl(dev, ifr, cmd); + } + + default: + return hdlc_ioctl(dev, ifr, cmd); + } +} + +static struct net_device_stats *cpc_get_stats(struct net_device *dev) +{ + pc300dev_t *d = (pc300dev_t *) dev->priv; + + if (d) + return &d->hdlc->stats; + else + return NULL; +} + +static int clock_rate_calc(uclong rate, uclong clock, int *br_io) +{ + int br, tc; + int br_pwr, error; + + if (rate == 0) + return (0); + + for (br = 0, br_pwr = 1; br <= 9; br++, br_pwr <<= 1) { + if ((tc = clock / br_pwr / rate) <= 0xff) { + *br_io = br; + break; + } + } + + if (tc <= 0xff) { + error = ((rate - (clock / br_pwr / rate)) / rate) * 1000; + /* Errors bigger than +/- 1% won't be tolerated */ + if (error < -10 || error > 10) + return (-1); + else + return (tc); + } else { + return (-1); + } +} + +int ch_config(pc300dev_t * d) +{ + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; + pc300_t *card = (pc300_t *) chan->card; + uclong scabase = card->hw.scabase; + uclong plxbase = card->hw.plxbase; + int ch = chan->channel; + uclong clkrate = chan->conf.phys_settings.clock_rate; + uclong clktype = chan->conf.phys_settings.clock_type; + ucshort encoding = chan->conf.proto_settings.encoding; + ucshort parity = chan->conf.proto_settings.parity; + int tmc, br; + ucchar md0, md2; + + /* Reset the channel */ + cpc_writeb(scabase + M_REG(CMD, ch), CMD_CH_RST); + + /* Configure the SCA registers */ + switch (parity) { + case PARITY_NONE: + md0 = MD0_BIT_SYNC; + break; + case PARITY_CRC16_PR0: + md0 = MD0_CRC16_0|MD0_CRCC0|MD0_BIT_SYNC; + break; + case PARITY_CRC16_PR1: + md0 = MD0_CRC16_1|MD0_CRCC0|MD0_BIT_SYNC; + break; + case PARITY_CRC32_PR1_CCITT: + md0 = MD0_CRC32|MD0_CRCC0|MD0_BIT_SYNC; + break; + case PARITY_CRC16_PR1_CCITT: + default: + md0 = MD0_CRC_CCITT|MD0_CRCC0|MD0_BIT_SYNC; + break; + } + switch (encoding) { + case ENCODING_NRZI: + md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_NRZI; + break; + case ENCODING_FM_MARK: /* FM1 */ + md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_FM1; + break; + case ENCODING_FM_SPACE: /* FM0 */ + md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_FM0; + break; + case ENCODING_MANCHESTER: /* It's not working... */ + md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_MANCH; + break; + case ENCODING_NRZ: + default: + md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_NRZ; + break; + } + cpc_writeb(scabase + M_REG(MD0, ch), md0); + cpc_writeb(scabase + M_REG(MD1, ch), 0); + cpc_writeb(scabase + M_REG(MD2, ch), md2); + cpc_writeb(scabase + M_REG(IDL, ch), 0x7e); + cpc_writeb(scabase + M_REG(CTL, ch), CTL_URSKP | CTL_IDLC); + + /* Configure HW media */ + switch (card->hw.type) { + case PC300_RSV: + if (conf->media == IF_IFACE_V35) { + cpc_writel((plxbase + card->hw.gpioc_reg), + cpc_readl(plxbase + card->hw.gpioc_reg) | PC300_CHMEDIA_MASK(ch)); + } else { + cpc_writel((plxbase + card->hw.gpioc_reg), + cpc_readl(plxbase + card->hw.gpioc_reg) & ~PC300_CHMEDIA_MASK(ch)); + } + break; + + case PC300_X21: + break; + + case PC300_TE: + te_config(card, ch); + break; + } + + switch (card->hw.type) { + case PC300_RSV: + case PC300_X21: + if (clktype == CLOCK_INT || clktype == CLOCK_TXINT) { + /* Calculate the clkrate parameters */ + tmc = clock_rate_calc(clkrate, card->hw.clock, &br); + cpc_writeb(scabase + M_REG(TMCT, ch), tmc); + cpc_writeb(scabase + M_REG(TXS, ch), + (TXS_DTRXC | TXS_IBRG | br)); + if (clktype == CLOCK_INT) { + cpc_writeb(scabase + M_REG(TMCR, ch), tmc); + cpc_writeb(scabase + M_REG(RXS, ch), + (RXS_IBRG | br)); + } else { + cpc_writeb(scabase + M_REG(TMCR, ch), 1); + cpc_writeb(scabase + M_REG(RXS, ch), 0); + } + if (card->hw.type == PC300_X21) { + cpc_writeb(scabase + M_REG(GPO, ch), 1); + cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1 | EXS_RES1); + } else { + cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1); + } + } else { + cpc_writeb(scabase + M_REG(TMCT, ch), 1); + if (clktype == CLOCK_EXT) { + cpc_writeb(scabase + M_REG(TXS, ch), + TXS_DTRXC); + } else { + cpc_writeb(scabase + M_REG(TXS, ch), + TXS_DTRXC|TXS_RCLK); + } + cpc_writeb(scabase + M_REG(TMCR, ch), 1); + cpc_writeb(scabase + M_REG(RXS, ch), 0); + if (card->hw.type == PC300_X21) { + cpc_writeb(scabase + M_REG(GPO, ch), 0); + cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1 | EXS_RES1); + } else { + cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1); + } + } + break; + + case PC300_TE: + /* SCA always receives clock from the FALC chip */ + cpc_writeb(scabase + M_REG(TMCT, ch), 1); + cpc_writeb(scabase + M_REG(TXS, ch), 0); + cpc_writeb(scabase + M_REG(TMCR, ch), 1); + cpc_writeb(scabase + M_REG(RXS, ch), 0); + cpc_writeb(scabase + M_REG(EXS, ch), 0); + break; + } + + /* Enable Interrupts */ + cpc_writel(scabase + IER0, + cpc_readl(scabase + IER0) | + IR0_M(IR0_RXINTA, ch) | + IR0_DRX(IR0_EFT | IR0_DMIA | IR0_DMIB, ch) | + IR0_DTX(IR0_EFT | IR0_DMIA | IR0_DMIB, ch)); + cpc_writeb(scabase + M_REG(IE0, ch), + cpc_readl(scabase + M_REG(IE0, ch)) | IE0_RXINTA); + cpc_writeb(scabase + M_REG(IE1, ch), + cpc_readl(scabase + M_REG(IE1, ch)) | IE1_CDCD); + + return 0; +} + +int rx_config(pc300dev_t * d) +{ + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + uclong scabase = card->hw.scabase; + int ch = chan->channel; + + cpc_writeb(scabase + DSR_RX(ch), 0); + + /* General RX settings */ + cpc_writeb(scabase + M_REG(RRC, ch), 0); + cpc_writeb(scabase + M_REG(RNR, ch), 16); + + /* Enable reception */ + cpc_writeb(scabase + M_REG(CMD, ch), CMD_RX_CRC_INIT); + cpc_writeb(scabase + M_REG(CMD, ch), CMD_RX_ENA); + + /* Initialize DMA stuff */ + chan->rx_first_bd = 0; + chan->rx_last_bd = N_DMA_RX_BUF - 1; + rx_dma_buf_init(card, ch); + cpc_writeb(scabase + DCR_RX(ch), DCR_FCT_CLR); + cpc_writeb(scabase + DMR_RX(ch), (DMR_TMOD | DMR_NF)); + cpc_writeb(scabase + DIR_RX(ch), (DIR_EOM | DIR_BOF)); + + /* Start DMA */ + rx_dma_start(card, ch); + + return 0; +} + +int tx_config(pc300dev_t * d) +{ + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + uclong scabase = card->hw.scabase; + int ch = chan->channel; + + cpc_writeb(scabase + DSR_TX(ch), 0); + + /* General TX settings */ + cpc_writeb(scabase + M_REG(TRC0, ch), 0); + cpc_writeb(scabase + M_REG(TFS, ch), 32); + cpc_writeb(scabase + M_REG(TNR0, ch), 20); + cpc_writeb(scabase + M_REG(TNR1, ch), 48); + cpc_writeb(scabase + M_REG(TCR, ch), 8); + + /* Enable transmission */ + cpc_writeb(scabase + M_REG(CMD, ch), CMD_TX_CRC_INIT); + + /* Initialize DMA stuff */ + chan->tx_first_bd = 0; + chan->tx_next_bd = 0; + tx_dma_buf_init(card, ch); + cpc_writeb(scabase + DCR_TX(ch), DCR_FCT_CLR); + cpc_writeb(scabase + DMR_TX(ch), (DMR_TMOD | DMR_NF)); + cpc_writeb(scabase + DIR_TX(ch), (DIR_EOM | DIR_BOF | DIR_UDRF)); + cpc_writel(scabase + DTX_REG(CDAL, ch), TX_BD_ADDR(ch, chan->tx_first_bd)); + cpc_writel(scabase + DTX_REG(EDAL, ch), TX_BD_ADDR(ch, chan->tx_next_bd)); + + return 0; +} + +static int cpc_attach(hdlc_device * hdlc, unsigned short encoding, + unsigned short parity) +{ + struct net_device * dev = hdlc_to_dev(hdlc); + pc300dev_t *d = (pc300dev_t *)dev->priv; + pc300ch_t *chan = (pc300ch_t *)d->chan; + pc300_t *card = (pc300_t *)chan->card; + pc300chconf_t *conf = (pc300chconf_t *)&chan->conf; + + if (card->hw.type == PC300_TE) { + if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI) { + return -EINVAL; + } + } else { + if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI && + encoding != ENCODING_FM_MARK && encoding != ENCODING_FM_SPACE) { + /* Driver doesn't support ENCODING_MANCHESTER yet */ + return -EINVAL; + } + } + + if (parity != PARITY_NONE && parity != PARITY_CRC16_PR0 && + parity != PARITY_CRC16_PR1 && parity != PARITY_CRC32_PR1_CCITT && + parity != PARITY_CRC16_PR1_CCITT) { + return -EINVAL; + } + + conf->proto_settings.encoding = encoding; + conf->proto_settings.parity = parity; + return 0; +} + +void cpc_opench(pc300dev_t * d) +{ + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + int ch = chan->channel; + uclong scabase = card->hw.scabase; + + ch_config(d); + + rx_config(d); + + tx_config(d); + + /* Assert RTS and DTR */ + cpc_writeb(scabase + M_REG(CTL, ch), + cpc_readb(scabase + M_REG(CTL, ch)) & ~(CTL_RTS | CTL_DTR)); +} + +void cpc_closech(pc300dev_t * d) +{ + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + falc_t *pfalc = (falc_t *) & chan->falc; + int ch = chan->channel; + + cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_CH_RST); + rx_dma_stop(card, ch); + tx_dma_stop(card, ch); + + if (card->hw.type == PC300_TE) { + memset(pfalc, 0, sizeof(falc_t)); + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & + ~((CPLD_REG2_FALC_TX_CLK | CPLD_REG2_FALC_RX_CLK | + CPLD_REG2_FALC_LED2) << (2 * ch))); + /* Reset the FALC chip */ + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) | + (CPLD_REG1_FALC_RESET << (2 * ch))); + udelay(10000); + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) & + ~(CPLD_REG1_FALC_RESET << (2 * ch))); + } +} + +int cpc_open(struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + pc300dev_t *d = (pc300dev_t *) dev->priv; + struct ifreq ifr; + int result; + +#ifdef PC300_DEBUG_OTHER + printk("pc300: cpc_open"); +#endif + + if (hdlc->proto == IF_PROTO_PPP) { + d->if_ptr = &hdlc->state.ppp.pppdev; + } + + result = hdlc_open(hdlc); + if (hdlc->proto == IF_PROTO_PPP) { + dev->priv = d; + } + if (result) { + return result; + } + + MOD_INC_USE_COUNT; + sprintf(ifr.ifr_name, "%s", dev->name); + cpc_opench(d); + netif_start_queue(dev); + return 0; +} + +int cpc_close(struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + pc300dev_t *d = (pc300dev_t *) dev->priv; + pc300ch_t *chan = (pc300ch_t *) d->chan; + pc300_t *card = (pc300_t *) chan->card; + uclong flags; + +#ifdef PC300_DEBUG_OTHER + printk("pc300: cpc_close"); +#endif + + netif_stop_queue(dev); + + CPC_LOCK(card, flags); + cpc_closech(d); + CPC_UNLOCK(card, flags); + + hdlc_close(hdlc); + if (hdlc->proto == IF_PROTO_PPP) { + d->if_ptr = NULL; + } +#ifdef CONFIG_PC300_MLPPP + if (chan->conf.proto == PC300_PROTO_MLPPP) { + cpc_tty_unregister_service(d); + chan->conf.proto = 0xffff; + } +#endif + + MOD_DEC_USE_COUNT; + return 0; +} + +static uclong detect_ram(pc300_t * card) +{ + uclong i; + ucchar data; + uclong rambase = card->hw.rambase; + + card->hw.ramsize = PC300_RAMSIZE; + /* Let's find out how much RAM is present on this board */ + for (i = 0; i < card->hw.ramsize; i++) { + data = (ucchar) (i & 0xff); + cpc_writeb(rambase + i, data); + if (cpc_readb(rambase + i) != data) { + break; + } + } + return (i); +} + +static void plx_init(pc300_t * card) +{ + struct RUNTIME_9050 *plx_ctl = (struct RUNTIME_9050 *) card->hw.plxbase; + + /* Reset PLX */ + cpc_writel(&plx_ctl->init_ctrl, + cpc_readl(&plx_ctl->init_ctrl) | 0x40000000); + udelay(10000L); + cpc_writel(&plx_ctl->init_ctrl, + cpc_readl(&plx_ctl->init_ctrl) & ~0x40000000); + + /* Reload Config. Registers from EEPROM */ + cpc_writel(&plx_ctl->init_ctrl, + cpc_readl(&plx_ctl->init_ctrl) | 0x20000000); + udelay(10000L); + cpc_writel(&plx_ctl->init_ctrl, + cpc_readl(&plx_ctl->init_ctrl) & ~0x20000000); + +} + +static inline void show_version(void) +{ + char *rcsvers, *rcsdate, *tmp; + + rcsvers = strchr(rcsid, ' '); + rcsvers++; + tmp = strchr(rcsvers, ' '); + *tmp++ = '\0'; + rcsdate = strchr(tmp, ' '); + rcsdate++; + tmp = strrchr(rcsdate, ' '); + *tmp = '\0'; + printk(KERN_INFO "Cyclades-PC300 driver %s %s (built %s %s)\n", + rcsvers, rcsdate, __DATE__, __TIME__); +} /* show_version */ + +static void cpc_init_card(pc300_t * card) +{ + int i, devcount = 0; + static int board_nbr = 1; + + /* Enable interrupts on the PCI bridge */ + plx_init(card); + cpc_writew(card->hw.plxbase + card->hw.intctl_reg, + cpc_readw(card->hw.plxbase + card->hw.intctl_reg) | 0x0040); + +#ifdef USE_PCI_CLOCK + /* Set board clock to PCI clock */ + cpc_writel(card->hw.plxbase + card->hw.gpioc_reg, + cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) | 0x00000004UL); + card->hw.clock = PC300_PCI_CLOCK; +#else + /* Set board clock to internal oscillator clock */ + cpc_writel(card->hw.plxbase + card->hw.gpioc_reg, + cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) & ~0x00000004UL); + card->hw.clock = PC300_OSC_CLOCK; +#endif + + /* Detect actual on-board RAM size */ + card->hw.ramsize = detect_ram(card); + + /* Set Global SCA-II registers */ + cpc_writeb(card->hw.scabase + PCR, PCR_PR2); + cpc_writeb(card->hw.scabase + BTCR, 0x10); + cpc_writeb(card->hw.scabase + WCRL, 0); + cpc_writeb(card->hw.scabase + DMER, 0x80); + + if (card->hw.type == PC300_TE) { + ucchar reg1; + + /* Check CPLD version */ + reg1 = cpc_readb(card->hw.falcbase + CPLD_REG1); + cpc_writeb(card->hw.falcbase + CPLD_REG1, (reg1 + 0x5a)); + if (cpc_readb(card->hw.falcbase + CPLD_REG1) == reg1) { + /* New CPLD */ + card->hw.cpld_id = cpc_readb(card->hw.falcbase + CPLD_ID_REG); + card->hw.cpld_reg1 = CPLD_V2_REG1; + card->hw.cpld_reg2 = CPLD_V2_REG2; + } else { + /* old CPLD */ + card->hw.cpld_id = 0; + card->hw.cpld_reg1 = CPLD_REG1; + card->hw.cpld_reg2 = CPLD_REG2; + cpc_writeb(card->hw.falcbase + CPLD_REG1, reg1); + } + + /* Enable the board's global clock */ + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) | + CPLD_REG1_GLOBAL_CLK); + + } + + for (i = 0; i < card->hw.nchan; i++) { + pc300ch_t *chan = &card->chan[i]; + pc300dev_t *d = &chan->d; + hdlc_device *hdlc; + struct net_device *dev; + + chan->card = card; + chan->channel = i; + chan->conf.phys_settings.clock_rate = 0; + chan->conf.phys_settings.clock_type = CLOCK_EXT; + chan->conf.proto_settings.encoding = ENCODING_NRZ; + chan->conf.proto_settings.parity = PARITY_CRC16_PR1_CCITT; + switch (card->hw.type) { + case PC300_TE: + chan->conf.media = IF_IFACE_T1; + chan->conf.lcode = PC300_LC_B8ZS; + chan->conf.fr_mode = PC300_FR_ESF; + chan->conf.lbo = PC300_LBO_0_DB; + chan->conf.rx_sens = PC300_RX_SENS_SH; + chan->conf.tslot_bitmap = 0xffffffffUL; + break; + + case PC300_X21: + chan->conf.media = IF_IFACE_X21; + break; + + case PC300_RSV: + default: + chan->conf.media = IF_IFACE_V35; + break; + } + chan->conf.proto = IF_PROTO_PPP; + chan->tx_first_bd = 0; + chan->tx_next_bd = 0; + chan->rx_first_bd = 0; + chan->rx_last_bd = N_DMA_RX_BUF - 1; + chan->nfree_tx_bd = N_DMA_TX_BUF; + + d->chan = chan; + d->tx_skb = NULL; + d->trace_on = 0; + d->line_on = 0; + d->line_off = 0; + + d->hdlc = (hdlc_device *) kmalloc(sizeof(hdlc_device), GFP_KERNEL); + if (d->hdlc == NULL) + continue; + memset(d->hdlc, 0, sizeof(hdlc_device)); + + hdlc = d->hdlc; + hdlc->xmit = cpc_queue_xmit; + hdlc->attach = cpc_attach; + + dev = hdlc_to_dev(hdlc); + + dev->mem_start = card->hw.ramphys; + dev->mem_end = card->hw.ramphys + card->hw.ramsize - 1; + dev->irq = card->hw.irq; + dev->init = NULL; + dev->tx_queue_len = PC300_TX_QUEUE_LEN; + dev->mtu = PC300_DEF_MTU; + + dev->open = cpc_open; + dev->stop = cpc_close; + dev->tx_timeout = cpc_tx_timeout; + dev->watchdog_timeo = PC300_TX_TIMEOUT; + dev->get_stats = cpc_get_stats; + dev->set_multicast_list = NULL; + dev->set_mac_address = NULL; + dev->change_mtu = cpc_change_mtu; + dev->do_ioctl = cpc_ioctl; + + if (register_hdlc_device(hdlc) == 0) { + dev->priv = d; /* We need 'priv', hdlc doesn't */ + printk("%s: Cyclades-PC300/", dev->name); + switch (card->hw.type) { + case PC300_TE: + if (card->hw.bus == PC300_PMC) { + printk("TE-M"); + } else { + printk("TE "); + } + break; + + case PC300_X21: + printk("X21 "); + break; + + case PC300_RSV: + default: + printk("RSV "); + break; + } + printk (" #%d, %ldKB of RAM at 0x%08lx, IRQ%d, channel %d.\n", + board_nbr, card->hw.ramsize / 1024, + card->hw.ramphys, card->hw.irq, i + 1); + devcount++; + } else { + printk ("Dev%d on card(0x%08lx): unable to allocate i/f name.\n", + i + 1, card->hw.ramphys); + *(dev->name) = 0; + kfree(d->hdlc); + continue; + } + } + spin_lock_init(&card->card_lock); + + board_nbr++; +} + +static int __devinit +cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int first_time = 1; + ucchar cpc_rev_id; + int err = 0, eeprom_outdated = 0; + ucshort device_id; + pc300_t *card; + + if (first_time) { + first_time = 0; + show_version(); +#ifdef CONFIG_PC300_MLPPP + cpc_tty_reset_var(); +#endif + } + + card = (pc300_t *) kmalloc(sizeof(pc300_t), GFP_KERNEL); + if (card == NULL) { + printk("PC300 found at RAM 0x%08lx, " + "but could not allocate card structure.\n", + pci_resource_start(pdev, 3)); + return -ENOMEM; + } + memset(card, 0, sizeof(pc300_t)); + + /* read PCI configuration area */ + device_id = ent->device; + card->hw.irq = pdev->irq; + card->hw.iophys = pci_resource_start(pdev, 1); + card->hw.iosize = pci_resource_len(pdev, 1); + card->hw.scaphys = pci_resource_start(pdev, 2); + card->hw.scasize = pci_resource_len(pdev, 2); + card->hw.ramphys = pci_resource_start(pdev, 3); + card->hw.alloc_ramsize = pci_resource_len(pdev, 3); + card->hw.falcphys = pci_resource_start(pdev, 4); + card->hw.falcsize = pci_resource_len(pdev, 4); + card->hw.plxphys = pci_resource_start(pdev, 5); + card->hw.plxsize = pci_resource_len(pdev, 5); + pci_read_config_byte(pdev, PCI_REVISION_ID, &cpc_rev_id); + + switch (device_id) { + case PCI_DEVICE_ID_PC300_RX_1: + case PCI_DEVICE_ID_PC300_TE_1: + card->hw.nchan = 1; + break; + + case PCI_DEVICE_ID_PC300_RX_2: + case PCI_DEVICE_ID_PC300_TE_2: + default: + card->hw.nchan = PC300_MAXCHAN; + break; + } +#ifdef PC300_DEBUG_PCI + printk("cpc (bus=0x0%x,pci_id=0x%x,", pdev->bus->number, pdev->devfn); + printk("rev_id=%d) IRQ%d\n", cpc_rev_id, card->hw.irq); + printk("cpc:found ramaddr=0x%08lx plxaddr=0x%08lx " + "ctladdr=0x%08lx falcaddr=0x%08lx\n", + card->hw.ramphys, card->hw.plxphys, card->hw.scaphys, + card->hw.falcphys); +#endif + /* Although we don't use this I/O region, we should + * request it from the kernel anyway, to avoid problems + * with other drivers accessing it. */ + if (!request_region(card->hw.iophys, card->hw.iosize, "PLX Registers")) { + /* In case we can't allocate it, warn user */ + printk("WARNING: couldn't allocate I/O region for PC300 board " + "at 0x%08lx!\n", card->hw.ramphys); + } + + if (card->hw.plxphys) { + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, card->hw.plxphys); + } else { + eeprom_outdated = 1; + card->hw.plxphys = pci_resource_start(pdev, 0); + card->hw.plxsize = pci_resource_len(pdev, 0); + } + + if (!request_mem_region(card->hw.plxphys, card->hw.plxsize, + "PLX Registers")) { + printk("PC300 found at RAM 0x%08lx, " + "but could not allocate PLX mem region.\n", + card->hw.ramphys); + err = -ENODEV; + goto err_release_io; + } + if (!request_mem_region(card->hw.ramphys, card->hw.alloc_ramsize, + "On-board RAM")) { + printk("PC300 found at RAM 0x%08lx, " + "but could not allocate RAM mem region.\n", + card->hw.ramphys); + err = -ENODEV; + goto err_release_plx; + } + if (!request_mem_region(card->hw.scaphys, card->hw.scasize, + "SCA-II Registers")) { + printk("PC300 found at RAM 0x%08lx, " + "but could not allocate SCA mem region.\n", + card->hw.ramphys); + err = -ENODEV; + goto err_release_ram; + } + + if ((err = pci_enable_device(pdev)) != 0) + goto err_release_sca; + + card->hw.plxbase = (uclong) ioremap(card->hw.plxphys, card->hw.plxsize); + card->hw.rambase = (uclong) ioremap(card->hw.ramphys, + card->hw.alloc_ramsize); + card->hw.scabase = (uclong) ioremap(card->hw.scaphys, card->hw.scasize); + switch (device_id) { + case PCI_DEVICE_ID_PC300_TE_1: + case PCI_DEVICE_ID_PC300_TE_2: + request_mem_region(card->hw.falcphys, card->hw.falcsize, + "FALC Registers"); + card->hw.falcbase = (uclong) ioremap(card->hw.falcphys, + card->hw.falcsize); + break; + + case PCI_DEVICE_ID_PC300_RX_1: + case PCI_DEVICE_ID_PC300_RX_2: + default: + card->hw.falcbase = 0; + break; + } + +#ifdef PC300_DEBUG_PCI + printk("cpc: relocate ramaddr=0x%08lx plxaddr=0x%08lx " + "ctladdr=0x%08lx falcaddr=0x%08lx\n", + card->hw.rambase, card->hw.plxbase, card->hw.scabase, + card->hw.falcbase); +#endif + + /* Set PCI drv pointer to the card structure */ + pdev->driver_data = card; + + /* Set board type */ + switch (device_id) { + case PCI_DEVICE_ID_PC300_TE_1: + case PCI_DEVICE_ID_PC300_TE_2: + card->hw.type = PC300_TE; + card->hw.bus = PC300_PCI; + /* Set PLX register offsets */ + card->hw.gpioc_reg = 0x50; + card->hw.intctl_reg = 0x4c; + break; + case PCI_DEVICE_ID_PC300_RX_1: + case PCI_DEVICE_ID_PC300_RX_2: + default: + card->hw.bus = PC300_PCI; + /* Set PLX register offsets */ + card->hw.gpioc_reg = 0x50; + card->hw.intctl_reg = 0x4c; + + if ((cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) & PC300_CTYPE_MASK)) { + card->hw.type = PC300_X21; + } else { + card->hw.type = PC300_RSV; + } + break; + } + + /* Allocate IRQ */ + if (request_irq(card->hw.irq, cpc_intr, SA_SHIRQ, "Cyclades-PC300", card)) { + printk ("PC300 found at RAM 0x%08lx, but could not allocate IRQ%d.\n", + card->hw.ramphys, card->hw.irq); + goto err_io_unmap; + } + + cpc_init_card(card); + + if (eeprom_outdated) + printk("WARNING: PC300 with outdated EEPROM.\n"); + return 0; + +err_io_unmap: + iounmap((void *) card->hw.plxbase); + iounmap((void *) card->hw.scabase); + iounmap((void *) card->hw.rambase); + if (card->hw.type == PC300_TE) { + iounmap((void *) card->hw.falcbase); + release_mem_region(card->hw.falcphys, card->hw.falcsize); + } +err_release_sca: + release_mem_region(card->hw.scaphys, card->hw.scasize); +err_release_ram: + release_mem_region(card->hw.ramphys, card->hw.alloc_ramsize); +err_release_plx: + release_mem_region(card->hw.plxphys, card->hw.plxsize); +err_release_io: + release_region(card->hw.iophys, card->hw.iosize); + kfree(card); + return -ENODEV; +} + +static void __devexit cpc_remove_one(struct pci_dev *pdev) +{ + pc300_t *card = (pc300_t *) pdev->driver_data; + + if (card->hw.rambase != 0) { + int i; + + /* Disable interrupts on the PCI bridge */ + cpc_writew(card->hw.plxbase + card->hw.intctl_reg, + cpc_readw(card->hw.plxbase + card->hw.intctl_reg) & ~(0x0040)); + + for (i = 0; i < card->hw.nchan; i++) { + unregister_hdlc_device(card->chan[i].d.hdlc); + } + iounmap((void *) card->hw.plxbase); + iounmap((void *) card->hw.scabase); + iounmap((void *) card->hw.rambase); + release_mem_region(card->hw.plxphys, card->hw.plxsize); + release_mem_region(card->hw.ramphys, card->hw.alloc_ramsize); + release_mem_region(card->hw.scaphys, card->hw.scasize); + release_region(card->hw.iophys, card->hw.iosize); + if (card->hw.type == PC300_TE) { + iounmap((void *) card->hw.falcbase); + release_mem_region(card->hw.falcphys, card->hw.falcsize); + } + if (card->hw.irq) + free_irq(card->hw.irq, card); + kfree(card); + } +} + +static struct pci_driver cpc_driver = { + name:"pc300", + id_table:cpc_pci_dev_id, + probe:cpc_init_one, + remove:cpc_remove_one, + suspend:NULL, + resume:NULL, +}; + +static int __init cpc_init(void) +{ + return pci_module_init(&cpc_driver); +} + +static void __exit cpc_cleanup_module(void) +{ + pci_unregister_driver(&cpc_driver); +} + +module_init(cpc_init); +module_exit(cpc_cleanup_module); + +MODULE_DESCRIPTION("Cyclades-PC300 cards driver"); +MODULE_AUTHOR( "Author: Ivan Passos \r\n" + "Maintainer: Henrique Gobbi + * + * Copyright: (c) 2000-2001 Cyclades 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. + * + */ + +#ifndef _FALC_LH_H +#define _FALC_LH_H + +#define NUM_OF_T1_CHANNELS 24 +#define NUM_OF_E1_CHANNELS 32 + +/*>>>>>>>>>>>>>>>>> FALC Register Bits (Transmit Mode) <<<<<<<<<<<<<<<<<<< */ + +/* CMDR (Command Register) + ---------------- E1 & T1 ------------------------------ */ +#define CMDR_RMC 0x80 +#define CMDR_RRES 0x40 +#define CMDR_XREP 0x20 +#define CMDR_XRES 0x10 +#define CMDR_XHF 0x08 +#define CMDR_XTF 0x04 +#define CMDR_XME 0x02 +#define CMDR_SRES 0x01 + +/* MODE (Mode Register) + ----------------- E1 & T1 ----------------------------- */ +#define MODE_MDS2 0x80 +#define MODE_MDS1 0x40 +#define MODE_MDS0 0x20 +#define MODE_BRAC 0x10 +#define MODE_HRAC 0x08 + +/* IPC (Interrupt Port Configuration) + ----------------- E1 & T1 ----------------------------- */ +#define IPC_VIS 0x80 +#define IPC_SCI 0x04 +#define IPC_IC1 0x02 +#define IPC_IC0 0x01 + +/* CCR1 (Common Configuration Register 1) + ----------------- E1 & T1 ----------------------------- */ +#define CCR1_SFLG 0x80 +#define CCR1_XTS16RA 0x40 +#define CCR1_BRM 0x40 +#define CCR1_CASSYM 0x20 +#define CCR1_EDLX 0x20 +#define CCR1_EITS 0x10 +#define CCR1_ITF 0x08 +#define CCR1_RFT1 0x02 +#define CCR1_RFT0 0x01 + +/* CCR3 (Common Configuration Register 3) + ---------------- E1 & T1 ------------------------------ */ + +#define CCR3_PRE1 0x80 +#define CCR3_PRE0 0x40 +#define CCR3_EPT 0x20 +#define CCR3_RADD 0x10 +#define CCR3_RCRC 0x04 +#define CCR3_XCRC 0x02 + + +/* RTR1-4 (Receive Timeslot Register 1-4) + ---------------- E1 & T1 ------------------------------ */ + +#define RTR1_TS0 0x80 +#define RTR1_TS1 0x40 +#define RTR1_TS2 0x20 +#define RTR1_TS3 0x10 +#define RTR1_TS4 0x08 +#define RTR1_TS5 0x04 +#define RTR1_TS6 0x02 +#define RTR1_TS7 0x01 + +#define RTR2_TS8 0x80 +#define RTR2_TS9 0x40 +#define RTR2_TS10 0x20 +#define RTR2_TS11 0x10 +#define RTR2_TS12 0x08 +#define RTR2_TS13 0x04 +#define RTR2_TS14 0x02 +#define RTR2_TS15 0x01 + +#define RTR3_TS16 0x80 +#define RTR3_TS17 0x40 +#define RTR3_TS18 0x20 +#define RTR3_TS19 0x10 +#define RTR3_TS20 0x08 +#define RTR3_TS21 0x04 +#define RTR3_TS22 0x02 +#define RTR3_TS23 0x01 + +#define RTR4_TS24 0x80 +#define RTR4_TS25 0x40 +#define RTR4_TS26 0x20 +#define RTR4_TS27 0x10 +#define RTR4_TS28 0x08 +#define RTR4_TS29 0x04 +#define RTR4_TS30 0x02 +#define RTR4_TS31 0x01 + + +/* TTR1-4 (Transmit Timeslot Register 1-4) + ---------------- E1 & T1 ------------------------------ */ + +#define TTR1_TS0 0x80 +#define TTR1_TS1 0x40 +#define TTR1_TS2 0x20 +#define TTR1_TS3 0x10 +#define TTR1_TS4 0x08 +#define TTR1_TS5 0x04 +#define TTR1_TS6 0x02 +#define TTR1_TS7 0x01 + +#define TTR2_TS8 0x80 +#define TTR2_TS9 0x40 +#define TTR2_TS10 0x20 +#define TTR2_TS11 0x10 +#define TTR2_TS12 0x08 +#define TTR2_TS13 0x04 +#define TTR2_TS14 0x02 +#define TTR2_TS15 0x01 + +#define TTR3_TS16 0x80 +#define TTR3_TS17 0x40 +#define TTR3_TS18 0x20 +#define TTR3_TS19 0x10 +#define TTR3_TS20 0x08 +#define TTR3_TS21 0x04 +#define TTR3_TS22 0x02 +#define TTR3_TS23 0x01 + +#define TTR4_TS24 0x80 +#define TTR4_TS25 0x40 +#define TTR4_TS26 0x20 +#define TTR4_TS27 0x10 +#define TTR4_TS28 0x08 +#define TTR4_TS29 0x04 +#define TTR4_TS30 0x02 +#define TTR4_TS31 0x01 + + + +/* IMR0-4 (Interrupt Mask Register 0-4) + + ----------------- E1 & T1 ----------------------------- */ + +#define IMR0_RME 0x80 +#define IMR0_RFS 0x40 +#define IMR0_T8MS 0x20 +#define IMR0_ISF 0x20 +#define IMR0_RMB 0x10 +#define IMR0_CASC 0x08 +#define IMR0_RSC 0x08 +#define IMR0_CRC6 0x04 +#define IMR0_CRC4 0x04 +#define IMR0_PDEN 0x02 +#define IMR0_RPF 0x01 + +#define IMR1_CASE 0x80 +#define IMR1_RDO 0x40 +#define IMR1_ALLS 0x20 +#define IMR1_XDU 0x10 +#define IMR1_XMB 0x08 +#define IMR1_XLSC 0x02 +#define IMR1_XPR 0x01 +#define IMR1_LLBSC 0x80 + +#define IMR2_FAR 0x80 +#define IMR2_LFA 0x40 +#define IMR2_MFAR 0x20 +#define IMR2_T400MS 0x10 +#define IMR2_LMFA 0x10 +#define IMR2_AIS 0x08 +#define IMR2_LOS 0x04 +#define IMR2_RAR 0x02 +#define IMR2_RA 0x01 + +#define IMR3_ES 0x80 +#define IMR3_SEC 0x40 +#define IMR3_LMFA16 0x20 +#define IMR3_AIS16 0x10 +#define IMR3_RA16 0x08 +#define IMR3_API 0x04 +#define IMR3_XSLP 0x20 +#define IMR3_XSLN 0x10 +#define IMR3_LLBSC 0x08 +#define IMR3_XRS 0x04 +#define IMR3_SLN 0x02 +#define IMR3_SLP 0x01 + +#define IMR4_LFA 0x80 +#define IMR4_FER 0x40 +#define IMR4_CER 0x20 +#define IMR4_AIS 0x10 +#define IMR4_LOS 0x08 +#define IMR4_CVE 0x04 +#define IMR4_SLIP 0x02 +#define IMR4_EBE 0x01 + +/* FMR0-5 for E1 and T1 (Framer Mode Register ) */ + +#define FMR0_XC1 0x80 +#define FMR0_XC0 0x40 +#define FMR0_RC1 0x20 +#define FMR0_RC0 0x10 +#define FMR0_EXTD 0x08 +#define FMR0_ALM 0x04 +#define E1_FMR0_FRS 0x02 +#define T1_FMR0_FRS 0x08 +#define FMR0_SRAF 0x04 +#define FMR0_EXLS 0x02 +#define FMR0_SIM 0x01 + +#define FMR1_MFCS 0x80 +#define FMR1_AFR 0x40 +#define FMR1_ENSA 0x20 +#define FMR1_CTM 0x80 +#define FMR1_SIGM 0x40 +#define FMR1_EDL 0x20 +#define FMR1_PMOD 0x10 +#define FMR1_XFS 0x08 +#define FMR1_CRC 0x08 +#define FMR1_ECM 0x04 +#define FMR1_IMOD 0x02 +#define FMR1_XAIS 0x01 + +#define FMR2_RFS1 0x80 +#define FMR2_RFS0 0x40 +#define FMR2_MCSP 0x40 +#define FMR2_RTM 0x20 +#define FMR2_SSP 0x20 +#define FMR2_DAIS 0x10 +#define FMR2_SAIS 0x08 +#define FMR2_PLB 0x04 +#define FMR2_AXRA 0x02 +#define FMR2_ALMF 0x01 +#define FMR2_EXZE 0x01 + +#define LOOP_RTM 0x40 +#define LOOP_SFM 0x40 +#define LOOP_ECLB 0x20 +#define LOOP_CLA 0x1f + +/*--------------------- E1 ----------------------------*/ +#define FMR3_XLD 0x20 +#define FMR3_XLU 0x10 + +/*--------------------- T1 ----------------------------*/ +#define FMR4_AIS3 0x80 +#define FMR4_TM 0x40 +#define FMR4_XRA 0x20 +#define FMR4_SSC1 0x10 +#define FMR4_SSC0 0x08 +#define FMR4_AUTO 0x04 +#define FMR4_FM1 0x02 +#define FMR4_FM0 0x01 + +#define FMR5_SRS 0x80 +#define FMR5_EIBR 0x40 +#define FMR5_XLD 0x20 +#define FMR5_XLU 0x10 + + +/* LOOP (Channel Loop Back) + + ------------------ E1 & T1 ---------------------------- */ + +#define LOOP_SFM 0x40 +#define LOOP_ECLB 0x20 +#define LOOP_CLA4 0x10 +#define LOOP_CLA3 0x08 +#define LOOP_CLA2 0x04 +#define LOOP_CLA1 0x02 +#define LOOP_CLA0 0x01 + + + +/* XSW (Transmit Service Word Pulseframe) + + ------------------- E1 --------------------------- */ + +#define XSW_XSIS 0x80 +#define XSW_XTM 0x40 +#define XSW_XRA 0x20 +#define XSW_XY0 0x10 +#define XSW_XY1 0x08 +#define XSW_XY2 0x04 +#define XSW_XY3 0x02 +#define XSW_XY4 0x01 + + +/* XSP (Transmit Spare Bits) + + ------------------- E1 --------------------------- */ + +#define XSP_XAP 0x80 +#define XSP_CASEN 0x40 +#define XSP_TT0 0x20 +#define XSP_EBP 0x10 +#define XSP_AXS 0x08 +#define XSP_XSIF 0x04 +#define XSP_XS13 0x02 +#define XSP_XS15 0x01 + + +/* XC0/1 (Transmit Control 0/1) + ------------------ E1 & T1 ---------------------------- */ + +#define XC0_SA8E 0x80 +#define XC0_SA7E 0x40 +#define XC0_SA6E 0x20 +#define XC0_SA5E 0x10 +#define XC0_SA4E 0x08 +#define XC0_BRM 0x80 +#define XC0_MFBS 0x40 +#define XC0_SFRZ 0x10 +#define XC0_XCO2 0x04 +#define XC0_XCO1 0x02 +#define XC0_XCO0 0x01 + +#define XC1_XTO5 0x20 +#define XC1_XTO4 0x10 +#define XC1_XTO3 0x08 +#define XC1_XTO2 0x04 +#define XC1_XTO1 0x02 +#define XC1_XTO0 0x01 + + +/* RC0/1 (Receive Control 0/1) + ------------------ E1 & T1 ---------------------------- */ + +#define RC0_SICS 0x40 +#define RC0_CRCI 0x20 +#define RC0_XCRCI 0x10 +#define RC0_RDIS 0x08 +#define RC0_RCO2 0x04 +#define RC0_RCO1 0x02 +#define RC0_RCO0 0x01 + +#define RC1_SWD 0x80 +#define RC1_ASY4 0x40 +#define RC1_RRAM 0x40 +#define RC1_RTO5 0x20 +#define RC1_RTO4 0x10 +#define RC1_RTO3 0x08 +#define RC1_RTO2 0x04 +#define RC1_RTO1 0x02 +#define RC1_RTO0 0x01 + + + +/* XPM0-2 (Transmit Pulse Mask 0-2) + --------------------- E1 & T1 ------------------------- */ + +#define XPM0_XP12 0x80 +#define XPM0_XP11 0x40 +#define XPM0_XP10 0x20 +#define XPM0_XP04 0x10 +#define XPM0_XP03 0x08 +#define XPM0_XP02 0x04 +#define XPM0_XP01 0x02 +#define XPM0_XP00 0x01 + +#define XPM1_XP30 0x80 +#define XPM1_XP24 0x40 +#define XPM1_XP23 0x20 +#define XPM1_XP22 0x10 +#define XPM1_XP21 0x08 +#define XPM1_XP20 0x04 +#define XPM1_XP14 0x02 +#define XPM1_XP13 0x01 + +#define XPM2_XLHP 0x80 +#define XPM2_XLT 0x40 +#define XPM2_DAXLT 0x20 +#define XPM2_XP34 0x08 +#define XPM2_XP33 0x04 +#define XPM2_XP32 0x02 +#define XPM2_XP31 0x01 + + +/* TSWM (Transparent Service Word Mask) + ------------------ E1 ---------------------------- */ + +#define TSWM_TSIS 0x80 +#define TSWM_TSIF 0x40 +#define TSWM_TRA 0x20 +#define TSWM_TSA4 0x10 +#define TSWM_TSA5 0x08 +#define TSWM_TSA6 0x04 +#define TSWM_TSA7 0x02 +#define TSWM_TSA8 0x01 + +/* IDLE + + ------------------ E1 & T1 ----------------------- */ + +#define IDLE_IDL7 0x80 +#define IDLE_IDL6 0x40 +#define IDLE_IDL5 0x20 +#define IDLE_IDL4 0x10 +#define IDLE_IDL3 0x08 +#define IDLE_IDL2 0x04 +#define IDLE_IDL1 0x02 +#define IDLE_IDL0 0x01 + + +/* XSA4-8 + -------------------E1 ----------------------------- */ + +#define XSA4_XS47 0x80 +#define XSA4_XS46 0x40 +#define XSA4_XS45 0x20 +#define XSA4_XS44 0x10 +#define XSA4_XS43 0x08 +#define XSA4_XS42 0x04 +#define XSA4_XS41 0x02 +#define XSA4_XS40 0x01 + +#define XSA5_XS57 0x80 +#define XSA5_XS56 0x40 +#define XSA5_XS55 0x20 +#define XSA5_XS54 0x10 +#define XSA5_XS53 0x08 +#define XSA5_XS52 0x04 +#define XSA5_XS51 0x02 +#define XSA5_XS50 0x01 + +#define XSA6_XS67 0x80 +#define XSA6_XS66 0x40 +#define XSA6_XS65 0x20 +#define XSA6_XS64 0x10 +#define XSA6_XS63 0x08 +#define XSA6_XS62 0x04 +#define XSA6_XS61 0x02 +#define XSA6_XS60 0x01 + +#define XSA7_XS77 0x80 +#define XSA7_XS76 0x40 +#define XSA7_XS75 0x20 +#define XSA7_XS74 0x10 +#define XSA7_XS73 0x08 +#define XSA7_XS72 0x04 +#define XSA7_XS71 0x02 +#define XSA7_XS70 0x01 + +#define XSA8_XS87 0x80 +#define XSA8_XS86 0x40 +#define XSA8_XS85 0x20 +#define XSA8_XS84 0x10 +#define XSA8_XS83 0x08 +#define XSA8_XS82 0x04 +#define XSA8_XS81 0x02 +#define XSA8_XS80 0x01 + + +/* XDL1-3 (Transmit DL-Bit Register1-3 (read/write)) + ----------------------- T1 --------------------- */ + +#define XDL1_XDL17 0x80 +#define XDL1_XDL16 0x40 +#define XDL1_XDL15 0x20 +#define XDL1_XDL14 0x10 +#define XDL1_XDL13 0x08 +#define XDL1_XDL12 0x04 +#define XDL1_XDL11 0x02 +#define XDL1_XDL10 0x01 + +#define XDL2_XDL27 0x80 +#define XDL2_XDL26 0x40 +#define XDL2_XDL25 0x20 +#define XDL2_XDL24 0x10 +#define XDL2_XDL23 0x08 +#define XDL2_XDL22 0x04 +#define XDL2_XDL21 0x02 +#define XDL2_XDL20 0x01 + +#define XDL3_XDL37 0x80 +#define XDL3_XDL36 0x40 +#define XDL3_XDL35 0x20 +#define XDL3_XDL34 0x10 +#define XDL3_XDL33 0x08 +#define XDL3_XDL32 0x04 +#define XDL3_XDL31 0x02 +#define XDL3_XDL30 0x01 + + +/* ICB1-4 (Idle Channel Register 1-4) + ------------------ E1 ---------------------------- */ + +#define E1_ICB1_IC0 0x80 +#define E1_ICB1_IC1 0x40 +#define E1_ICB1_IC2 0x20 +#define E1_ICB1_IC3 0x10 +#define E1_ICB1_IC4 0x08 +#define E1_ICB1_IC5 0x04 +#define E1_ICB1_IC6 0x02 +#define E1_ICB1_IC7 0x01 + +#define E1_ICB2_IC8 0x80 +#define E1_ICB2_IC9 0x40 +#define E1_ICB2_IC10 0x20 +#define E1_ICB2_IC11 0x10 +#define E1_ICB2_IC12 0x08 +#define E1_ICB2_IC13 0x04 +#define E1_ICB2_IC14 0x02 +#define E1_ICB2_IC15 0x01 + +#define E1_ICB3_IC16 0x80 +#define E1_ICB3_IC17 0x40 +#define E1_ICB3_IC18 0x20 +#define E1_ICB3_IC19 0x10 +#define E1_ICB3_IC20 0x08 +#define E1_ICB3_IC21 0x04 +#define E1_ICB3_IC22 0x02 +#define E1_ICB3_IC23 0x01 + +#define E1_ICB4_IC24 0x80 +#define E1_ICB4_IC25 0x40 +#define E1_ICB4_IC26 0x20 +#define E1_ICB4_IC27 0x10 +#define E1_ICB4_IC28 0x08 +#define E1_ICB4_IC29 0x04 +#define E1_ICB4_IC30 0x02 +#define E1_ICB4_IC31 0x01 + +/* ICB1-4 (Idle Channel Register 1-4) + ------------------ T1 ---------------------------- */ + +#define T1_ICB1_IC1 0x80 +#define T1_ICB1_IC2 0x40 +#define T1_ICB1_IC3 0x20 +#define T1_ICB1_IC4 0x10 +#define T1_ICB1_IC5 0x08 +#define T1_ICB1_IC6 0x04 +#define T1_ICB1_IC7 0x02 +#define T1_ICB1_IC8 0x01 + +#define T1_ICB2_IC9 0x80 +#define T1_ICB2_IC10 0x40 +#define T1_ICB2_IC11 0x20 +#define T1_ICB2_IC12 0x10 +#define T1_ICB2_IC13 0x08 +#define T1_ICB2_IC14 0x04 +#define T1_ICB2_IC15 0x02 +#define T1_ICB2_IC16 0x01 + +#define T1_ICB3_IC17 0x80 +#define T1_ICB3_IC18 0x40 +#define T1_ICB3_IC19 0x20 +#define T1_ICB3_IC20 0x10 +#define T1_ICB3_IC21 0x08 +#define T1_ICB3_IC22 0x04 +#define T1_ICB3_IC23 0x02 +#define T1_ICB3_IC24 0x01 + +/* FMR3 (Framer Mode Register 3) + --------------------E1------------------------ */ + +#define FMR3_CMI 0x08 +#define FMR3_SYNSA 0x04 +#define FMR3_CFRZ 0x02 +#define FMR3_EXTIW 0x01 + + + +/* CCB1-3 (Clear Channel Register) + ------------------- T1 ----------------------- */ + +#define CCB1_CH1 0x80 +#define CCB1_CH2 0x40 +#define CCB1_CH3 0x20 +#define CCB1_CH4 0x10 +#define CCB1_CH5 0x08 +#define CCB1_CH6 0x04 +#define CCB1_CH7 0x02 +#define CCB1_CH8 0x01 + +#define CCB2_CH9 0x80 +#define CCB2_CH10 0x40 +#define CCB2_CH11 0x20 +#define CCB2_CH12 0x10 +#define CCB2_CH13 0x08 +#define CCB2_CH14 0x04 +#define CCB2_CH15 0x02 +#define CCB2_CH16 0x01 + +#define CCB3_CH17 0x80 +#define CCB3_CH18 0x40 +#define CCB3_CH19 0x20 +#define CCB3_CH20 0x10 +#define CCB3_CH21 0x08 +#define CCB3_CH22 0x04 +#define CCB3_CH23 0x02 +#define CCB3_CH24 0x01 + + +/* LIM0/1 (Line Interface Mode 0/1) + ------------------- E1 & T1 --------------------------- */ + +#define LIM0_XFB 0x80 +#define LIM0_XDOS 0x40 +#define LIM0_SCL1 0x20 +#define LIM0_SCL0 0x10 +#define LIM0_EQON 0x08 +#define LIM0_ELOS 0x04 +#define LIM0_LL 0x02 +#define LIM0_MAS 0x01 + +#define LIM1_EFSC 0x80 +#define LIM1_RIL2 0x40 +#define LIM1_RIL1 0x20 +#define LIM1_RIL0 0x10 +#define LIM1_DCOC 0x08 +#define LIM1_JATT 0x04 +#define LIM1_RL 0x02 +#define LIM1_DRS 0x01 + + +/* PCDR (Pulse Count Detection Register(Read/Write)) + ------------------ E1 & T1 ------------------------- */ + +#define PCDR_PCD7 0x80 +#define PCDR_PCD6 0x40 +#define PCDR_PCD5 0x20 +#define PCDR_PCD4 0x10 +#define PCDR_PCD3 0x08 +#define PCDR_PCD2 0x04 +#define PCDR_PCD1 0x02 +#define PCDR_PCD0 0x01 + +#define PCRR_PCR7 0x80 +#define PCRR_PCR6 0x40 +#define PCRR_PCR5 0x20 +#define PCRR_PCR4 0x10 +#define PCRR_PCR3 0x08 +#define PCRR_PCR2 0x04 +#define PCRR_PCR1 0x02 +#define PCRR_PCR0 0x01 + + +/* LIM2 (Line Interface Mode 2) + + ------------------ E1 & T1 ---------------------------- */ + +#define LIM2_DJA2 0x20 +#define LIM2_DJA1 0x10 +#define LIM2_LOS2 0x02 +#define LIM2_LOS1 0x01 + +/* LCR1 (Loop Code Register 1) */ + +#define LCR1_EPRM 0x80 +#define LCR1_XPRBS 0x40 + +/* SIC1 (System Interface Control 1) */ +#define SIC1_SRSC 0x80 +#define SIC1_RBS1 0x20 +#define SIC1_RBS0 0x10 +#define SIC1_SXSC 0x08 +#define SIC1_XBS1 0x02 +#define SIC1_XBS0 0x01 + +/* DEC (Disable Error Counter) + ------------------ E1 & T1 ---------------------------- */ + +#define DEC_DCEC3 0x20 +#define DEC_DBEC 0x10 +#define DEC_DCEC1 0x08 +#define DEC_DCEC 0x08 +#define DEC_DEBC 0x04 +#define DEC_DCVC 0x02 +#define DEC_DFEC 0x01 + + +/* FALC Register Bits (Receive Mode) + ---------------------------------------------------------------------------- */ + + +/* FRS0/1 (Framer Receive Status Register 0/1) + ----------------- E1 & T1 ---------------------------------- */ + +#define FRS0_LOS 0x80 +#define FRS0_AIS 0x40 +#define FRS0_LFA 0x20 +#define FRS0_RRA 0x10 +#define FRS0_API 0x08 +#define FRS0_NMF 0x04 +#define FRS0_LMFA 0x02 +#define FRS0_FSRF 0x01 + +#define FRS1_TS16RA 0x40 +#define FRS1_TS16LOS 0x20 +#define FRS1_TS16AIS 0x10 +#define FRS1_TS16LFA 0x08 +#define FRS1_EXZD 0x80 +#define FRS1_LLBDD 0x10 +#define FRS1_LLBAD 0x08 +#define FRS1_XLS 0x02 +#define FRS1_XLO 0x01 +#define FRS1_PDEN 0x40 + +/* FRS2/3 (Framer Receive Status Register 2/3) + ----------------- T1 ---------------------------------- */ + +#define FRS2_ESC2 0x80 +#define FRS2_ESC1 0x40 +#define FRS2_ESC0 0x20 + +#define FRS3_FEH5 0x20 +#define FRS3_FEH4 0x10 +#define FRS3_FEH3 0x08 +#define FRS3_FEH2 0x04 +#define FRS3_FEH1 0x02 +#define FRS3_FEH0 0x01 + + +/* RSW (Receive Service Word Pulseframe) + ----------------- E1 ------------------------------ */ + +#define RSW_RSI 0x80 +#define RSW_RRA 0x20 +#define RSW_RYO 0x10 +#define RSW_RY1 0x08 +#define RSW_RY2 0x04 +#define RSW_RY3 0x02 +#define RSW_RY4 0x01 + + +/* RSP (Receive Spare Bits / Additional Status) + ---------------- E1 ------------------------------- */ + +#define RSP_SI1 0x80 +#define RSP_SI2 0x40 +#define RSP_LLBDD 0x10 +#define RSP_LLBAD 0x08 +#define RSP_RSIF 0x04 +#define RSP_RS13 0x02 +#define RSP_RS15 0x01 + + +/* FECL (Framing Error Counter) + ---------------- E1 & T1 -------------------------- */ + +#define FECL_FE7 0x80 +#define FECL_FE6 0x40 +#define FECL_FE5 0x20 +#define FECL_FE4 0x10 +#define FECL_FE3 0x08 +#define FECL_FE2 0x04 +#define FECL_FE1 0x02 +#define FECL_FE0 0x01 + +#define FECH_FE15 0x80 +#define FECH_FE14 0x40 +#define FECH_FE13 0x20 +#define FECH_FE12 0x10 +#define FECH_FE11 0x08 +#define FECH_FE10 0x04 +#define FECH_FE9 0x02 +#define FECH_FE8 0x01 + + +/* CVCl (Code Violation Counter) + ----------------- E1 ------------------------- */ + +#define CVCL_CV7 0x80 +#define CVCL_CV6 0x40 +#define CVCL_CV5 0x20 +#define CVCL_CV4 0x10 +#define CVCL_CV3 0x08 +#define CVCL_CV2 0x04 +#define CVCL_CV1 0x02 +#define CVCL_CV0 0x01 + +#define CVCH_CV15 0x80 +#define CVCH_CV14 0x40 +#define CVCH_CV13 0x20 +#define CVCH_CV12 0x10 +#define CVCH_CV11 0x08 +#define CVCH_CV10 0x04 +#define CVCH_CV9 0x02 +#define CVCH_CV8 0x01 + + +/* CEC1-3L (CRC Error Counter) + ------------------ E1 ----------------------------- */ + +#define CEC1L_CR7 0x80 +#define CEC1L_CR6 0x40 +#define CEC1L_CR5 0x20 +#define CEC1L_CR4 0x10 +#define CEC1L_CR3 0x08 +#define CEC1L_CR2 0x04 +#define CEC1L_CR1 0x02 +#define CEC1L_CR0 0x01 + +#define CEC1H_CR15 0x80 +#define CEC1H_CR14 0x40 +#define CEC1H_CR13 0x20 +#define CEC1H_CR12 0x10 +#define CEC1H_CR11 0x08 +#define CEC1H_CR10 0x04 +#define CEC1H_CR9 0x02 +#define CEC1H_CR8 0x01 + +#define CEC2L_CR7 0x80 +#define CEC2L_CR6 0x40 +#define CEC2L_CR5 0x20 +#define CEC2L_CR4 0x10 +#define CEC2L_CR3 0x08 +#define CEC2L_CR2 0x04 +#define CEC2L_CR1 0x02 +#define CEC2L_CR0 0x01 + +#define CEC2H_CR15 0x80 +#define CEC2H_CR14 0x40 +#define CEC2H_CR13 0x20 +#define CEC2H_CR12 0x10 +#define CEC2H_CR11 0x08 +#define CEC2H_CR10 0x04 +#define CEC2H_CR9 0x02 +#define CEC2H_CR8 0x01 + +#define CEC3L_CR7 0x80 +#define CEC3L_CR6 0x40 +#define CEC3L_CR5 0x20 +#define CEC3L_CR4 0x10 +#define CEC3L_CR3 0x08 +#define CEC3L_CR2 0x04 +#define CEC3L_CR1 0x02 +#define CEC3L_CR0 0x01 + +#define CEC3H_CR15 0x80 +#define CEC3H_CR14 0x40 +#define CEC3H_CR13 0x20 +#define CEC3H_CR12 0x10 +#define CEC3H_CR11 0x08 +#define CEC3H_CR10 0x04 +#define CEC3H_CR9 0x02 +#define CEC3H_CR8 0x01 + + +/* CECL (CRC Error Counter) + + ------------------ T1 ----------------------------- */ + +#define CECL_CR7 0x80 +#define CECL_CR6 0x40 +#define CECL_CR5 0x20 +#define CECL_CR4 0x10 +#define CECL_CR3 0x08 +#define CECL_CR2 0x04 +#define CECL_CR1 0x02 +#define CECL_CR0 0x01 + +#define CECH_CR15 0x80 +#define CECH_CR14 0x40 +#define CECH_CR13 0x20 +#define CECH_CR12 0x10 +#define CECH_CR11 0x08 +#define CECH_CR10 0x04 +#define CECH_CR9 0x02 +#define CECH_CR8 0x01 + +/* EBCL (E Bit Error Counter) + ------------------- E1 & T1 ------------------------- */ + +#define EBCL_EB7 0x80 +#define EBCL_EB6 0x40 +#define EBCL_EB5 0x20 +#define EBCL_EB4 0x10 +#define EBCL_EB3 0x08 +#define EBCL_EB2 0x04 +#define EBCL_EB1 0x02 +#define EBCL_EB0 0x01 + +#define EBCH_EB15 0x80 +#define EBCH_EB14 0x40 +#define EBCH_EB13 0x20 +#define EBCH_EB12 0x10 +#define EBCH_EB11 0x08 +#define EBCH_EB10 0x04 +#define EBCH_EB9 0x02 +#define EBCH_EB8 0x01 + + +/* RSA4-8 (Receive Sa4-8-Bit Register) + -------------------- E1 --------------------------- */ + +#define RSA4_RS47 0x80 +#define RSA4_RS46 0x40 +#define RSA4_RS45 0x20 +#define RSA4_RS44 0x10 +#define RSA4_RS43 0x08 +#define RSA4_RS42 0x04 +#define RSA4_RS41 0x02 +#define RSA4_RS40 0x01 + +#define RSA5_RS57 0x80 +#define RSA5_RS56 0x40 +#define RSA5_RS55 0x20 +#define RSA5_RS54 0x10 +#define RSA5_RS53 0x08 +#define RSA5_RS52 0x04 +#define RSA5_RS51 0x02 +#define RSA5_RS50 0x01 + +#define RSA6_RS67 0x80 +#define RSA6_RS66 0x40 +#define RSA6_RS65 0x20 +#define RSA6_RS64 0x10 +#define RSA6_RS63 0x08 +#define RSA6_RS62 0x04 +#define RSA6_RS61 0x02 +#define RSA6_RS60 0x01 + +#define RSA7_RS77 0x80 +#define RSA7_RS76 0x40 +#define RSA7_RS75 0x20 +#define RSA7_RS74 0x10 +#define RSA7_RS73 0x08 +#define RSA7_RS72 0x04 +#define RSA7_RS71 0x02 +#define RSA7_RS70 0x01 + +#define RSA8_RS87 0x80 +#define RSA8_RS86 0x40 +#define RSA8_RS85 0x20 +#define RSA8_RS84 0x10 +#define RSA8_RS83 0x08 +#define RSA8_RS82 0x04 +#define RSA8_RS81 0x02 +#define RSA8_RS80 0x01 + +/* RSA6S (Receive Sa6 Bit Status Register) + ------------------------ T1 ------------------------- */ + +#define RSA6S_SX 0x20 +#define RSA6S_SF 0x10 +#define RSA6S_SE 0x08 +#define RSA6S_SC 0x04 +#define RSA6S_SA 0x02 +#define RSA6S_S8 0x01 + + +/* RDL1-3 Receive DL-Bit Register1-3) + ------------------------ T1 ------------------------- */ + +#define RDL1_RDL17 0x80 +#define RDL1_RDL16 0x40 +#define RDL1_RDL15 0x20 +#define RDL1_RDL14 0x10 +#define RDL1_RDL13 0x08 +#define RDL1_RDL12 0x04 +#define RDL1_RDL11 0x02 +#define RDL1_RDL10 0x01 + +#define RDL2_RDL27 0x80 +#define RDL2_RDL26 0x40 +#define RDL2_RDL25 0x20 +#define RDL2_RDL24 0x10 +#define RDL2_RDL23 0x08 +#define RDL2_RDL22 0x04 +#define RDL2_RDL21 0x02 +#define RDL2_RDL20 0x01 + +#define RDL3_RDL37 0x80 +#define RDL3_RDL36 0x40 +#define RDL3_RDL35 0x20 +#define RDL3_RDL34 0x10 +#define RDL3_RDL33 0x08 +#define RDL3_RDL32 0x04 +#define RDL3_RDL31 0x02 +#define RDL3_RDL30 0x01 + + +/* SIS (Signaling Status Register) + + -------------------- E1 & T1 -------------------------- */ + +#define SIS_XDOV 0x80 +#define SIS_XFW 0x40 +#define SIS_XREP 0x20 +#define SIS_RLI 0x08 +#define SIS_CEC 0x04 +#define SIS_BOM 0x01 + + +/* RSIS (Receive Signaling Status Register) + + -------------------- E1 & T1 --------------------------- */ + +#define RSIS_VFR 0x80 +#define RSIS_RDO 0x40 +#define RSIS_CRC16 0x20 +#define RSIS_RAB 0x10 +#define RSIS_HA1 0x08 +#define RSIS_HA0 0x04 +#define RSIS_HFR 0x02 +#define RSIS_LA 0x01 + + +/* RBCL/H (Receive Byte Count Low/High) + + ------------------- E1 & T1 ----------------------- */ + +#define RBCL_RBC7 0x80 +#define RBCL_RBC6 0x40 +#define RBCL_RBC5 0x20 +#define RBCL_RBC4 0x10 +#define RBCL_RBC3 0x08 +#define RBCL_RBC2 0x04 +#define RBCL_RBC1 0x02 +#define RBCL_RBC0 0x01 + +#define RBCH_OV 0x10 +#define RBCH_RBC11 0x08 +#define RBCH_RBC10 0x04 +#define RBCH_RBC9 0x02 +#define RBCH_RBC8 0x01 + + +/* ISR1-3 (Interrupt Status Register 1-3) + + ------------------ E1 & T1 ------------------------------ */ + +#define FISR0_RME 0x80 +#define FISR0_RFS 0x40 +#define FISR0_T8MS 0x20 +#define FISR0_ISF 0x20 +#define FISR0_RMB 0x10 +#define FISR0_CASC 0x08 +#define FISR0_RSC 0x08 +#define FISR0_CRC6 0x04 +#define FISR0_CRC4 0x04 +#define FISR0_PDEN 0x02 +#define FISR0_RPF 0x01 + +#define FISR1_CASE 0x80 +#define FISR1_LLBSC 0x80 +#define FISR1_RDO 0x40 +#define FISR1_ALLS 0x20 +#define FISR1_XDU 0x10 +#define FISR1_XMB 0x08 +#define FISR1_XLSC 0x02 +#define FISR1_XPR 0x01 + +#define FISR2_FAR 0x80 +#define FISR2_LFA 0x40 +#define FISR2_MFAR 0x20 +#define FISR2_T400MS 0x10 +#define FISR2_LMFA 0x10 +#define FISR2_AIS 0x08 +#define FISR2_LOS 0x04 +#define FISR2_RAR 0x02 +#define FISR2_RA 0x01 + +#define FISR3_ES 0x80 +#define FISR3_SEC 0x40 +#define FISR3_LMFA16 0x20 +#define FISR3_AIS16 0x10 +#define FISR3_RA16 0x08 +#define FISR3_API 0x04 +#define FISR3_XSLP 0x20 +#define FISR3_XSLN 0x10 +#define FISR3_LLBSC 0x08 +#define FISR3_XRS 0x04 +#define FISR3_SLN 0x02 +#define FISR3_SLP 0x01 + + +/* GIS (Global Interrupt Status Register) + + --------------------- E1 & T1 --------------------- */ + +#define GIS_ISR3 0x08 +#define GIS_ISR2 0x04 +#define GIS_ISR1 0x02 +#define GIS_ISR0 0x01 + + +/* VSTR (Version Status Register) + + --------------------- E1 & T1 --------------------- */ + +#define VSTR_VN3 0x08 +#define VSTR_VN2 0x04 +#define VSTR_VN1 0x02 +#define VSTR_VN0 0x01 + + +/*>>>>>>>>>>>>>>>>>>>>> Local Control Structures <<<<<<<<<<<<<<<<<<<<<<<<< */ + +/* Write-only Registers (E1/T1 control mode write registers) */ +#define XFIFOH 0x00 /* Tx FIFO High Byte */ +#define XFIFOL 0x01 /* Tx FIFO Low Byte */ +#define CMDR 0x02 /* Command Reg */ +#define DEC 0x60 /* Disable Error Counter */ +#define TEST2 0x62 /* Manuf. Test Reg 2 */ +#define XS(nbr) (0x70 + (nbr)) /* Tx CAS Reg (0 to 15) */ + +/* Read-write Registers (E1/T1 status mode read registers) */ +#define MODE 0x03 /* Mode Reg */ +#define RAH1 0x04 /* Receive Address High 1 */ +#define RAH2 0x05 /* Receive Address High 2 */ +#define RAL1 0x06 /* Receive Address Low 1 */ +#define RAL2 0x07 /* Receive Address Low 2 */ +#define IPC 0x08 /* Interrupt Port Configuration */ +#define CCR1 0x09 /* Common Configuration Reg 1 */ +#define CCR3 0x0A /* Common Configuration Reg 3 */ +#define PRE 0x0B /* Preamble Reg */ +#define RTR1 0x0C /* Receive Timeslot Reg 1 */ +#define RTR2 0x0D /* Receive Timeslot Reg 2 */ +#define RTR3 0x0E /* Receive Timeslot Reg 3 */ +#define RTR4 0x0F /* Receive Timeslot Reg 4 */ +#define TTR1 0x10 /* Transmit Timeslot Reg 1 */ +#define TTR2 0x11 /* Transmit Timeslot Reg 2 */ +#define TTR3 0x12 /* Transmit Timeslot Reg 3 */ +#define TTR4 0x13 /* Transmit Timeslot Reg 4 */ +#define IMR0 0x14 /* Interrupt Mask Reg 0 */ +#define IMR1 0x15 /* Interrupt Mask Reg 1 */ +#define IMR2 0x16 /* Interrupt Mask Reg 2 */ +#define IMR3 0x17 /* Interrupt Mask Reg 3 */ +#define IMR4 0x18 /* Interrupt Mask Reg 4 */ +#define IMR5 0x19 /* Interrupt Mask Reg 5 */ +#define FMR0 0x1A /* Framer Mode Reigster 0 */ +#define FMR1 0x1B /* Framer Mode Reigster 1 */ +#define FMR2 0x1C /* Framer Mode Reigster 2 */ +#define LOOP 0x1D /* Channel Loop Back */ +#define XSW 0x1E /* Transmit Service Word */ +#define FMR4 0x1E /* Framer Mode Reg 4 */ +#define XSP 0x1F /* Transmit Spare Bits */ +#define FMR5 0x1F /* Framer Mode Reg 5 */ +#define XC0 0x20 /* Transmit Control 0 */ +#define XC1 0x21 /* Transmit Control 1 */ +#define RC0 0x22 /* Receive Control 0 */ +#define RC1 0x23 /* Receive Control 1 */ +#define XPM0 0x24 /* Transmit Pulse Mask 0 */ +#define XPM1 0x25 /* Transmit Pulse Mask 1 */ +#define XPM2 0x26 /* Transmit Pulse Mask 2 */ +#define TSWM 0x27 /* Transparent Service Word Mask */ +#define TEST1 0x28 /* Manuf. Test Reg 1 */ +#define IDLE 0x29 /* Idle Channel Code */ +#define XSA4 0x2A /* Transmit SA4 Bit Reg */ +#define XDL1 0x2A /* Transmit DL-Bit Reg 2 */ +#define XSA5 0x2B /* Transmit SA4 Bit Reg */ +#define XDL2 0x2B /* Transmit DL-Bit Reg 2 */ +#define XSA6 0x2C /* Transmit SA4 Bit Reg */ +#define XDL3 0x2C /* Transmit DL-Bit Reg 2 */ +#define XSA7 0x2D /* Transmit SA4 Bit Reg */ +#define CCB1 0x2D /* Clear Channel Reg 1 */ +#define XSA8 0x2E /* Transmit SA4 Bit Reg */ +#define CCB2 0x2E /* Clear Channel Reg 2 */ +#define FMR3 0x2F /* Framer Mode Reg. 3 */ +#define CCB3 0x2F /* Clear Channel Reg 3 */ +#define ICB1 0x30 /* Idle Channel Reg 1 */ +#define ICB2 0x31 /* Idle Channel Reg 2 */ +#define ICB3 0x32 /* Idle Channel Reg 3 */ +#define ICB4 0x33 /* Idle Channel Reg 4 */ +#define LIM0 0x34 /* Line Interface Mode 0 */ +#define LIM1 0x35 /* Line Interface Mode 1 */ +#define PCDR 0x36 /* Pulse Count Detection */ +#define PCRR 0x37 /* Pulse Count Recovery */ +#define LIM2 0x38 /* Line Interface Mode Reg 2 */ +#define LCR1 0x39 /* Loop Code Reg 1 */ +#define LCR2 0x3A /* Loop Code Reg 2 */ +#define LCR3 0x3B /* Loop Code Reg 3 */ +#define SIC1 0x3C /* System Interface Control 1 */ + +/* Read-only Registers (E1/T1 control mode read registers) */ +#define RFIFOH 0x00 /* Receive FIFO */ +#define RFIFOL 0x01 /* Receive FIFO */ +#define FRS0 0x4C /* Framer Receive Status 0 */ +#define FRS1 0x4D /* Framer Receive Status 1 */ +#define RSW 0x4E /* Receive Service Word */ +#define FRS2 0x4E /* Framer Receive Status 2 */ +#define RSP 0x4F /* Receive Spare Bits */ +#define FRS3 0x4F /* Framer Receive Status 3 */ +#define FECL 0x50 /* Framing Error Counter */ +#define FECH 0x51 /* Framing Error Counter */ +#define CVCL 0x52 /* Code Violation Counter */ +#define CVCH 0x53 /* Code Violation Counter */ +#define CECL 0x54 /* CRC Error Counter 1 */ +#define CECH 0x55 /* CRC Error Counter 1 */ +#define EBCL 0x56 /* E-Bit Error Counter */ +#define EBCH 0x57 /* E-Bit Error Counter */ +#define BECL 0x58 /* Bit Error Counter Low */ +#define BECH 0x59 /* Bit Error Counter Low */ +#define CEC3 0x5A /* CRC Error Counter 3 (16-bit) */ +#define RSA4 0x5C /* Receive SA4 Bit Reg */ +#define RDL1 0x5C /* Receive DL-Bit Reg 1 */ +#define RSA5 0x5D /* Receive SA5 Bit Reg */ +#define RDL2 0x5D /* Receive DL-Bit Reg 2 */ +#define RSA6 0x5E /* Receive SA6 Bit Reg */ +#define RDL3 0x5E /* Receive DL-Bit Reg 3 */ +#define RSA7 0x5F /* Receive SA7 Bit Reg */ +#define RSA8 0x60 /* Receive SA8 Bit Reg */ +#define RSA6S 0x61 /* Receive SA6 Bit Status Reg */ +#define TSR0 0x62 /* Manuf. Test Reg 0 */ +#define TSR1 0x63 /* Manuf. Test Reg 1 */ +#define SIS 0x64 /* Signaling Status Reg */ +#define RSIS 0x65 /* Receive Signaling Status Reg */ +#define RBCL 0x66 /* Receive Byte Control */ +#define RBCH 0x67 /* Receive Byte Control */ +#define FISR0 0x68 /* Interrupt Status Reg 0 */ +#define FISR1 0x69 /* Interrupt Status Reg 1 */ +#define FISR2 0x6A /* Interrupt Status Reg 2 */ +#define FISR3 0x6B /* Interrupt Status Reg 3 */ +#define GIS 0x6E /* Global Interrupt Status */ +#define VSTR 0x6F /* Version Status */ +#define RS(nbr) (0x70 + (nbr)) /* Rx CAS Reg (0 to 15) */ + +#endif /* _FALC_LH_H */ + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/pc300.h linux.21rc1-ac2/drivers/net/wan/pc300.h --- linux.21rc1/drivers/net/wan/pc300.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/pc300.h 2003-04-25 16:05:08.000000000 +0100 @@ -0,0 +1,428 @@ +/* + * pc300.h Cyclades-PC300(tm) Kernel API Definitions. + * + * Author: Ivan Passos + * + * Copyright: (c) 1999-2002 Cyclades 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. + * + */ + +#ifndef _PC300_H +#define _PC300_H + +#ifndef __HDLC_IOCTL_H__ +#include +#endif + +#ifndef __HDLC_H +#include +#endif + +#ifndef _HD64572_H +#include "hd64572.h" +#endif +#ifndef _FALC_LH_H +#include "pc300-falc-lh.h" +#endif + +#ifndef CY_TYPES +#define CY_TYPES +#if defined(__alpha__) +typedef unsigned long ucdouble; /* 64 bits, unsigned */ +typedef unsigned int uclong; /* 32 bits, unsigned */ +#else +typedef unsigned long uclong; /* 32 bits, unsigned */ +#endif +typedef unsigned short ucshort; /* 16 bits, unsigned */ +typedef unsigned char ucchar; /* 8 bits, unsigned */ +#endif /* CY_TYPES */ + +#define PC300_PROTO_MLPPP 1 + +#define PC300_KERNEL "2.4.x" /* Kernel supported by this driver */ + +#define PC300_DEVNAME "hdlc" /* Dev. name base (for hdlc0, hdlc1, etc.) */ +#define PC300_MAXINDEX 100 /* Max dev. name index (the '0' in hdlc0) */ + +#define PC300_MAXCARDS 4 /* Max number of cards per system */ +#define PC300_MAXCHAN 2 /* Number of channels per card */ + +#define PC300_PLX_WIN 0x80 /* PLX control window size (128b) */ +#define PC300_RAMSIZE 0x40000 /* RAM window size (256Kb) */ +#define PC300_SCASIZE 0x400 /* SCA window size (1Kb) */ +#define PC300_FALCSIZE 0x400 /* FALC window size (1Kb) */ + +#define PC300_OSC_CLOCK 24576000 +#define PC300_PCI_CLOCK 33000000 + +#define BD_DEF_LEN 0x0800 /* DMA buffer length (2KB) */ +#define DMA_TX_MEMSZ 0x8000 /* Total DMA Tx memory size (32KB/ch) */ +#define DMA_RX_MEMSZ 0x10000 /* Total DMA Rx memory size (64KB/ch) */ + +#define N_DMA_TX_BUF (DMA_TX_MEMSZ / BD_DEF_LEN) /* DMA Tx buffers */ +#define N_DMA_RX_BUF (DMA_RX_MEMSZ / BD_DEF_LEN) /* DMA Rx buffers */ + +/* DMA Buffer Offsets */ +#define DMA_TX_BASE ((N_DMA_TX_BUF + N_DMA_RX_BUF) * \ + PC300_MAXCHAN * sizeof(pcsca_bd_t)) +#define DMA_RX_BASE (DMA_TX_BASE + PC300_MAXCHAN*DMA_TX_MEMSZ) + +/* DMA Descriptor Offsets */ +#define DMA_TX_BD_BASE 0x0000 +#define DMA_RX_BD_BASE (DMA_TX_BD_BASE + ((PC300_MAXCHAN*DMA_TX_MEMSZ / \ + BD_DEF_LEN) * sizeof(pcsca_bd_t))) + +/* DMA Descriptor Macros */ +#define TX_BD_ADDR(chan, n) (DMA_TX_BD_BASE + \ + ((N_DMA_TX_BUF*chan) + n) * sizeof(pcsca_bd_t)) +#define RX_BD_ADDR(chan, n) (DMA_RX_BD_BASE + \ + ((N_DMA_RX_BUF*chan) + n) * sizeof(pcsca_bd_t)) + +/* Macro to access the FALC registers (TE only) */ +#define F_REG(reg, chan) (0x200*(chan) + ((reg)<<2)) + +/*************************************** + * Memory access functions/macros * + * (required to support Alpha systems) * + ***************************************/ +#ifdef __KERNEL__ +#define cpc_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();} +#define cpc_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();} +#define cpc_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();} + +#define cpc_readb(port) readb(port) +#define cpc_readw(port) readw(port) +#define cpc_readl(port) readl(port) + +#else /* __KERNEL__ */ +#define cpc_writeb(port,val) (*(volatile ucchar *)(port) = (ucchar)(val)) +#define cpc_writew(port,val) (*(volatile ucshort *)(port) = (ucshort)(val)) +#define cpc_writel(port,val) (*(volatile uclong *)(port) = (uclong)(val)) + +#define cpc_readb(port) (*(volatile ucchar *)(port)) +#define cpc_readw(port) (*(volatile ucshort *)(port)) +#define cpc_readl(port) (*(volatile uclong *)(port)) + +#endif /* __KERNEL__ */ + +/****** Data Structures *****************************************************/ + +/* + * RUNTIME_9050 - PLX PCI9050-1 local configuration and shared runtime + * registers. This structure can be used to access the 9050 registers + * (memory mapped). + */ +struct RUNTIME_9050 { + uclong loc_addr_range[4]; /* 00-0Ch : Local Address Ranges */ + uclong loc_rom_range; /* 10h : Local ROM Range */ + uclong loc_addr_base[4]; /* 14-20h : Local Address Base Addrs */ + uclong loc_rom_base; /* 24h : Local ROM Base */ + uclong loc_bus_descr[4]; /* 28-34h : Local Bus Descriptors */ + uclong rom_bus_descr; /* 38h : ROM Bus Descriptor */ + uclong cs_base[4]; /* 3C-48h : Chip Select Base Addrs */ + uclong intr_ctrl_stat; /* 4Ch : Interrupt Control/Status */ + uclong init_ctrl; /* 50h : EEPROM ctrl, Init Ctrl, etc */ +}; + +#define PLX_9050_LINT1_ENABLE 0x01 +#define PLX_9050_LINT1_POL 0x02 +#define PLX_9050_LINT1_STATUS 0x04 +#define PLX_9050_LINT2_ENABLE 0x08 +#define PLX_9050_LINT2_POL 0x10 +#define PLX_9050_LINT2_STATUS 0x20 +#define PLX_9050_INTR_ENABLE 0x40 +#define PLX_9050_SW_INTR 0x80 + +/* Masks to access the init_ctrl PLX register */ +#define PC300_CLKSEL_MASK (0x00000004UL) +#define PC300_CHMEDIA_MASK(chan) (0x00000020UL<<(chan*3)) +#define PC300_CTYPE_MASK (0x00000800UL) + +/* CPLD Registers (base addr = falcbase, TE only) */ +/* CPLD v. 0 */ +#define CPLD_REG1 0x140 /* Chip resets, DCD/CTS status */ +#define CPLD_REG2 0x144 /* Clock enable , LED control */ +/* CPLD v. 2 or higher */ +#define CPLD_V2_REG1 0x100 /* Chip resets, DCD/CTS status */ +#define CPLD_V2_REG2 0x104 /* Clock enable , LED control */ +#define CPLD_ID_REG 0x108 /* CPLD version */ + +/* CPLD Register bit description: for the FALC bits, they should always be + set based on the channel (use (bit<<(2*ch)) to access the correct bit for + that channel) */ +#define CPLD_REG1_FALC_RESET 0x01 +#define CPLD_REG1_SCA_RESET 0x02 +#define CPLD_REG1_GLOBAL_CLK 0x08 +#define CPLD_REG1_FALC_DCD 0x10 +#define CPLD_REG1_FALC_CTS 0x20 + +#define CPLD_REG2_FALC_TX_CLK 0x01 +#define CPLD_REG2_FALC_RX_CLK 0x02 +#define CPLD_REG2_FALC_LED1 0x10 +#define CPLD_REG2_FALC_LED2 0x20 + +/* Structure with FALC-related fields (TE only) */ +#define PC300_FALC_MAXLOOP 0x0000ffff /* for falc_issue_cmd() */ + +typedef struct falc { + ucchar sync; /* If true FALC is synchronized */ + ucchar active; /* if TRUE then already active */ + ucchar loop_active; /* if TRUE a line loopback UP was received */ + ucchar loop_gen; /* if TRUE a line loopback UP was issued */ + + ucchar num_channels; + ucchar offset; /* 1 for T1, 0 for E1 */ + ucchar full_bandwidth; + + ucchar xmb_cause; + ucchar multiframe_mode; + + /* Statistics */ + ucshort pden; /* Pulse Density violation count */ + ucshort los; /* Loss of Signal count */ + ucshort losr; /* Loss of Signal recovery count */ + ucshort lfa; /* Loss of frame alignment count */ + ucshort farec; /* Frame Alignment Recovery count */ + ucshort lmfa; /* Loss of multiframe alignment count */ + ucshort ais; /* Remote Alarm indication Signal count */ + ucshort sec; /* One-second timer */ + ucshort es; /* Errored second */ + ucshort rai; /* remote alarm received */ + ucshort bec; + ucshort fec; + ucshort cvc; + ucshort cec; + ucshort ebc; + + /* Status */ + ucchar red_alarm; + ucchar blue_alarm; + ucchar loss_fa; + ucchar yellow_alarm; + ucchar loss_mfa; + ucchar prbs; +} falc_t; + +typedef struct falc_status { + ucchar sync; /* If true FALC is synchronized */ + ucchar red_alarm; + ucchar blue_alarm; + ucchar loss_fa; + ucchar yellow_alarm; + ucchar loss_mfa; + ucchar prbs; +} falc_status_t; + +typedef struct rsv_x21_status { + ucchar dcd; + ucchar dsr; + ucchar cts; + ucchar rts; + ucchar dtr; +} rsv_x21_status_t; + +typedef struct pc300stats { + int hw_type; + uclong line_on; + uclong line_off; + struct net_device_stats gen_stats; + falc_t te_stats; +} pc300stats_t; + +typedef struct pc300status { + int hw_type; + rsv_x21_status_t gen_status; + falc_status_t te_status; +} pc300status_t; + +typedef struct pc300loopback { + char loop_type; + char loop_on; +} pc300loopback_t; + +typedef struct pc300patterntst { + char patrntst_on; /* 0 - off; 1 - on; 2 - read num_errors */ + ucshort num_errors; +} pc300patterntst_t; + +typedef struct pc300dev { + void *if_ptr; /* General purpose pointer */ + struct pc300ch *chan; + ucchar trace_on; + uclong line_on; /* DCD(X.21, RSV) / sync(TE) change counters */ + uclong line_off; +#ifdef __KERNEL__ + char name[16]; + hdlc_device *hdlc; + + void *private; + struct sk_buff *tx_skb; + union { /* This union has all the protocol-specific structures */ + struct ppp_device pppdev; + }ifu; +#ifdef CONFIG_PC300_MLPPP + void *cpc_tty; /* information to PC300 TTY driver */ +#endif +#endif /* __KERNEL__ */ +}pc300dev_t; + +typedef struct pc300hw { + int type; /* RSV, X21, etc. */ + int bus; /* Bus (PCI, PMC, etc.) */ + int nchan; /* number of channels */ + int irq; /* interrupt request level */ + uclong clock; /* Board clock */ + ucchar cpld_id; /* CPLD ID (TE only) */ + ucshort cpld_reg1; /* CPLD reg 1 (TE only) */ + ucshort cpld_reg2; /* CPLD reg 2 (TE only) */ + ucshort gpioc_reg; /* PLX GPIOC reg */ + ucshort intctl_reg; /* PLX Int Ctrl/Status reg */ + uclong iophys; /* PLX registers I/O base */ + uclong iosize; /* PLX registers I/O size */ + uclong plxphys; /* PLX registers MMIO base (physical) */ + uclong plxbase; /* PLX registers MMIO base (virtual) */ + uclong plxsize; /* PLX registers MMIO size */ + uclong scaphys; /* SCA registers MMIO base (physical) */ + uclong scabase; /* SCA registers MMIO base (virtual) */ + uclong scasize; /* SCA registers MMIO size */ + uclong ramphys; /* On-board RAM MMIO base (physical) */ + uclong rambase; /* On-board RAM MMIO base (virtual) */ + uclong alloc_ramsize; /* RAM MMIO size allocated by the PCI bridge */ + uclong ramsize; /* On-board RAM MMIO size */ + uclong falcphys; /* FALC registers MMIO base (physical) */ + uclong falcbase; /* FALC registers MMIO base (virtual) */ + uclong falcsize; /* FALC registers MMIO size */ +} pc300hw_t; + +typedef struct pc300chconf { + sync_serial_settings phys_settings; /* Clock type/rate (in bps), + loopback mode */ + raw_hdlc_proto proto_settings; /* Encoding, parity (CRC) */ + uclong media; /* HW media (RS232, V.35, etc.) */ + uclong proto; /* Protocol (PPP, X.25, etc.) */ + ucchar monitor; /* Monitor mode (0 = off, !0 = on) */ + + /* TE-specific parameters */ + ucchar lcode; /* Line Code (AMI, B8ZS, etc.) */ + ucchar fr_mode; /* Frame Mode (ESF, D4, etc.) */ + ucchar lbo; /* Line Build Out */ + ucchar rx_sens; /* Rx Sensitivity (long- or short-haul) */ + uclong tslot_bitmap; /* bit[i]=1 => timeslot _i_ is active */ +} pc300chconf_t; + +typedef struct pc300ch { + struct pc300 *card; + int channel; + pc300dev_t d; + pc300chconf_t conf; + ucchar tx_first_bd; /* First TX DMA block descr. w/ data */ + ucchar tx_next_bd; /* Next free TX DMA block descriptor */ + ucchar rx_first_bd; /* First free RX DMA block descriptor */ + ucchar rx_last_bd; /* Last free RX DMA block descriptor */ + ucchar nfree_tx_bd; /* Number of free TX DMA block descriptors */ + falc_t falc; /* FALC structure (TE only) */ +} pc300ch_t; + +typedef struct pc300 { + pc300hw_t hw; /* hardware config. */ + pc300ch_t chan[PC300_MAXCHAN]; +#ifdef __KERNEL__ + spinlock_t card_lock; +#endif /* __KERNEL__ */ +} pc300_t; + +typedef struct pc300conf { + pc300hw_t hw; + pc300chconf_t conf; +} pc300conf_t; + +/* DEV ioctl() commands */ +#define N_SPPP_IOCTLS 2 + +enum pc300_ioctl_cmds { + SIOCCPCRESERVED = (SIOCDEVPRIVATE + N_SPPP_IOCTLS), + SIOCGPC300CONF, + SIOCSPC300CONF, + SIOCGPC300STATUS, + SIOCGPC300FALCSTATUS, + SIOCGPC300UTILSTATS, + SIOCGPC300UTILSTATUS, + SIOCSPC300TRACE, + SIOCSPC300LOOPBACK, + SIOCSPC300PATTERNTEST, +}; + +/* Loopback types - PC300/TE boards */ +enum pc300_loopback_cmds { + PC300LOCLOOP = 1, + PC300REMLOOP, + PC300PAYLOADLOOP, + PC300GENLOOPUP, + PC300GENLOOPDOWN, +}; + +/* Control Constant Definitions */ +#define PC300_RSV 0x01 +#define PC300_X21 0x02 +#define PC300_TE 0x03 + +#define PC300_PCI 0x00 +#define PC300_PMC 0x01 + +#define PC300_LC_AMI 0x01 +#define PC300_LC_B8ZS 0x02 +#define PC300_LC_NRZ 0x03 +#define PC300_LC_HDB3 0x04 + +/* Framing (T1) */ +#define PC300_FR_ESF 0x01 +#define PC300_FR_D4 0x02 +#define PC300_FR_ESF_JAPAN 0x03 + +/* Framing (E1) */ +#define PC300_FR_MF_CRC4 0x04 +#define PC300_FR_MF_NON_CRC4 0x05 +#define PC300_FR_UNFRAMED 0x06 + +#define PC300_LBO_0_DB 0x00 +#define PC300_LBO_7_5_DB 0x01 +#define PC300_LBO_15_DB 0x02 +#define PC300_LBO_22_5_DB 0x03 + +#define PC300_RX_SENS_SH 0x01 +#define PC300_RX_SENS_LH 0x02 + +#define PC300_TX_TIMEOUT (2*HZ) +#define PC300_TX_QUEUE_LEN 100 +#define PC300_DEF_MTU 1600 + +#ifdef __KERNEL__ +/* Function Prototypes */ +int dma_buf_write(pc300_t *, int, ucchar *, int); +int dma_buf_read(pc300_t *, int, struct sk_buff *); +void tx_dma_start(pc300_t *, int); +void rx_dma_start(pc300_t *, int); +void tx_dma_stop(pc300_t *, int); +void rx_dma_stop(pc300_t *, int); +int cpc_queue_xmit(struct sk_buff *, struct net_device *); +void cpc_net_rx(hdlc_device *); +void cpc_sca_status(pc300_t *, int); +int cpc_change_mtu(struct net_device *, int); +int cpc_ioctl(struct net_device *, struct ifreq *, int); +int ch_config(pc300dev_t *); +int rx_config(pc300dev_t *); +int tx_config(pc300dev_t *); +void cpc_opench(pc300dev_t *); +void cpc_closech(pc300dev_t *); +int cpc_open(struct net_device *dev); +int cpc_close(struct net_device *dev); +int cpc_set_media(hdlc_device *, int); +#endif /* __KERNEL__ */ + +#endif /* _PC300_H */ + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wan/pc300_tty.c linux.21rc1-ac2/drivers/net/wan/pc300_tty.c --- linux.21rc1/drivers/net/wan/pc300_tty.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wan/pc300_tty.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,1122 @@ +/* + * pc300_tty.c Cyclades-PC300(tm) TTY Driver. + * + * Author: Regina Kodato + * + * Copyright: (c) 1999-2002 Cyclades 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TTY includes */ +#include +#include +#include + +#include "pc300.h" + +/* defines and macros */ +/* TTY Global definitions */ +#define CPC_TTY_NPORTS 8 /* maximum number of the sync tty connections */ +#define CPC_TTY_MAJOR CYCLADES_MAJOR +#define CPC_TTY_MINOR_START 240 /* minor of the first PC300 interface */ + +#define CPC_TTY_MAX_MTU 2000 + +/* tty interface state */ +#define CPC_TTY_ST_IDLE 0 +#define CPC_TTY_ST_INIT 1 /* configured with MLPPP and up */ +#define CPC_TTY_ST_OPEN 2 /* opened by application */ + +#define CPC_TTY_LOCK(card,flags)\ + do {\ + spin_lock_irqsave(&card->card_lock, flags); \ + } while (0) + +#define CPC_TTY_UNLOCK(card,flags) \ + do {\ + spin_unlock_irqrestore(&card->card_lock, flags); \ + } while (0) + +//#define CPC_TTY_DBG(format,a...) printk(format,##a) +#define CPC_TTY_DBG(format,a...) + +/* data structures */ +typedef struct _st_cpc_rx_buf { + struct _st_cpc_rx_buf *next; + int size; + unsigned char data[1]; +} st_cpc_rx_buf; + +struct st_cpc_rx_list { + st_cpc_rx_buf *first; + st_cpc_rx_buf *last; +}; + +typedef struct _st_cpc_tty_area { + int state; /* state of the TTY interface */ + int num_open; + unsigned int tty_minor; /* minor this interface */ + volatile struct st_cpc_rx_list buf_rx; /* ptr. to reception buffer */ + unsigned char* buf_tx; /* ptr. to transmission buffer */ + pc300dev_t* pc300dev; /* ptr. to info struct in PC300 driver */ + unsigned char name[20]; /* interf. name + "-tty" */ + struct tty_struct *tty; + struct tq_struct tty_tx_task_queue; /* tx task - tx interrupt */ + struct tq_struct tty_rx_task_queue; /* rx task - rx interrupt */ + } st_cpc_tty_area; + +/* TTY data structures */ +static struct tty_struct *cpc_tty_serial_table[CPC_TTY_NPORTS]; +static struct termios *cpc_tty_serial_termios[CPC_TTY_NPORTS]; +static struct termios *cpc_tty_serial_termios_locked[CPC_TTY_NPORTS]; +static struct tty_driver serial_drv, callout_drv; + +/* local variables */ +st_cpc_tty_area cpc_tty_area[CPC_TTY_NPORTS]; + +int cpc_tty_cnt=0; /* number of intrfaces configured with MLPPP */ +int cpc_tty_refcount; +int cpc_tty_unreg_flag = 0; + +/* TTY functions prototype */ +static int cpc_tty_open(struct tty_struct *tty, struct file *flip); +static void cpc_tty_close(struct tty_struct *tty, struct file *flip); +static int cpc_tty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count); +static int cpc_tty_write_room(struct tty_struct *tty); +static int cpc_tty_chars_in_buffer(struct tty_struct *tty); +static int cpc_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +static void cpc_tty_flush_buffer(struct tty_struct *tty); +static void cpc_tty_hangup(struct tty_struct *tty); +static void cpc_tty_rx_task(void *data); +static void cpc_tty_tx_task(void *data); +static int cpc_tty_send_to_card(pc300dev_t *dev,void *buf, int len); +static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx); +static void cpc_tty_dtr_off(pc300dev_t *pc300dev); +static void cpc_tty_dtr_on(pc300dev_t *pc300dev); + +/* functions called by PC300 driver */ +void cpc_tty_init(pc300dev_t *dev); +void cpc_tty_unregister_service(pc300dev_t *pc300dev); +void cpc_tty_receive(pc300dev_t *pc300dev); +void cpc_tty_trigger_poll(pc300dev_t *pc300dev); +void cpc_tty_reset_var(void); + +/* + * PC300 TTY clear DTR signal + */ +static void cpc_tty_dtr_off(pc300dev_t *pc300dev) +{ + pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; + pc300_t *card = (pc300_t *) pc300chan->card; + int ch = pc300chan->channel; + unsigned long flags; + + CPC_TTY_DBG("%s-tty: Clear signal DTR\n", + ((struct net_device*)(pc300dev->hdlc))->name); + CPC_TTY_LOCK(card, flags); + cpc_writeb(card->hw.scabase + M_REG(CTL,ch), + cpc_readb(card->hw.scabase+M_REG(CTL,ch))& CTL_DTR); + CPC_TTY_UNLOCK(card,flags); +} + +/* + * PC300 TTY set DTR signal to ON + */ +static void cpc_tty_dtr_on(pc300dev_t *pc300dev) +{ + pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; + pc300_t *card = (pc300_t *) pc300chan->card; + int ch = pc300chan->channel; + unsigned long flags; + + CPC_TTY_DBG("%s-tty: Set signal DTR\n", + ((struct net_device*)(pc300dev->hdlc))->name); + CPC_TTY_LOCK(card, flags); + cpc_writeb(card->hw.scabase + M_REG(CTL,ch), + cpc_readb(card->hw.scabase+M_REG(CTL,ch))& ~CTL_DTR); + CPC_TTY_UNLOCK(card,flags); +} + +/* + * PC300 TTY initialization routine + * + * This routine is called by the PC300 driver during board configuration + * (ioctl=SIOCSP300CONF). At this point the adapter is completely + * initialized. + * o verify kernel version (only 2.4.x) + * o register TTY driver + * o init cpc_tty_area struct + */ +void cpc_tty_init(pc300dev_t *pc300dev) +{ + int port, aux; + st_cpc_tty_area * cpc_tty; + + if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) { + printk("%s-tty: Error: TTY driver is supported on 2.4.X kernel!\n", + ((struct net_device*)(pc300dev->hdlc))->name); + return; + } + + /* hdlcX - X=interface number */ + port = ((struct net_device*)(pc300dev->hdlc))->name[4] - '0'; + if (port >= CPC_TTY_NPORTS) { + printk("%s-tty: invalid interface selected (0-%i): %i", + ((struct net_device*)(pc300dev->hdlc))->name, + CPC_TTY_NPORTS-1,port); + return; + } + + if (cpc_tty_cnt == 0) { /* first TTY connection -> register driver */ + CPC_TTY_DBG("%s-tty: driver init, major:%i, minor range:%i=%i\n", + ((struct net_device*)(pc300dev->hdlc))->name, + CPC_TTY_MAJOR, CPC_TTY_MINOR_START, + CPC_TTY_MINOR_START+CPC_TTY_NPORTS); + /* initialize tty driver struct */ + memset(&serial_drv,0,sizeof(struct tty_driver)); + serial_drv.magic = TTY_DRIVER_MAGIC; + serial_drv.driver_name = "pc300_tty"; + serial_drv.name = "ttyCP"; + serial_drv.major = CPC_TTY_MAJOR; + serial_drv.minor_start = CPC_TTY_MINOR_START; + serial_drv.num = CPC_TTY_NPORTS; + serial_drv.type = TTY_DRIVER_TYPE_SERIAL; + serial_drv.subtype = SERIAL_TYPE_NORMAL; + + serial_drv.init_termios = tty_std_termios; + serial_drv.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + serial_drv.flags = TTY_DRIVER_REAL_RAW; + serial_drv.refcount = &cpc_tty_refcount; + + /* tty data structures */ + serial_drv.table = cpc_tty_serial_table; + serial_drv.termios = cpc_tty_serial_termios; + serial_drv.termios_locked = cpc_tty_serial_termios_locked; + + /* interface routines from the upper tty layer to the tty driver */ + serial_drv.open = cpc_tty_open; + serial_drv.close = cpc_tty_close; + serial_drv.write = cpc_tty_write; + serial_drv.write_room = cpc_tty_write_room; + serial_drv.chars_in_buffer = cpc_tty_chars_in_buffer; + serial_drv.ioctl = cpc_tty_ioctl; + serial_drv.flush_buffer = cpc_tty_flush_buffer; + serial_drv.hangup = cpc_tty_hangup; + + /* the callout device is just like normal device except for major */ + /* number and the subtype code */ + callout_drv = serial_drv; + callout_drv.name = "cucp"; + callout_drv.major = CPC_TTY_MAJOR + 1; + callout_drv.subtype = SERIAL_TYPE_CALLOUT; + callout_drv.read_proc = 0; + callout_drv.proc_entry = 0; + + /* register the TTY driver */ + if (tty_register_driver(&serial_drv)) { + printk("%s-tty: Failed to register serial driver! ", + ((struct net_device*)(pc300dev->hdlc))->name); + return; + } + + if (tty_register_driver(&callout_drv)) { + CPC_TTY_DBG("%s-tty: Failed to register callout driver! ", + ((struct net_device*)(pc300dev->hdlc))->name); + return; + } + memset((void *)cpc_tty_area, 0, + sizeof(st_cpc_tty_area) * CPC_TTY_NPORTS); + } + + cpc_tty = &cpc_tty_area[port]; + + if (cpc_tty->state != CPC_TTY_ST_IDLE) { + CPC_TTY_DBG("%s-tty: TTY port %i, already in use.\n", + ((struct net_device*)(pc300dev->hdlc))->name,port); + return; + } + + cpc_tty_cnt++; + cpc_tty->state = CPC_TTY_ST_INIT; + cpc_tty->num_open= 0; + cpc_tty->tty_minor = port + CPC_TTY_MINOR_START; + cpc_tty->pc300dev = pc300dev; + + cpc_tty->tty_tx_task_queue.routine = cpc_tty_tx_task; + cpc_tty->tty_tx_task_queue.data = (void *)cpc_tty; + + cpc_tty->tty_rx_task_queue.routine = cpc_tty_rx_task; + cpc_tty->tty_rx_task_queue.data = (void *) port; + + cpc_tty->buf_rx.first = cpc_tty->buf_rx.last = 0; + + pc300dev->cpc_tty = (void *)cpc_tty; + + aux = strlen(((struct net_device*)(pc300dev->hdlc))->name); + memcpy(cpc_tty->name,((struct net_device*)(pc300dev->hdlc))->name,aux); + memcpy(&cpc_tty->name[aux], "-tty", 5); + + cpc_open((struct net_device *)pc300dev->hdlc); + cpc_tty_dtr_off(pc300dev); + + CPC_TTY_DBG("%s: Initializing TTY Sync Driver, tty major#%d minor#%i\n", + cpc_tty->name,CPC_TTY_MAJOR,cpc_tty->tty_minor); + return; +} + +/* + * PC300 TTY OPEN routine + * + * This routine is called by the tty driver to open the interface + * o verify minor + * o allocate buffer to Rx and Tx + */ +static int cpc_tty_open(struct tty_struct *tty, struct file *flip) +{ + int port ; + st_cpc_tty_area *cpc_tty; + + if (!tty) { + return -ENODEV; + } + + port = MINOR(tty->device) - tty->driver.minor_start; + + if ((port < 0) || (port >= CPC_TTY_NPORTS)){ + CPC_TTY_DBG("pc300_tty: open invalid minor %i\n",MINOR(tty->device)); + return -ENODEV; + } + + cpc_tty = &cpc_tty_area[port]; + + if (cpc_tty->state == CPC_TTY_ST_IDLE){ + CPC_TTY_DBG("%s: open - invalid interface, minor=%i\n", + cpc_tty->name, MINOR(tty->device)); + return -ENODEV; + } + + if (cpc_tty->num_open == 0) { /* first open of this tty */ + if (!cpc_tty_area[port].buf_tx){ + cpc_tty_area[port].buf_tx = kmalloc(CPC_TTY_MAX_MTU,GFP_KERNEL); + if (cpc_tty_area[port].buf_tx == 0){ + CPC_TTY_DBG("%s: error in memory allocation\n",cpc_tty->name); + return -ENOMEM; + } + } + + if (cpc_tty_area[port].buf_rx.first) { + unsigned char * aux; + while (cpc_tty_area[port].buf_rx.first) { + aux = (unsigned char *)cpc_tty_area[port].buf_rx.first; + cpc_tty_area[port].buf_rx.first = cpc_tty_area[port].buf_rx.first->next; + kfree(aux); + } + cpc_tty_area[port].buf_rx.first = NULL; + cpc_tty_area[port].buf_rx.last = NULL; + } + + cpc_tty_area[port].state = CPC_TTY_ST_OPEN; + cpc_tty_area[port].tty = tty; + tty->driver_data = &cpc_tty_area[port]; + + cpc_tty_dtr_on(cpc_tty->pc300dev); + } + + cpc_tty->num_open++; + + CPC_TTY_DBG("%s: opening TTY driver\n", cpc_tty->name); + + /* avisar driver PC300 */ + return 0; +} + +/* + * PC300 TTY CLOSE routine + * + * This routine is called by the tty driver to close the interface + * o call close channel in PC300 driver (cpc_closech) + * o free Rx and Tx buffers + */ + +static void cpc_tty_close(struct tty_struct *tty, struct file *flip) +{ + st_cpc_tty_area *cpc_tty; + unsigned long flags; + int res; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlx-tty: no TTY in close \n"); + return; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty)|| (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return; + } + + if (!cpc_tty->num_open) { + CPC_TTY_DBG("%s: TTY is closed\n",cpc_tty->name); + return; + } + + if (--cpc_tty->num_open > 0) { + CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name); + return; + } + + cpc_tty_dtr_off(cpc_tty->pc300dev); + + CPC_TTY_LOCK(cpc_tty->pc300dev->chan->card, flags); /* lock irq */ + cpc_tty->tty = NULL; + cpc_tty->state = CPC_TTY_ST_INIT; + CPC_TTY_UNLOCK(cpc_tty->pc300dev->chan->card, flags); /* unlock irq */ + + if (cpc_tty->buf_rx.first) { + unsigned char * aux; + while (cpc_tty->buf_rx.first) { + aux = (unsigned char *)cpc_tty->buf_rx.first; + cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next; + kfree(aux); + } + cpc_tty->buf_rx.first = NULL; + cpc_tty->buf_rx.last = NULL; + } + + if (cpc_tty->buf_tx) { + kfree(cpc_tty->buf_tx); + cpc_tty->buf_tx = NULL; + } + + CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name); + + if (!cpc_tty_refcount && cpc_tty_unreg_flag) { + cpc_tty_unreg_flag = 0; + CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name); + if ((res=tty_unregister_driver(&serial_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + if ((res=tty_unregister_driver(&callout_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + } + return; +} + +/* + * PC300 TTY WRITE routine + * + * This routine is called by the tty driver to write a series of characters + * to the tty device. The characters may come from user or kernel space. + * o verify the DCD signal + * o send characters to board and start the transmission + */ +static int cpc_tty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + st_cpc_tty_area *cpc_tty; + pc300ch_t *pc300chan; + pc300_t *card; + int ch; + unsigned long flags; + struct net_device_stats *stats; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY in write\n"); + return -ENODEV; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n", cpc_tty->name); + return -ENODEV; + } + + if (count > CPC_TTY_MAX_MTU) { + CPC_TTY_DBG("%s: count is invalid\n",cpc_tty->name); + return -EINVAL; /* frame too big */ + } + + CPC_TTY_DBG("%s: cpc_tty_write %s data len=%i\n",cpc_tty->name, + (from_user)?"from user" : "from kernel",count); + + pc300chan = (pc300ch_t *)((pc300dev_t*)cpc_tty->pc300dev)->chan; + stats = &((pc300dev_t*)cpc_tty->pc300dev)->hdlc->stats; + card = (pc300_t *) pc300chan->card; + ch = pc300chan->channel; + + /* verify DCD signal*/ + if (cpc_readb(card->hw.scabase + M_REG(ST3,ch)) & ST3_DCD) { + /* DCD is OFF */ + CPC_TTY_DBG("%s : DCD is OFF\n", cpc_tty->name); + stats->tx_errors++; + stats->tx_carrier_errors++; + CPC_TTY_LOCK(card, flags); + cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR); + + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & + ~(CPLD_REG2_FALC_LED1 << (2 *ch))); + } + + CPC_TTY_UNLOCK(card, flags); + + return -EINVAL; + } + + if (from_user) { + unsigned char *buf_tmp; + + buf_tmp = cpc_tty->buf_tx; + if (copy_from_user(buf_tmp, buf, count)) { + /* failed to copy from user */ + CPC_TTY_DBG("%s: error in copy from user\n",cpc_tty->name); + return -EINVAL; + } + + if (cpc_tty_send_to_card(cpc_tty->pc300dev, (void*) buf_tmp,count)) { + /* failed to send */ + CPC_TTY_DBG("%s: transmission error\n",cpc_tty->name); + return 0; + } + } else { + if (cpc_tty_send_to_card(cpc_tty->pc300dev, (void*)buf, count)) { + /* failed to send */ + CPC_TTY_DBG("%s: trasmition error\n", cpc_tty->name); + return 0; + } + } + return count; +} + +/* + * PC300 TTY Write Room routine + * + * This routine returns the numbers of characteres the tty driver will accept + * for queuing to be written. + * o return MTU + */ +static int cpc_tty_write_room(struct tty_struct *tty) +{ + st_cpc_tty_area *cpc_tty; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY to write room\n"); + return -ENODEV; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return -ENODEV; + } + + CPC_TTY_DBG("%s: write room\n",cpc_tty->name); + + return CPC_TTY_MAX_MTU; +} + +/* + * PC300 TTY chars in buffer routine + * + * This routine returns the chars number in the transmission buffer + * o returns 0 + */ +static int cpc_tty_chars_in_buffer(struct tty_struct *tty) +{ + st_cpc_tty_area *cpc_tty; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n"); + return -ENODEV; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return -ENODEV; + } + + return(0); +} + +/* + * PC300 TTY IOCTL routine + * + * This routine treats TIOCMBIS (set DTR signal) and TIOCMBIC (clear DTR + * signal)IOCTL commands. + */ +static int cpc_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) + +{ + st_cpc_tty_area *cpc_tty; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n"); + return -ENODEV; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return -ENODEV; + } + + CPC_TTY_DBG("%s: IOCTL cmd %x\n",cpc_tty->name,cmd); + + switch (cmd) { + case TIOCMBIS : /* set DTR */ + cpc_tty_dtr_on(cpc_tty->pc300dev); + break; + + case TIOCMBIC: /* clear DTR */ + cpc_tty_dtr_off(cpc_tty->pc300dev); + break; + default : + return -ENOIOCTLCMD; + } + return 0; +} + +/* + * PC300 TTY Flush Buffer routine + * + * This routine resets the transmission buffer + */ +static void cpc_tty_flush_buffer(struct tty_struct *tty) +{ + st_cpc_tty_area *cpc_tty; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY to flush buffer\n"); + return; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return; + } + + CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name); + + wake_up_interruptible(&tty->write_wait); + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ + CPC_TTY_DBG("%s: call line disc. wake up\n",cpc_tty->name); + tty->ldisc.write_wakeup(tty); + } + + return; +} + +/* + * PC300 TTY Hangup routine + * + * This routine is called by the tty driver to hangup the interface + * o clear DTR signal + */ + +static void cpc_tty_hangup(struct tty_struct *tty) +{ + st_cpc_tty_area *cpc_tty; + int res; + + if (!tty || !tty->driver_data ) { + CPC_TTY_DBG("hdlcX-tty: no TTY to hangup\n"); + return ; + } + + cpc_tty = (st_cpc_tty_area *) tty->driver_data; + + if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { + CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name); + return ; + } + if (!cpc_tty_refcount && cpc_tty_unreg_flag) { + cpc_tty_unreg_flag = 0; + CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name); + if ((res=tty_unregister_driver(&serial_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + if ((res=tty_unregister_driver(&callout_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + } + cpc_tty_dtr_off(cpc_tty->pc300dev); +} + +/* + * PC300 TTY RX task routine + * This routine treats RX task + * o verify read buffer + * o call the line disc. read + * o free memory + */ +static void cpc_tty_rx_task(void * data) +{ + int port, i, j; + st_cpc_tty_area *cpc_tty; + volatile st_cpc_rx_buf * buf; + char flags=0,flg_rx=1; + + if (cpc_tty_cnt == 0) return; + + for (i=0; (i < 4) && flg_rx ; i++) { + flg_rx = 0; + port = (int) data; + for (j=0; j < CPC_TTY_NPORTS; j++) { + cpc_tty = &cpc_tty_area[port]; + + if ((buf=cpc_tty->buf_rx.first) != 0) { + + if (cpc_tty->tty && (cpc_tty->tty->ldisc.receive_buf)) { + CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name); + cpc_tty->tty->ldisc.receive_buf(cpc_tty->tty, buf->data, + &flags, buf->size); + } + cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next; + kfree((unsigned char *)buf); + buf = cpc_tty->buf_rx.first; + flg_rx = 1; + } + if (++port == CPC_TTY_NPORTS) port = 0; + } + } +} + +/* + * PC300 TTY RX task routine + * + * This routine treats RX interrupt. + * o read all frames in card + * o verify the frame size + * o read the frame in rx buffer + */ +static void cpc_tty_rx_disc_frame(pc300ch_t *pc300chan) +{ + volatile pcsca_bd_t * ptdescr; + volatile unsigned char status; + pc300_t *card = (pc300_t *)pc300chan->card; + int ch = pc300chan->channel; + + /* dma buf read */ + ptdescr = (pcsca_bd_t *)(card->hw.rambase + + RX_BD_ADDR(ch, pc300chan->rx_first_bd)); + while (pc300chan->rx_first_bd != pc300chan->rx_last_bd) { + status = cpc_readb(&ptdescr->status); + cpc_writeb(&ptdescr->status, 0); + cpc_writeb(&ptdescr->len, 0); + pc300chan->rx_first_bd = (pc300chan->rx_first_bd + 1) & + (N_DMA_RX_BUF - 1); + if (status & DST_EOM) { + break; /* end of message */ + } + ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next)); + } +} + +void cpc_tty_receive(pc300dev_t *pc300dev) +{ + st_cpc_tty_area *cpc_tty; + pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; + pc300_t *card = (pc300_t *)pc300chan->card; + int ch = pc300chan->channel; + volatile pcsca_bd_t * ptdescr; + struct net_device_stats *stats = &pc300dev->hdlc->stats; + int rx_len, rx_aux; + volatile unsigned char status; + unsigned short first_bd = pc300chan->rx_first_bd; + st_cpc_rx_buf *new; + unsigned char dsr_rx; + + if (pc300dev->cpc_tty == NULL) { + return; + } + + dsr_rx = cpc_readb(card->hw.scabase + DSR_RX(ch)); + + cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty; + + while (1) { + rx_len = 0; + ptdescr = (pcsca_bd_t *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd)); + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + rx_len += cpc_readw(&ptdescr->len); + first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1); + if (status & DST_EOM) { + break; + } + ptdescr=(pcsca_bd_t*)(card->hw.rambase+cpc_readl(&ptdescr->next)); + } + + if (!rx_len) { + if (dsr_rx & DSR_BOF) { + /* update EDA */ + cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, pc300chan->rx_last_bd)); + } + return; + } + + if (rx_len > CPC_TTY_MAX_MTU) { + /* Free RX descriptors */ + CPC_TTY_DBG("%s: frame size is invalid.\n",cpc_tty->name); + stats->rx_errors++; + stats->rx_frame_errors++; + cpc_tty_rx_disc_frame(pc300chan); + continue; + } + + new = (st_cpc_rx_buf *) kmalloc(rx_len + sizeof(st_cpc_rx_buf), GFP_ATOMIC); + if (new == 0) { + cpc_tty_rx_disc_frame(pc300chan); + continue; + } + + /* dma buf read */ + ptdescr = (pcsca_bd_t *)(card->hw.rambase + + RX_BD_ADDR(ch, pc300chan->rx_first_bd)); + + rx_len = 0; /* counter frame size */ + + while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { + rx_aux = cpc_readw(&ptdescr->len); + if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) + || (rx_aux > BD_DEF_LEN)) { + CPC_TTY_DBG("%s: reception error\n", cpc_tty->name); + stats->rx_errors++; + if (status & DST_OVR) { + stats->rx_fifo_errors++; + } + if (status & DST_CRC) { + stats->rx_crc_errors++; + } + if ((status & (DST_RBIT | DST_SHRT | DST_ABT)) || + (rx_aux > BD_DEF_LEN)) { + stats->rx_frame_errors++; + } + /* discard remainig descriptors used by the bad frame */ + CPC_TTY_DBG("%s: reception error - discard descriptors", + cpc_tty->name); + cpc_tty_rx_disc_frame(pc300chan); + rx_len = 0; + kfree((unsigned char *)new); + break; /* read next frame - while(1) */ + } + + if (cpc_tty->state != CPC_TTY_ST_OPEN) { + /* Free RX descriptors */ + cpc_tty_rx_disc_frame(pc300chan); + stats->rx_dropped++; + rx_len = 0; + kfree((unsigned char *)new); + break; /* read next frame - while(1) */ + } + + /* read the segment of the frame */ + if (rx_aux != 0) { + memcpy_fromio((new->data + rx_len), + (void *)(card->hw.rambase + + cpc_readl(&ptdescr->ptbuf)), rx_aux); + rx_len += rx_aux; + } + cpc_writeb(&ptdescr->status,0); + cpc_writeb(&ptdescr->len, 0); + pc300chan->rx_first_bd = (pc300chan->rx_first_bd + 1) & + (N_DMA_RX_BUF -1); + if (status & DST_EOM)break; + + ptdescr = (pcsca_bd_t *) (card->hw.rambase + + cpc_readl(&ptdescr->next)); + } + /* update pointer */ + pc300chan->rx_last_bd = (pc300chan->rx_first_bd - 1) & + (N_DMA_RX_BUF - 1) ; + if (!(dsr_rx & DSR_BOF)) { + /* update EDA */ + cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), + RX_BD_ADDR(ch, pc300chan->rx_last_bd)); + } + if (rx_len != 0) { + stats->rx_bytes += rx_len; + + if (pc300dev->trace_on) { + cpc_tty_trace(pc300dev, new->data,rx_len, 'R'); + } + new->size = rx_len; + new->next = 0; + if (cpc_tty->buf_rx.first == 0) { + cpc_tty->buf_rx.first = new; + cpc_tty->buf_rx.last = new; + } else { + cpc_tty->buf_rx.last->next = new; + cpc_tty->buf_rx.last = new; + } + schedule_task(&(cpc_tty->tty_rx_task_queue)); + stats->rx_packets++; + } + } +} + +/* + * PC300 TTY TX task routine + * + * This routine treats TX interrupt. + * o if need call line discipline wakeup + * o call wake_up_interruptible + */ +static void cpc_tty_tx_task(void *data) +{ + st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *) data; + struct tty_struct *tty; + + CPC_TTY_DBG("%s: cpc_tty_tx_task init\n",cpc_tty->name); + + if ((tty = cpc_tty->tty) == 0) { + CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name); + return; + } + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ + CPC_TTY_DBG("%s:call line disc. wakeup\n",cpc_tty->name); + tty->ldisc.write_wakeup (tty); + } + + wake_up_interruptible(&tty->write_wait); +} + +/* + * PC300 TTY send to card routine + * + * This routine send data to card. + * o clear descriptors + * o write data to DMA buffers + * o start the transmission + */ +static int cpc_tty_send_to_card(pc300dev_t *dev,void* buf, int len) +{ + pc300ch_t *chan = (pc300ch_t *)dev->chan; + pc300_t *card = (pc300_t *)chan->card; + int ch = chan->channel; + struct net_device_stats *stats = &dev->hdlc->stats; + unsigned long flags; + volatile pcsca_bd_t * ptdescr; + int i, nchar; + int tosend = len; + int nbuf = ((len - 1)/BD_DEF_LEN) + 1; + unsigned char *pdata=buf; + + CPC_TTY_DBG("%s:cpc_tty_send_to_cars len=%i", + (st_cpc_tty_area *)dev->cpc_tty->name,len); + + if (nbuf >= card->chan[ch].nfree_tx_bd) { + return 1; + } + + /* write buffer to DMA buffers */ + CPC_TTY_DBG("%s: call dma_buf_write\n", + (st_cpc_tty_area *)dev->cpc_tty->name); + for (i = 0 ; i < nbuf ; i++) { + ptdescr = (pcsca_bd_t *)(card->hw.rambase + + TX_BD_ADDR(ch, card->chan[ch].tx_next_bd)); + nchar = (BD_DEF_LEN > tosend) ? tosend : BD_DEF_LEN; + if (cpc_readb(&ptdescr->status) & DST_OSB) { + memcpy_toio((void *)(card->hw.rambase + + cpc_readl(&ptdescr->ptbuf)), + &pdata[len - tosend], + nchar); + card->chan[ch].nfree_tx_bd--; + if ((i + 1) == nbuf) { + /* This must be the last BD to be used */ + cpc_writeb(&ptdescr->status, DST_EOM); + } else { + cpc_writeb(&ptdescr->status, 0); + } + cpc_writew(&ptdescr->len, nchar); + } else { + CPC_TTY_DBG("%s: error in dma_buf_write\n", + (st_cpc_tty_area *)dev->cpc_tty->name); + stats->tx_dropped++; + return 1; + } + tosend -= nchar; + card->chan[ch].tx_next_bd = + (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1); + } + + if (dev->trace_on) { + cpc_tty_trace(dev, buf, len,'T'); + } + + /* start transmission */ + CPC_TTY_DBG("%s: start transmission\n", + (st_cpc_tty_area *)dev->cpc_tty->name); + + CPC_TTY_LOCK(card, flags); + cpc_writeb(card->hw.scabase + DTX_REG(EDAL, ch), + TX_BD_ADDR(ch, chan->tx_next_bd)); + cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_ENA); + cpc_writeb(card->hw.scabase + DSR_TX(ch), DSR_DE); + + if (card->hw.type == PC300_TE) { + cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, + cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) | + (CPLD_REG2_FALC_LED1 << (2 * ch))); + } + CPC_TTY_UNLOCK(card, flags); + return 0; +} + +/* + * PC300 TTY trace routine + * + * This routine send trace of connection to application. + * o clear descriptors + * o write data to DMA buffers + * o start the transmission + */ + +static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx) +{ + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(10 + len)) == NULL) { + /* out of memory */ + CPC_TTY_DBG("%s: tty_trace - out of memory\n", + ((struct net_device *)(dev->hdlc))->name); + return; + } + + skb_put (skb, 10 + len); + skb->dev = (struct net_device *) dev->hdlc; + skb->protocol = htons(ETH_P_CUST); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + skb->len = 10 + len; + + memcpy(skb->data,((struct net_device *)(dev->hdlc))->name,5); + skb->data[5] = '['; + skb->data[6] = rxtx; + skb->data[7] = ']'; + skb->data[8] = ':'; + skb->data[9] = ' '; + memcpy(&skb->data[10], buf, len); + netif_rx(skb); +} + +/* + * PC300 TTY unregister service routine + * + * This routine unregister one interface. + */ +void cpc_tty_unregister_service(pc300dev_t *pc300dev) +{ + st_cpc_tty_area *cpc_tty; + ulong flags; + int res; + + if ((cpc_tty= (st_cpc_tty_area *) pc300dev->cpc_tty) == 0) { + CPC_TTY_DBG("%s: interface is not TTY\n", + ((struct net_device *)(pc300dev->hdlc))->name); + return; + } + CPC_TTY_DBG("%s: cpc_tty_unregister_service", cpc_tty->name); + + if (cpc_tty->pc300dev != pc300dev) { + CPC_TTY_DBG("%s: invalid tty ptr=%s\n", + ((struct net_device *)(pc300dev->hdlc))->name, cpc_tty->name); + return; + } + + if (--cpc_tty_cnt == 0) { + if (cpc_tty_refcount) { + CPC_TTY_DBG("%s: unregister is not possible, refcount=%d", + cpc_tty->name, cpc_tty_refcount); + cpc_tty_cnt++; + cpc_tty_unreg_flag = 1; + return; + } else { + CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name); + if ((res=tty_unregister_driver(&serial_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + if ((res=tty_unregister_driver(&callout_drv))) { + CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n", + cpc_tty->name,res); + } + } + } + CPC_TTY_LOCK(pc300dev->chan->card,flags); + cpc_tty->tty = NULL; + CPC_TTY_UNLOCK(pc300dev->chan->card, flags); + cpc_tty->tty_minor = 0; + cpc_tty->state = CPC_TTY_ST_IDLE; +} + +/* + * PC300 TTY trigger poll routine + * This routine is called by pc300driver to treats Tx interrupt. + */ +void cpc_tty_trigger_poll(pc300dev_t *pc300dev) +{ + st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty; + if (!cpc_tty) { + return; + } + schedule_task(&(cpc_tty->tty_tx_task_queue)); +} + +/* + * PC300 TTY reset var routine + * This routine is called by pc300driver to init the TTY area. + */ + +void cpc_tty_reset_var(void) +{ + int i ; + + CPC_TTY_DBG("hdlcX-tty: reset variables\n"); + /* reset the tty_driver structure - serial_drv */ + memset(&serial_drv, 0, sizeof(struct tty_driver)); + memset(&callout_drv, 0, sizeof(struct tty_driver)); + for (i=0; i < CPC_TTY_NPORTS; i++){ + memset(&cpc_tty_area[i],0, sizeof(st_cpc_tty_area)); + } +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/airport.c linux.21rc1-ac2/drivers/net/wireless/airport.c --- linux.21rc1/drivers/net/wireless/airport.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/airport.c 2003-04-23 14:29:39.000000000 +0100 @@ -1,4 +1,4 @@ -/* airport.c 0.13b +/* airport.c 0.13d * * A driver for "Hermes" chipset based Apple Airport wireless * card. @@ -95,7 +95,7 @@ netif_device_detach(dev); - priv->hw_unavailable = 1; + priv->hw_unavailable++; orinoco_unlock(priv, &flags); @@ -121,14 +121,15 @@ netif_device_attach(dev); - if (priv->open) { + priv->hw_unavailable--; + + if (priv->open && (! priv->hw_unavailable)) { err = __orinoco_up(dev); if (err) printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n", dev->name, err); } - priv->hw_unavailable = 0; spin_unlock_irqrestore(&priv->lock, flags); @@ -140,8 +141,21 @@ static int airport_hard_reset(struct orinoco_private *priv) { + /* It would be nice to power cycle the Airport for a real hard + * reset, but for some reason although it appears to + * re-initialize properly, it falls in a screaming heap + * shortly afterwards. */ +#if 0 + struct net_device *dev = priv->ndev; struct airport *card = priv->card; + /* Vitally important. If we don't do this it seems we get an + * interrupt somewhere during the power cycle, since + * hw_unavailable is already set it doesn't get ACKed, we get + * into an interrupt loop and the the PMU decides to turn us + * off. */ + disable_irq(dev->irq); + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0); current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ); @@ -149,6 +163,10 @@ current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ); + enable_irq(dev->irq); + schedule_timeout(HZ); +#endif + return 0; } @@ -209,7 +227,7 @@ /* Reset it before we get the interrupt */ hermes_init(hw); - if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) { + if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", dev)) { printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq); goto failed; } @@ -251,7 +269,7 @@ card->ndev_registered = 0; if (card->irq_requested) - free_irq(dev->irq, priv); + free_irq(dev->irq, dev); card->irq_requested = 0; if (card->vaddr) @@ -269,11 +287,10 @@ kfree(dev); } /* airport_detach */ -static char version[] __initdata = "airport.c 0.13b (Benjamin Herrenschmidt )"; +static char version[] __initdata = "airport.c 0.13d (Benjamin Herrenschmidt )"; MODULE_AUTHOR("Benjamin Herrenschmidt "); MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); MODULE_LICENSE("Dual MPL/GPL"); -EXPORT_NO_SYMBOLS; static int __init init_airport(void) @@ -282,15 +299,11 @@ printk(KERN_DEBUG "%s\n", version); - MOD_INC_USE_COUNT; - /* Lookup card in device tree */ airport_node = find_devices("radio"); if (airport_node && !strcmp(airport_node->parent->name, "mac-io")) airport_dev = airport_attach(airport_node); - MOD_DEC_USE_COUNT; - return airport_dev ? 0 : -ENODEV; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/hermes.c linux.21rc1-ac2/drivers/net/wireless/hermes.c --- linux.21rc1/drivers/net/wireless/hermes.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/hermes.c 2003-04-23 14:29:39.000000000 +0100 @@ -544,4 +544,9 @@ return 0; } +static void __exit exit_hermes(void) +{ +} + module_init(init_hermes); +module_exit(exit_hermes); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/hermes.h linux.21rc1-ac2/drivers/net/wireless/hermes.h --- linux.21rc1/drivers/net/wireless/hermes.h 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/hermes.h 2003-04-23 14:29:39.000000000 +0100 @@ -250,7 +250,6 @@ u16 scanreason; /* ??? */ struct hermes_scan_apinfo aps[35]; /* Scan result */ } __attribute__ ((packed)); - #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) #define HERMES_LINKSTATUS_CONNECTED (0x0001) #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) @@ -368,7 +367,7 @@ if (hw->io_space) { insw(hw->iobase + off, buf, count); } else { - int i; + unsigned i; u16 *p; /* This needs to *not* byteswap (like insw()) but @@ -388,7 +387,7 @@ if (hw->io_space) { outsw(hw->iobase + off, buf, count); } else { - int i; + unsigned i; const u16 *p; /* This needs to *not* byteswap (like outsw()) but @@ -401,6 +400,21 @@ } } +static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count) +{ + unsigned i; + + off = off << hw->reg_spacing;; + + if (hw->io_space) { + for (i = 0; i < count; i++) + outw(0, hw->iobase + off); + } else { + for (i = 0; i < count; i++) + writew(0, hw->iobase + off); + } +} + #define HERMES_READ_RECORD(hw, bap, rid, buf) \ (hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf))) #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco.c linux.21rc1-ac2/drivers/net/wireless/orinoco.c --- linux.21rc1/drivers/net/wireless/orinoco.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco.c 2003-04-23 14:29:39.000000000 +0100 @@ -1,4 +1,4 @@ -/* orinoco.c 0.13b - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* orinoco.c 0.13d - (formerly known as dldwd_cs.c and orinoco_cs.c) * * A driver for Hermes or Prism 2 chipset based PCMCIA wireless * adaptors, with Lucent/Agere, Intersil or Symbol firmware. @@ -345,11 +345,45 @@ * we are connected (avoids cofusing the firmware), and only * give LINKSTATUS printk()s if the status has changed. * + * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson + * o Cleanup: use dev instead of priv in various places. + * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event + * if we're in the middle of a (driver initiated) hard reset. + * o Bug fix: ETH_ZLEN is supposed to include the header + * (Dionysus Blazakis & Manish Karir) + * o Convert to using workqueues instead of taskqueues (and + * backwards compatibility macros for pre 2.5.41 kernels). + * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in + * airport.c + * o New orinoco_tmd.c init module from Joerg Dorchain for + * TMD7160 based PCI to PCMCIA bridges (similar to + * orinoco_plx.c). + * + * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson + * o Make hw_unavailable a counter, rather than just a flag, this + * is necessary to avoid some races (such as a card being + * removed in the middle of orinoco_reset(). + * o Restore Release/RequestConfiguration in the PCMCIA event handler + * when dealing with a driver initiated hard reset. This is + * necessary to prevent hangs due to a spurious interrupt while + * the reset is in progress. + * o Clear the 802.11 header when transmitting, even though we + * don't use it. This fixes a long standing bug on some + * firmwares, which seem to get confused if that isn't done. + * o Be less eager to de-encapsulate SNAP frames, only do so if + * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old + * behaviour broke CDP (Cisco Discovery Protocol). + * o Use dev instead of priv for free_irq() as well as + * request_irq() (oops). + * o Attempt to reset rather than giving up if we get too many + * IRQs. + * o Changed semantics of __orinoco_down() so it can be called + * safely with hw_unavailable set. It also now clears the + * linkstatus (since we're going to have to reassociate). + * * TODO - * o New wireless extensions API (patch from Moustafa * Youssef, updated by Jim Carter). - * o Fix PCMCIA hard resets with pcmcia-cs. * o Handle de-encapsulation within network layer, provide 802.11 * headers (patch from Thomas 'Dent' Mirlacher) * o Fix possible races in SPY handling. @@ -373,7 +407,7 @@ * flag after taking the lock, and if it is set, give up on whatever * they are doing and drop the lock again. The orinoco_lock() * function handles this (it unlocks and returns -EBUSY if - * hw_unavailable is true). */ + * hw_unavailable is non-zero). */ #include @@ -522,7 +556,7 @@ /* Hardware control routines */ -static int __orinoco_program_rids(struct orinoco_private *priv); +static int __orinoco_program_rids(struct net_device *dev); static int __orinoco_hw_set_bitrate(struct orinoco_private *priv); static int __orinoco_hw_setup_wep(struct orinoco_private *priv); @@ -535,14 +569,14 @@ static void __orinoco_set_multicast_list(struct net_device *dev); /* Interrupt handling routines */ -static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); -static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); +static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw); +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw); /* ioctl() routines */ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); @@ -577,7 +611,7 @@ struct hermes *hw = &priv->hw; int err; - err = __orinoco_program_rids(priv); + err = __orinoco_program_rids(dev); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", dev->name, err); @@ -606,14 +640,25 @@ netif_stop_queue(dev); - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_ERR "%s: Error %d disabling MAC port\n", - dev->name, err); - return err; + if (! priv->hw_unavailable) { + if (! priv->broken_disableport) { + err = hermes_disable_port(hw, 0); + if (err) { + /* Some firmwares (e.g. Intersil 1.3.x) seem + * to have problems disabling the port, oh + * well, too bad. */ + printk(KERN_WARNING "%s: Error %d disabling MAC port\n", + dev->name, err); + priv->broken_disableport = 1; + } + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); } - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); + + /* firmware will have to reassociate */ + priv->last_linkstatus = 0xffff; + priv->connected = 0; return 0; } @@ -656,38 +701,38 @@ if (err) return err; - priv->open = 1; - err = __orinoco_up(dev); + if (! err) + priv->open = 1; + orinoco_unlock(priv, &flags); return err; } -static int orinoco_stop(struct net_device *dev) +int orinoco_stop(struct net_device *dev) { struct orinoco_private *priv = dev->priv; int err = 0; /* We mustn't use orinoco_lock() here, because we need to be - able to close the interface, even if hw_unavailable is set + able to close the interface even if hw_unavailable is set (e.g. as we're released after a PC Card removal) */ spin_lock_irq(&priv->lock); priv->open = 0; - if (! priv->hw_unavailable) - err = __orinoco_down(dev); + err = __orinoco_down(dev); spin_unlock_irq(&priv->lock); return err; } -static int __orinoco_program_rids(struct orinoco_private *priv) +static int __orinoco_program_rids(struct net_device *dev) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; int err; struct hermes_idstring idbuf; @@ -873,51 +918,84 @@ } /* xyzzy */ -static int orinoco_reconfigure(struct orinoco_private *priv) +static int orinoco_reconfigure(struct net_device *dev) { + struct orinoco_private *priv = dev->priv; struct hermes *hw = &priv->hw; unsigned long flags; int err = 0; - orinoco_lock(priv, &flags); + if (priv->broken_disableport) { + schedule_work(&priv->reset_work); + return 0; + } + err = orinoco_lock(priv, &flags); + if (err) + return err; + + err = hermes_disable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", - priv->ndev->name); + printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", + dev->name); + priv->broken_disableport = 1; goto out; } - err = __orinoco_program_rids(priv); - if (err) + err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); goto out; + } err = hermes_enable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", - priv->ndev->name); + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); goto out; } out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); + schedule_work(&priv->reset_work); + err = 0; + } + orinoco_unlock(priv, &flags); return err; } /* This must be called from user context, without locks held - use - * schedule_task() */ + * schedule_work() */ static void orinoco_reset(struct net_device *dev) { struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; int err; unsigned long flags; err = orinoco_lock(priv, &flags); if (err) + /* When the hardware becomes available again, whatever + * detects that is responsible for re-initializing + * it. So no need for anything further*/ return; - priv->hw_unavailable = 1; + netif_stop_queue(dev); + + /* Shut off interrupts. Depending on what state the hardware + * is in, this might not work, but we'll try anyway */ + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); + + priv->hw_unavailable++; + priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ + priv->connected = 0; + orinoco_unlock(priv, &flags); if (priv->hard_reset) @@ -936,18 +1014,22 @@ return; } - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); /* This has to be called from user context */ - priv->hw_unavailable = 0; + priv->hw_unavailable--; - err = __orinoco_up(dev); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", - dev->name, err); - } else - dev->trans_start = jiffies; + /* priv->open or priv->hw_unavailable might have changed while + * we dropped the lock */ + if (priv->open && (! priv->hw_unavailable)) { + err = __orinoco_up(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", + dev->name, err); + } else + dev->trans_start = jiffies; + } - orinoco_unlock(priv, &flags); + spin_unlock_irq(&priv->lock); return; } @@ -979,10 +1061,18 @@ } } +/* Does the frame have a SNAP header indicating it should be + * de-encapsulated to Ethernet-II? */ static inline int -is_snap(struct header_struct *hdr) +is_ethersnap(struct header_struct *hdr) { - return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); + /* We de-encapsulate all packets which, a) have SNAP headers + * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header + * and where b) the OUI of the SNAP header is 00:00:00 or + * 00:00:f8 - we need both because different APs appear to use + * different OUIs for some reason */ + return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) + && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); } static void @@ -1140,7 +1230,8 @@ return 0; } -static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]) +static int orinoco_hw_get_bssid(struct orinoco_private *priv, + char buf[ETH_ALEN]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1159,7 +1250,7 @@ } static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]) + char buf[IW_ESSID_MAX_SIZE+1]) { hermes_t *hw = &priv->hw; int err = 0; @@ -1236,9 +1327,8 @@ } if ( (channel < 1) || (channel > NUM_CHANNELS) ) { - struct net_device *dev = priv->ndev; - - printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel); + printk(KERN_WARNING "%s: Channel out of range (%d)!\n", + priv->ndev->name, channel); err = -EBUSY; goto out; @@ -1253,8 +1343,8 @@ return err ? err : freq; } -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max) +static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max) { hermes_t *hw = &priv->hw; struct hermes_idstring list; @@ -1354,9 +1444,9 @@ */ void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct orinoco_private *priv = (struct orinoco_private *) dev_id; + struct net_device *dev = (struct net_device *)dev_id; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; - struct net_device *dev = priv->ndev; int count = MAX_IRQLOOPS_PER_IRQ; u16 evstat, events; /* These are used to detect a runaway interrupt situation */ @@ -1380,11 +1470,11 @@ while (events && count--) { if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { - printk(KERN_CRIT "%s: IRQ handler is looping too \ -much! Shutting down.\n", - dev->name); - /* Perform an emergency shutdown */ + printk(KERN_WARNING "%s: IRQ handler is looping too " + "much! Resetting.\n", dev->name); + /* Disable interrupts for now */ hermes_set_irqmask(hw, 0); + schedule_work(&priv->reset_work); break; } @@ -1395,21 +1485,21 @@ } if (events & HERMES_EV_TICK) - __orinoco_ev_tick(priv, hw); + __orinoco_ev_tick(dev, hw); if (events & HERMES_EV_WTERR) - __orinoco_ev_wterr(priv, hw); + __orinoco_ev_wterr(dev, hw); if (events & HERMES_EV_INFDROP) - __orinoco_ev_infdrop(priv, hw); + __orinoco_ev_infdrop(dev, hw); if (events & HERMES_EV_INFO) - __orinoco_ev_info(priv, hw); + __orinoco_ev_info(dev, hw); if (events & HERMES_EV_RX) - __orinoco_ev_rx(priv, hw); + __orinoco_ev_rx(dev, hw); if (events & HERMES_EV_TXEXC) - __orinoco_ev_txexc(priv, hw); + __orinoco_ev_txexc(dev, hw); if (events & HERMES_EV_TX) - __orinoco_ev_tx(priv, hw); + __orinoco_ev_tx(dev, hw); if (events & HERMES_EV_ALLOC) - __orinoco_ev_alloc(priv, hw); + __orinoco_ev_alloc(dev, hw); hermes_write_regn(hw, EVACK, events); @@ -1420,22 +1510,22 @@ orinoco_unlock(priv, &flags); } -static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) { - printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name); + printk(KERN_DEBUG "%s: TICK\n", dev->name); } -static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", - priv->ndev->name); + dev->name); } -static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) { - printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name); + printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); } static void print_linkstatus(struct net_device *dev, u16 status) @@ -1472,9 +1562,9 @@ dev->name, s, status); } -static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; u16 infofid; struct { u16 len; @@ -1573,9 +1663,9 @@ } } -static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; @@ -1664,7 +1754,7 @@ * So, check ourselves */ if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || - is_snap(&hdr)) { + is_ethersnap(&hdr)) { /* These indicate a SNAP within 802.2 LLC within 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ @@ -1726,9 +1816,9 @@ return; } -static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); struct hermes_tx_descriptor desc; @@ -1752,8 +1842,9 @@ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } -static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) { + struct orinoco_private *priv = dev->priv; struct net_device_stats *stats = &priv->stats; stats->tx_packets++; @@ -1761,9 +1852,10 @@ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } -static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw) +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; + u16 fid = hermes_read_regn(hw, ALLOCFID); if (fid != priv->txfid) { @@ -1945,7 +2037,7 @@ TRACE_ENTER(dev->name); - /* No need to lock, the resetting flag is already set in + /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; @@ -2081,8 +2173,6 @@ priv->wep_on = 0; priv->tx_key = 0; - priv->hw_unavailable = 0; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO) { /* Try workaround for old Symbol firmware bug */ @@ -2102,6 +2192,12 @@ goto out; } + /* Make the hardware available, as long as it hasn't been + * removed elsewhere (e.g. by PCMCIA hot unplug) */ + spin_lock_irq(&priv->lock); + priv->hw_unavailable--; + spin_unlock_irq(&priv->lock); + printk(KERN_DEBUG "%s: ready\n", dev->name); out: @@ -2267,7 +2363,7 @@ /* Length of the packet body */ /* FIXME: what if the skb is smaller than this? */ - len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN); + len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN); eh = (struct ethhdr *)skb->data; @@ -2281,6 +2377,12 @@ goto fail; } + /* Clear the 802.11 header and data length fields - some + * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused + * if this isn't done. */ + hermes_clear_words(hw, HERMES_DATA0, + HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); + /* Encapsulate Ethernet-II frames */ if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */ struct header_struct hdr; @@ -2362,7 +2464,7 @@ stats->tx_errors++; - schedule_task(&priv->timeout_task); + schedule_work(&priv->reset_work); } static int @@ -2532,7 +2634,7 @@ } err = orinoco_hw_get_bitratelist(priv, &numrates, - range.bitrate, IW_MAX_BITRATES); + range.bitrate, IW_MAX_BITRATES); if (err) return err; range.num_bitrates = numrates; @@ -3128,7 +3230,7 @@ rrq->value = 5500000; else rrq->value = val * 1000000; - break; + break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ for (i = 0; i < BITRATE_TABLE_SIZE; i++) @@ -3754,7 +3856,7 @@ printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); - schedule_task(&priv->timeout_task); + schedule_work(&priv->reset_work); break; case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ @@ -3839,7 +3941,7 @@ } if (! err && changed && netif_running(dev)) { - err = orinoco_reconfigure(priv); + err = orinoco_reconfigure(dev); } TRACE_EXIT(dev->name); @@ -3924,7 +4026,7 @@ DEBUG_REC(PRIID,WORDS), DEBUG_REC(PRISUPRANGE,WORDS), DEBUG_REC(CFIACTRANGES,WORDS), - DEBUG_REC(NICSERNUM,WORDS), + DEBUG_REC(NICSERNUM,XSTRING), DEBUG_REC(NICID,WORDS), DEBUG_REC(MFISUPRANGE,WORDS), DEBUG_REC(CFISUPRANGE,WORDS), @@ -4062,7 +4164,7 @@ priv->hw_unavailable = 1; /* orinoco_init() must clear this * before anything else touches the * hardware */ - INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev); + INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); priv->last_linkstatus = 0xffff; priv->connected = 0; @@ -4079,13 +4181,14 @@ EXPORT_SYMBOL(__orinoco_up); EXPORT_SYMBOL(__orinoco_down); +EXPORT_SYMBOL(orinoco_stop); EXPORT_SYMBOL(orinoco_reinit_firmware); EXPORT_SYMBOL(orinoco_interrupt); /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco.c 0.13b (David Gibson and others)"; +static char version[] __initdata = "orinoco.c 0.13d (David Gibson and others)"; static int __init init_orinoco(void) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco_cs.c linux.21rc1-ac2/drivers/net/wireless/orinoco_cs.c --- linux.21rc1/drivers/net/wireless/orinoco_cs.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco_cs.c 2003-04-23 14:29:39.000000000 +0100 @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.13b - (formerly known as dldwd_cs.c) +/* orinoco_cs.c 0.13d - (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -38,7 +37,6 @@ #include #include #include -#include #include "orinoco.h" @@ -269,19 +267,12 @@ return; } - /* - If the device is currently configured and active, we won't - actually delete it yet. Instead, it is marked so that when - the release() function is called, that will trigger a proper - detach(). - */ if (link->state & DEV_CONFIG) { -#ifdef PCMCIA_DEBUG - printk(KERN_DEBUG "orinoco_cs: detach postponed, '%s' " - "still locked\n", link->dev->dev_name); -#endif - link->state |= DEV_STALE_LINK; - return; + orinoco_cs_release((u_long)link); + if (link->state & DEV_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } } /* Break the link with Card Services */ @@ -472,7 +463,7 @@ link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = orinoco_interrupt; - link->irq.Instance = priv; + link->irq.Instance = dev; CS_CHECK(RequestIRQ, link->handle, &link->irq); } @@ -549,18 +540,13 @@ dev_link_t *link = (dev_link_t *) arg; struct net_device *dev = link->priv; struct orinoco_private *priv = dev->priv; + unsigned long flags; - /* - If the device is currently in use, we won't release until it - is actually closed, because until then, we can't be sure that - no one will try to access the device or its data structures. - */ - if (priv->open) { - DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n", - link->dev->dev_name); - link->state |= DEV_STALE_CONFIG; - return; - } + /* We're committed to taking the device away now, so mark the + * hardware as unavailable */ + spin_lock_irqsave(&priv->lock, flags); + priv->hw_unavailable++; + spin_unlock_irqrestore(&priv->lock, flags); /* Don't bother checking to see if these succeed or not */ CardServices(ReleaseConfiguration, link->handle); @@ -593,14 +579,9 @@ orinoco_lock(priv, &flags); netif_device_detach(dev); - priv->hw_unavailable = 1; + priv->hw_unavailable++; orinoco_unlock(priv, &flags); - -/* if (link->open) */ -/* orinoco_cs_stop(dev); */ - - mod_timer(&link->release, jiffies + HZ / 20); } break; @@ -619,13 +600,8 @@ a better way, short of rewriting the PCMCIA layer to not suck :-( */ if (! test_bit(0, &card->hard_reset_in_progress)) { - err = orinoco_lock(priv, &flags); - if (err) { - printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n", - dev->name); - break; - } - + spin_lock_irqsave(&priv->lock, flags); + err = __orinoco_down(dev); if (err) printk(KERN_WARNING "%s: %s: Error %d downing interface\n", @@ -634,9 +610,9 @@ err); netif_device_detach(dev); - priv->hw_unavailable = 1; - - orinoco_unlock(priv, &flags); + priv->hw_unavailable++; + + spin_unlock_irqrestore(&priv->lock, flags); } CardServices(ReleaseConfiguration, link->handle); @@ -653,10 +629,6 @@ CardServices(RequestConfiguration, link->handle, &link->conf); - /* If we're only getting these events because - of the ResetCard in the hard reset, we - don't need to do anything - orinoco_reset() - will handle reinitialization. */ if (! test_bit(0, &card->hard_reset_in_progress)) { err = orinoco_reinit_firmware(dev); if (err) { @@ -668,9 +640,9 @@ spin_lock_irqsave(&priv->lock, flags); netif_device_attach(dev); - priv->hw_unavailable = 0; + priv->hw_unavailable--; - if (priv->open) { + if (priv->open && ! priv->hw_unavailable) { err = __orinoco_up(dev); if (err) printk(KERN_ERR "%s: Error %d restarting card\n", @@ -678,7 +650,7 @@ } - orinoco_unlock(priv, &flags); + spin_unlock_irqrestore(&priv->lock, flags); } } break; @@ -693,7 +665,7 @@ /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco_cs.c 0.13b (David Gibson and others)"; +static char version[] __initdata = "orinoco_cs.c 0.13d (David Gibson and others)"; static int __init init_orinoco_cs(void) @@ -722,7 +694,6 @@ if (dev_list) DEBUG(0, "orinoco_cs: Removing leftover devices.\n"); while (dev_list != NULL) { - del_timer(&dev_list->release); if (dev_list->state & DEV_CONFIG) orinoco_cs_release((u_long) dev_list); orinoco_cs_detach(dev_list); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco.h linux.21rc1-ac2/drivers/net/wireless/orinoco.h --- linux.21rc1/drivers/net/wireless/orinoco.h 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco.h 2003-04-25 16:05:08.000000000 +0100 @@ -11,9 +11,20 @@ #include #include #include -#include +#include #include "hermes.h" +/* Workqueue / task queue backwards compatibility stuff */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) +#include +#else +#include +#define work_struct tq_struct +#define INIT_WORK INIT_TQUEUE +#define schedule_work schedule_task +#endif + /* To enable debug messages */ //#define ORINOCO_DEBUG 3 @@ -42,7 +53,7 @@ /* Synchronisation stuff */ spinlock_t lock; int hw_unavailable; - struct tq_struct timeout_task; + struct work_struct reset_work; /* driver state */ int open; @@ -72,6 +83,7 @@ int has_sensitivity; int nicbuf_size; u16 channel_mask; + int broken_disableport; /* Configuration paramaters */ u32 iw_mode; @@ -111,8 +123,8 @@ int (*hard_reset)(struct orinoco_private *)); extern int __orinoco_up(struct net_device *dev); extern int __orinoco_down(struct net_device *dev); -int orinoco_reinit_firmware(struct net_device *dev); - +extern int orinoco_stop(struct net_device *dev); +extern int orinoco_reinit_firmware(struct net_device *dev); extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); /********************************************************************/ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco_pci.c linux.21rc1-ac2/drivers/net/wireless/orinoco_pci.c --- linux.21rc1/drivers/net/wireless/orinoco_pci.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco_pci.c 2003-04-23 14:29:39.000000000 +0100 @@ -1,4 +1,4 @@ -/* orinoco_pci.c 0.13b +/* orinoco_pci.c 0.13d * * Driver for Prism II devices that have a direct PCI interface * (i.e., not in a Pcmcia or PLX bridge) @@ -143,8 +143,6 @@ unsigned long timeout; u16 reg; - TRACE_ENTER(priv->ndev->name); - /* Assert the reset until the card notice */ hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); printk(KERN_NOTICE "Reset done"); @@ -181,8 +179,6 @@ } printk(KERN_NOTICE "pci_cor : reg = 0x%X - %lX - %lX\n", reg, timeout, jiffies); - TRACE_EXIT(priv->ndev->name); - return 0; } @@ -232,7 +228,7 @@ hermes_struct_init(&(priv->hw), dev->base_addr, HERMES_MEM, HERMES_32BIT_REGSPACING); pci_set_drvdata(pdev, dev); - err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv); + err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev); if (err) { printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n", pdev->irq); err = -EBUSY; @@ -264,7 +260,7 @@ unregister_netdev(dev); if (dev->irq) - free_irq(dev->irq, priv); + free_irq(dev->irq, dev); kfree(dev); } @@ -286,7 +282,7 @@ unregister_netdev(dev); if (dev->irq) - free_irq(dev->irq, priv); + free_irq(dev->irq, dev); if (priv->hw.iobase) iounmap((unsigned char *) priv->hw.iobase); @@ -321,7 +317,7 @@ netif_device_detach(dev); - priv->hw_unavailable = 1; + priv->hw_unavailable++; orinoco_unlock(priv, &flags); @@ -348,15 +344,15 @@ netif_device_attach(dev); - if (priv->open) { + priv->hw_unavailable--; + + if (priv->open && (! priv->hw_unavailable)) { err = __orinoco_up(dev); if (err) printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n", dev->name, err); } - priv->hw_unavailable = 0; - spin_unlock_irqrestore(&priv->lock, flags); return 0; @@ -378,7 +374,7 @@ .resume = orinoco_pci_resume, }; -static char version[] __initdata = "orinoco_pci.c 0.13b (David Gibson & Jean Tourrilhes )"; +static char version[] __initdata = "orinoco_pci.c 0.13d (David Gibson & Jean Tourrilhes )"; MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); MODULE_LICENSE("Dual MPL/GPL"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco_plx.c linux.21rc1-ac2/drivers/net/wireless/orinoco_plx.c --- linux.21rc1/drivers/net/wireless/orinoco_plx.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco_plx.c 2003-04-23 14:29:39.000000000 +0100 @@ -1,4 +1,4 @@ -/* orinoco_plx.c 0.13b +/* orinoco_plx.c 0.13d * * Driver for Prism II devices which would usually be driven by orinoco_cs, * but are connected to the PCI bus by a PLX9052. @@ -243,7 +243,7 @@ HERMES_IO, HERMES_16BIT_REGSPACING); pci_set_drvdata(pdev, dev); - err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv); + err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev); if (err) { printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq); err = -EBUSY; @@ -266,7 +266,7 @@ unregister_netdev(dev); if (dev->irq) - free_irq(dev->irq, priv); + free_irq(dev->irq, dev); kfree(priv); } @@ -285,7 +285,6 @@ static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = dev->priv; if (! dev) BUG(); @@ -293,7 +292,7 @@ unregister_netdev(dev); if (dev->irq) - free_irq(dev->irq, priv); + free_irq(dev->irq, dev); pci_set_drvdata(pdev, NULL); @@ -334,7 +333,7 @@ .resume = 0, }; -static char version[] __initdata = "orinoco_plx.c 0.13b (Daniel Barlow , David Gibson )"; +static char version[] __initdata = "orinoco_plx.c 0.13d (Daniel Barlow , David Gibson )"; MODULE_AUTHOR("Daniel Barlow "); MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); #ifdef MODULE_LICENSE diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/net/wireless/orinoco_tmd.c linux.21rc1-ac2/drivers/net/wireless/orinoco_tmd.c --- linux.21rc1/drivers/net/wireless/orinoco_tmd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/net/wireless/orinoco_tmd.c 2003-04-23 14:29:39.000000000 +0100 @@ -0,0 +1,240 @@ +/* orinoco_tmd.c 0.01 + * + * Driver for Prism II devices which would usually be driven by orinoco_cs, + * but are connected to the PCI bus by a TMD7160. + * + * Copyright (C) 2003 Joerg Dorchain + * based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + + * Caution: this is experimental and probably buggy. For success and + * failure reports for different cards and adaptors, see + * orinoco_tmd_pci_id_table near the end of the file. If you have a + * card we don't have the PCI id for, and looks like it should work, + * drop me mail with the id and "it works"/"it doesn't work". + * + * Note: if everything gets detected fine but it doesn't actually send + * or receive packets, your first port of call should probably be to + * try newer firmware in the card. Especially if you're doing Ad-Hoc + * modes + * + * The actual driving is done by orinoco.c, this is just resource + * allocation stuff. + * + * This driver is modeled after the orinoco_plx driver. The main + * difference is that the TMD chip has only IO port ranges and no + * memory space, i.e. no access to the CIS. Compared to the PLX chip, + * the io range functionalities are exchanged. + * + * Pheecom sells cards with the TMD chip as "ASIC version" + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hermes.h" +#include "orinoco.h" + +static char dev_info[] = "orinoco_tmd"; + +#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA | COR_FUNC_ENA) /* Enable PC card with level triggered irqs and irq requests */ + + +static int orinoco_tmd_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err = 0; + u32 reg, addr; + struct orinoco_private *priv = NULL; + unsigned long pccard_ioaddr = 0; + unsigned long pccard_iolen = 0; + struct net_device *dev = NULL; + int netdev_registered = 0; + + err = pci_enable_device(pdev); + if (err) + return -EIO; + + printk(KERN_DEBUG "TMD setup\n"); + pccard_ioaddr = pci_resource_start(pdev, 2); + pccard_iolen = pci_resource_len(pdev, 2); + if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) { + printk(KERN_ERR "orinoco_tmd: I/O resource at 0x%lx len 0x%lx busy\n", + pccard_ioaddr, pccard_iolen); + pccard_ioaddr = 0; + err = -EBUSY; + goto fail; + } + addr = pci_resource_start(pdev, 1); + outb(COR_VALUE, addr); + mdelay(1); + reg = inb(addr); + if (reg != COR_VALUE) { + printk(KERN_ERR "orinoco_tmd: Error setting TMD COR values %x should be %x\n", reg, COR_VALUE); + err = -EIO; + goto fail; + } + + dev = alloc_orinocodev(0, NULL); + if (! dev) { + err = -ENOMEM; + goto fail; + } + + priv = dev->priv; + dev->base_addr = pccard_ioaddr; + SET_MODULE_OWNER(dev); + + printk(KERN_DEBUG + "Detected Orinoco/Prism2 TMD device at %s irq:%d, io addr:0x%lx\n", + pdev->slot_name, pdev->irq, pccard_ioaddr); + + hermes_struct_init(&(priv->hw), dev->base_addr, + HERMES_IO, HERMES_16BIT_REGSPACING); + pci_set_drvdata(pdev, dev); + + err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, + dev); + if (err) { + printk(KERN_ERR "orinoco_tmd: Error allocating IRQ %d.\n", + pdev->irq); + err = -EBUSY; + goto fail; + } + dev->irq = pdev->irq; + + err = register_netdev(dev); + if (err) + goto fail; + netdev_registered = 1; + + return 0; /* succeeded */ + + fail: + printk(KERN_DEBUG "orinoco_tmd: init_one(), FAIL!\n"); + + if (priv) { + if (dev->irq) + free_irq(dev->irq, dev); + + kfree(priv); + } + + if (pccard_ioaddr) + release_region(pccard_ioaddr, pccard_iolen); + + pci_disable_device(pdev); + + return err; +} + +static void __devexit orinoco_tmd_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (! dev) + BUG(); + + unregister_netdev(dev); + + if (dev->irq) + free_irq(dev->irq, dev); + + pci_set_drvdata(pdev, NULL); + + kfree(dev); + + release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2)); + + pci_disable_device(pdev); +} + + +static struct pci_device_id orinoco_tmd_pci_id_table[] __devinitdata = { + {0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */ + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_tmd_pci_id_table); + +static struct pci_driver orinoco_tmd_driver = { + .name = "orinoco_tmd", + .id_table = orinoco_tmd_pci_id_table, + .probe = orinoco_tmd_init_one, + .remove = __devexit_p(orinoco_tmd_remove_one), + .suspend = 0, + .resume = 0, +}; + +static char version[] __initdata = "orinoco_tmd.c 0.01 (Joerg Dorchain )"; +MODULE_AUTHOR("Joerg Dorchain "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual MPL/GPL"); +#endif + +static int __init orinoco_tmd_init(void) +{ + printk(KERN_DEBUG "%s\n", version); + return pci_module_init(&orinoco_tmd_driver); +} + +extern void __exit orinoco_tmd_exit(void) +{ + pci_unregister_driver(&orinoco_tmd_driver); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); +} + +module_init(orinoco_tmd_init); +module_exit(orinoco_tmd_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pci/pci.ids linux.21rc1-ac2/drivers/pci/pci.ids --- linux.21rc1/drivers/pci/pci.ids 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/pci/pci.ids 2003-04-25 13:30:16.000000000 +0100 @@ -1119,6 +1119,7 @@ 0735 735 Host 0740 740 Host 0745 745 Host + 0746 746 Host 0900 SiS900 10/100 Ethernet 1039 0900 SiS900 10/100 Ethernet Adapter 0961 SiS961 [MuTIOL Media IO] diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pcmcia/i82092.c linux.21rc1-ac2/drivers/pcmcia/i82092.c --- linux.21rc1/drivers/pcmcia/i82092.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/pcmcia/i82092.c 2003-04-22 19:08:46.000000000 +0100 @@ -1,12 +1,12 @@ /* * Driver for Intel I82092AA PCI-PCMCIA bridge. * - * (C) 2001 Red Hat, Inc. + * (C) 2001-2003 Red Hat, Inc. * - * Author: Arjan Van De Ven - * Loosly based on i82365.c from the pcmcia-cs package + * Author: Arjan Van De Ven + * Loosely based on i82365.c from the pcmcia-cs package * - * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $ + * $Id: i82092.c,v 1.16 2003/04/15 16:36:42 dwmw2 Exp $ */ #include @@ -26,6 +26,11 @@ #include "i82365.h" MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Red Hat, Inc. - Arjan Van De Ven "); +MODULE_DESCRIPTION("Socket driver for Intel i82092AA PCI-PCMCIA bridge"); + +/* Extra i82092-specific register */ +#define I365_CPAGE 0x26 /* PCI core routines */ static struct pci_device_id i82092aa_pci_ids[] = { @@ -73,9 +78,9 @@ 1 = empty socket, 2 = card but not initialized, 3 = operational card */ - int io_base; /* base io address of the socket */ + unsigned long io_base; /* base io address of the socket */ socket_cap_t cap; - + unsigned int pending_events; /* Pending events on this interface */ void (*handler)(void *info, u_int events); @@ -87,19 +92,35 @@ #define MAX_SOCKETS 4 static struct socket_info sockets[MAX_SOCKETS]; -static int socket_count; /* shortcut */ +static int socket_count; /* shortcut */ + +int membase = -1; +int isa_setup; +MODULE_PARM(membase, "i"); +MODULE_PARM(isa_setup, "i"); static int __init i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { unsigned char configbyte; + struct pci_dev *parent; int i; enter("i82092aa_pci_probe"); if (pci_enable_device(dev)) return -EIO; - + + /* Since we have no memory BARs some firmware we may not + have had PCI_COMMAND_MEM enabled, yet the device needs + it. */ + pci_read_config_byte(dev, PCI_COMMAND, &configbyte); + if (!(configbyte | PCI_COMMAND_MEMORY)) { + dprintk(KERN_DEBUG "Enabling PCI_COMMAND_MEMORY\n"); + configbyte |= PCI_COMMAND_MEMORY; + pci_write_config_byte(dev, PCI_COMMAND, configbyte); + } + pci_read_config_byte(dev, 0x40, &configbyte); /* PCI Configuration Control */ switch(configbyte&6) { case 0: @@ -122,6 +143,53 @@ break; } + if (membase == -1) { + for (i = 0; i < 4; i++) { + if ((dev->bus->resource[i]->flags & (IORESOURCE_MEM|IORESOURCE_READONLY|IORESOURCE_CACHEABLE|IORESOURCE_SHADOWABLE)) + == IORESOURCE_MEM) { + membase = dev->bus->resource[i]->start >> 24; + goto mem_ok; + } + } + printk(KERN_WARNING "No suitable memory range for i82092aa found\n"); + return -ENOSPC; + } + mem_ok: + if (membase) + printk(KERN_NOTICE "i82092 memory base address set to 0x%02x000000\n", membase); + + /* If we're down the end of the PCI bus chain where ISA cycles don't get sent, then + only 1/4 of the I/O address space is going to be usable, unless we make sure that + the NO_ISA bit in the Bridge Control register of all upstream busses is cleared. + Since some PCMCIA cards (most serial ports, for example) will decode 10 bits and + respond only to addresses where bits 8 and 9 are non-zero, we need to do this. */ + for (parent = dev->bus->self; + parent && (parent->class>>8) == PCI_CLASS_BRIDGE_PCI; + parent = parent->bus->self) { + uint16_t brctl; + + if (pci_read_config_word(parent, PCI_BRIDGE_CONTROL, &brctl)) { + printk(KERN_WARNING "Error reading bridge control word from device %s\n", parent->slot_name); + continue; + } + + if (!(brctl & PCI_BRIDGE_CTL_NO_ISA)) + continue; + + if (isa_setup) { + printk(KERN_NOTICE "PCI bridge %s has NOISA bit set. Clearing to allow full PCMCIA operation\n", + parent->slot_name); + brctl &= ~PCI_BRIDGE_CTL_NO_ISA; + if (pci_write_config_word(parent, PCI_BRIDGE_CONTROL, brctl)) + printk(KERN_WARNING "Error writing bridge control word from device %s\n", parent->slot_name); + } else { + printk(KERN_NOTICE "PCI bridge %s has NOISA bit set. Some I/O addresses for PCMCIA cards will not work.\n", + parent->slot_name); + printk(KERN_NOTICE "Perhaps use 'isa_setup=1' option to i82092.o?\n"); + break; + } + } + for (i = 0;iresource[0].start & ~1); @@ -129,10 +197,13 @@ request_region(sockets[i].io_base, 2, "i82092aa"); - sockets[i].cap.features |= SS_CAP_PCCARD; + sockets[i].cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS; sockets[i].cap.map_size = 0x1000; sockets[i].cap.irq_mask = 0; sockets[i].cap.pci_irq = dev->irq; + + /* Trick the resource code into doing the right thing... */ + sockets[i].cap.cb_dev = dev; if (card_present(i)) { sockets[i].card_state = 3; @@ -177,7 +248,7 @@ static unsigned char indirect_read(int socket, unsigned short reg) { - unsigned short int port; + unsigned long port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); @@ -191,7 +262,7 @@ static unsigned short indirect_read16(int socket, unsigned short reg) { - unsigned short int port; + unsigned long port; unsigned short tmp; unsigned long flags; spin_lock_irqsave(&port_lock,flags); @@ -208,7 +279,7 @@ static void indirect_write(int socket, unsigned short reg, unsigned char value) { - unsigned short int port; + unsigned long port; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; @@ -220,7 +291,7 @@ static void indirect_setbit(int socket, unsigned short reg, unsigned char mask) { - unsigned short int port; + unsigned long port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); @@ -237,7 +308,7 @@ static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask) { - unsigned short int port; + unsigned long port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); @@ -253,7 +324,7 @@ static void indirect_write16(int socket, unsigned short reg, unsigned short value) { - unsigned short int port; + unsigned long port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); @@ -299,7 +370,7 @@ for (i=0; i < socket_count; i++) { events = xchg(&(sockets[i].pending_events),0); - printk("events = %x \n",events); + dprintk("events = %x \n",events); if (sockets[i].handler) sockets[i].handler(sockets[i].info, events); } @@ -343,7 +414,7 @@ if (csc & I365_CSC_DETECT) { events |= SS_DETECT; - printk("Card detected in socket %i!\n",i); + dprintk("Card detected in socket %i!\n",i); } if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) { @@ -419,7 +490,6 @@ enter("i82092aa_init"); - mem.sys_stop = 0x0fff; i82092aa_set_socket(s, &dead_socket); for (i = 0; i < 2; i++) { io.map = i; @@ -604,11 +674,11 @@ reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */ if (state->flags & SS_PWR_AUTO) { - printk("Auto power\n"); + dprintk("Auto power\n"); reg |= I365_PWR_AUTO; /* automatic power mngmnt */ } if (state->flags & SS_OUTPUT_ENA) { - printk("Power Enabled \n"); + dprintk("Power Enabled \n"); reg |= I365_PWR_OUT; /* enable power */ } @@ -616,11 +686,11 @@ case 0: break; case 50: - printk("setting voltage to Vcc to 5V on socket %i\n",sock); + dprintk("setting voltage to Vcc to 5V on socket %i\n",sock); reg |= I365_VCC_5V; break; default: - printk("i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc); + printk(KERN_WARNING "i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc); leave("i82092aa_set_socket"); return -EINVAL; } @@ -628,18 +698,18 @@ switch (state->Vpp) { case 0: - printk("not setting Vpp on socket %i\n",sock); + dprintk("not setting Vpp on socket %i\n",sock); break; case 50: - printk("setting Vpp to 5.0 for socket %i\n",sock); + dprintk("setting Vpp to 5.0 for socket %i\n",sock); reg |= I365_VPP1_5V | I365_VPP2_5V; break; case 120: - printk("setting Vpp to 12.0\n"); + dprintk("setting Vpp to 12.0\n"); reg |= I365_VPP1_12V | I365_VPP2_12V; break; default: - printk("i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc); + printk(KERN_WARNING "i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc); leave("i82092aa_set_socket"); return -EINVAL; } @@ -797,7 +867,7 @@ mem->card_start = ( (unsigned long)(i & 0x3fff)<12) + mem->sys_start; mem->card_start &= 0x3ffffff; - printk("Card %i is from %lx to %lx \n",sock,mem->sys_start,mem->sys_stop); + dprintk("Card %i is from %lx to %lx \n",sock,mem->sys_start,mem->sys_stop); leave("i82092aa_get_mem_map"); return 0; @@ -808,7 +878,7 @@ { unsigned short base, i; unsigned char map; - + enter("i82092aa_set_mem_map"); map = mem->map; @@ -817,17 +887,22 @@ return -EINVAL; } + /* Turn off the window before changing anything */ + if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) + indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); + + if (!(mem->flags & MAP_ACTIVE)) + return 0; + if ( (mem->card_start > 0x3ffffff) || (mem->sys_start > mem->sys_stop) || + ((mem->sys_start >> 24) != membase) || ((mem->sys_stop >> 24) != membase) || (mem->speed > 1000) ) { leave("i82092aa_set_mem_map: invalid address / speed"); - printk("invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,mem->sys_start, mem->sys_stop, mem->card_start); + printk(KERN_WARNING "invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,mem->sys_start, mem->sys_stop, mem->card_start); return -EINVAL; } - /* Turn off the window before changing anything */ - if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) - indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); /* printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, mem->sys_start,mem->sys_stop,sock,mem->speed,mem->flags & MAP_ACTIVE); */ @@ -862,7 +937,7 @@ /* card start */ - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + i = (((mem->card_start - mem->sys_start) >> 12) - (membase << 12)) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) { @@ -872,10 +947,10 @@ /* printk("requesting normal memory for socket %i\n",sock);*/ } indirect_write16(sock,base+I365_W_OFF,i); - + indirect_write(sock, I365_CPAGE, membase); + /* Enable the window if necessary */ - if (mem->flags & MAP_ACTIVE) - indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); + indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); leave("i82092aa_set_mem_map"); return 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pcmcia/ti113x.h linux.21rc1-ac2/drivers/pcmcia/ti113x.h --- linux.21rc1/drivers/pcmcia/ti113x.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/pcmcia/ti113x.h 2003-04-22 17:37:24.000000000 +0100 @@ -167,6 +167,27 @@ new |= I365_INTR_ENA; if (new != reg) exca_writeb(socket, I365_INTCTL, new); + + /* + * If ISA interrupts don't work, then fall back to routing card + * interrupts to the PCI interrupt of the socket. + */ + if (!socket->cap.irq_mask) { + int irqmux, devctl; + + printk (KERN_INFO "ti113x: Routing card interrupts to PCI\n"); + + devctl = config_readb(socket, TI113X_DEVICE_CONTROL); + devctl &= ~TI113X_DCR_IMODE_MASK; + + irqmux = config_readl(socket, TI122X_IRQMUX); + irqmux = (irqmux & ~0x0f) | 0x02; /* route INTA */ + irqmux = (irqmux & ~0xf0) | 0x20; /* route INTB */ + + config_writel(socket, TI122X_IRQMUX, irqmux); + config_writeb(socket, TI113X_DEVICE_CONTROL, devctl); + } + return 0; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pnp/Config.in linux.21rc1-ac2/drivers/pnp/Config.in --- linux.21rc1/drivers/pnp/Config.in 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/pnp/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -8,4 +8,8 @@ dep_tristate ' ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_bool ' PNPBIOS support (EXPERIMENTAL)' CONFIG_PNPBIOS $CONFIG_PNP +fi + endmenu diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pnp/Makefile linux.21rc1-ac2/drivers/pnp/Makefile --- linux.21rc1/drivers/pnp/Makefile 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/pnp/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -10,15 +10,22 @@ O_TARGET := pnp.o -export-objs := isapnp.o -list-multi := isa-pnp.o +export-objs := isapnp.o pnpbios_core.o +multi-objs := isa-pnp.o pnpbios.o -proc-$(CONFIG_PROC_FS) = isapnp_proc.o -isa-pnp-objs := isapnp.o quirks.o $(proc-y) +isa-pnp-proc-$(CONFIG_PROC_FS) = isapnp_proc.o +pnpbios-proc-$(CONFIG_PROC_FS) = pnpbios_proc.o + +isa-pnp-objs := isapnp.o quirks.o $(isa-pnp-proc-y) +pnpbios-objs := pnpbios_core.o $(pnpbios-proc-y) obj-$(CONFIG_ISAPNP) += isa-pnp.o +obj-$(CONFIG_PNPBIOS) += pnpbios.o include $(TOPDIR)/Rules.make isa-pnp.o: $(isa-pnp-objs) $(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs) + +pnpbios.o: $(pnpbios-objs) + $(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pnp/pnpbios_core.c linux.21rc1-ac2/drivers/pnp/pnpbios_core.c --- linux.21rc1/drivers/pnp/pnpbios_core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/pnp/pnpbios_core.c 2003-04-23 14:39:14.000000000 +0100 @@ -0,0 +1,1352 @@ +/* + * PnP BIOS services + * + * Originally (C) 1998 Christian Schmidt + * Modifications (c) 1998 Tom Lees + * Minor reorganizations by David Hinds + * Modifications (c) 2001,2002 by Thomas Hood + * + * 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, 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 + * + * References: + * Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corporation + * Plug and Play BIOS Specification, Version 1.0A, May 5, 1994 + * Plug and Play BIOS Clarification Paper, October 6, 1994 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * + * PnP BIOS INTERFACE + * + */ + +/* PnP BIOS signature: "$PnP" */ +#define PNP_SIGNATURE (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24)) + +#pragma pack(1) +union pnp_bios_expansion_header { + struct { + u32 signature; /* "$PnP" */ + u8 version; /* in BCD */ + u8 length; /* length in bytes, currently 21h */ + u16 control; /* system capabilities */ + u8 checksum; /* all bytes must add up to 0 */ + + u32 eventflag; /* phys. address of the event flag */ + u16 rmoffset; /* real mode entry point */ + u16 rmcseg; + u16 pm16offset; /* 16 bit protected mode entry */ + u32 pm16cseg; + u32 deviceID; /* EISA encoded system ID or 0 */ + u16 rmdseg; /* real mode data segment */ + u32 pm16dseg; /* 16 bit pm data segment base */ + } fields; + char chars[0x21]; /* To calculate the checksum */ +}; +#pragma pack() + +static struct { + u16 offset; + u16 segment; +} pnp_bios_callpoint; + +static union pnp_bios_expansion_header * pnp_bios_hdr = NULL; + +/* The PnP BIOS entries in the GDT */ +#define PNP_GDT (0x0060) +#define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ +#define PNP_CS16 (PNP_GDT+0x08) /* code segment for BIOS */ +#define PNP_DS (PNP_GDT+0x10) /* data segment for BIOS */ +#define PNP_TS1 (PNP_GDT+0x18) /* transfer data segment */ +#define PNP_TS2 (PNP_GDT+0x20) /* another data segment */ + +/* + * These are some opcodes for a "static asmlinkage" + * As this code is *not* executed inside the linux kernel segment, but in a + * alias at offset 0, we need a far return that can not be compiled by + * default (please, prove me wrong! this is *really* ugly!) + * This is the only way to get the bios to return into the kernel code, + * because the bios code runs in 16 bit protected mode and therefore can only + * return to the caller if the call is within the first 64kB, and the linux + * kernel begins at offset 3GB... + */ + +asmlinkage void pnp_bios_callfunc(void); + +__asm__( + ".text \n" + __ALIGN_STR "\n" + SYMBOL_NAME_STR(pnp_bios_callfunc) ":\n" + " pushl %edx \n" + " pushl %ecx \n" + " pushl %ebx \n" + " pushl %eax \n" + " lcallw " SYMBOL_NAME_STR(pnp_bios_callpoint) "\n" + " addl $16, %esp \n" + " lret \n" + ".previous \n" +); + +#define Q_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], __va((u32)(address))); \ +set_limit (gdt [(selname) >> 3], size) + +#define Q2_SET_SEL(selname, address, size) \ +set_base (gdt [(selname) >> 3], (u32)(address)); \ +set_limit (gdt [(selname) >> 3], size) + +/* + * At some point we want to use this stack frame pointer to unwind + * after PnP BIOS oopses. + */ + +u32 pnp_bios_fault_esp; +u32 pnp_bios_fault_eip; +u32 pnp_bios_is_utter_crap = 0; + +static spinlock_t pnp_bios_lock; + +static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3, + u16 arg4, u16 arg5, u16 arg6, u16 arg7, + void *ts1_base, u32 ts1_size, + void *ts2_base, u32 ts2_size) +{ + unsigned long flags; + u16 status; + + /* + * PnP BIOSes are generally not terribly re-entrant. + * Also, don't rely on them to save everything correctly. + */ + if(pnp_bios_is_utter_crap) + return PNP_FUNCTION_NOT_SUPPORTED; + + /* On some boxes IRQ's during PnP BIOS calls are deadly. */ + spin_lock_irqsave(&pnp_bios_lock, flags); + + if (ts1_size) + Q2_SET_SEL(PNP_TS1, ts1_base, ts1_size); + if (ts2_size) + Q2_SET_SEL(PNP_TS2, ts2_base, ts2_size); + + __asm__ __volatile__( + "pushl %%ebp\n\t" + "pushl %%edi\n\t" + "pushl %%esi\n\t" + "pushl %%ds\n\t" + "pushl %%es\n\t" + "pushl %%fs\n\t" + "pushl %%gs\n\t" + "pushfl\n\t" + "movl %%esp, pnp_bios_fault_esp\n\t" + "movl $1f, pnp_bios_fault_eip\n\t" + "lcall %5,%6\n\t" + "1:popfl\n\t" + "popl %%gs\n\t" + "popl %%fs\n\t" + "popl %%es\n\t" + "popl %%ds\n\t" + "popl %%esi\n\t" + "popl %%edi\n\t" + "popl %%ebp\n\t" + : "=a" (status) + : "0" ((func) | (((u32)arg1) << 16)), + "b" ((arg2) | (((u32)arg3) << 16)), + "c" ((arg4) | (((u32)arg5) << 16)), + "d" ((arg6) | (((u32)arg7) << 16)), + "i" (PNP_CS32), + "i" (0) + : "memory" + ); + spin_unlock_irqrestore(&pnp_bios_lock, flags); + + /* If we get here and this is set then the PnP BIOS faulted on us. */ + if(pnp_bios_is_utter_crap) + { + printk(KERN_ERR "PnPBIOS: Warning! Your PnP BIOS caused a fatal error. Attempting to continue.\n"); + printk(KERN_ERR "PnPBIOS: You may need to reboot with the \"pnpbios=off\" option to operate stably.\n"); + printk(KERN_ERR "PnPBIOS: Check with your vendor for an updated BIOS.\n"); + } + + return status; +} + + +/* + * + * UTILITY FUNCTIONS + * + */ + +static void pnpbios_warn_unexpected_status(const char * module, u16 status) +{ + printk(KERN_ERR "PnPBIOS: %s: Unexpected status 0x%x\n", module, status); +} + +void *pnpbios_kmalloc(size_t size, int f) +{ + void *p = kmalloc( size, f ); + if ( p == NULL ) + printk(KERN_ERR "PnPBIOS: kmalloc() failed\n"); + return p; +} + +/* + * Call this only after init time + */ +static inline int pnp_bios_present(void) +{ + return (pnp_bios_hdr != NULL); +} + +/* Forward declaration */ +static void update_devlist( u8 nodenum, struct pnp_bios_node *data ); + + +/* + * + * PnP BIOS ACCESS FUNCTIONS + * + */ + +#define PNP_GET_NUM_SYS_DEV_NODES 0x00 +#define PNP_GET_SYS_DEV_NODE 0x01 +#define PNP_SET_SYS_DEV_NODE 0x02 +#define PNP_GET_EVENT 0x03 +#define PNP_SEND_MESSAGE 0x04 +#define PNP_GET_DOCKING_STATION_INFORMATION 0x05 +#define PNP_SET_STATIC_ALLOCED_RES_INFO 0x09 +#define PNP_GET_STATIC_ALLOCED_RES_INFO 0x0a +#define PNP_GET_APM_ID_TABLE 0x0b +#define PNP_GET_PNP_ISA_CONFIG_STRUC 0x40 +#define PNP_GET_ESCD_INFO 0x41 +#define PNP_READ_ESCD 0x42 +#define PNP_WRITE_ESCD 0x43 + +/* + * Call PnP BIOS with function 0x00, "get number of system device nodes" + */ +static int __pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0, + data, sizeof(struct pnp_dev_node_info), 0, 0); + data->no_nodes &= 0xff; + return status; +} + +int pnp_bios_dev_node_info(struct pnp_dev_node_info *data) +{ + int status = __pnp_bios_dev_node_info( data ); + if ( status ) + pnpbios_warn_unexpected_status( "dev_node_info", status ); + return status; +} + +/* + * Note that some PnP BIOSes (e.g., on Sony Vaio laptops) die a horrible + * death if they are asked to access the "current" configuration. + * Therefore, if it's a matter of indifference, it's better to call + * get_dev_node() and set_dev_node() with boot=1 rather than with boot=0. + */ + +/* + * Call PnP BIOS with function 0x01, "get system device node" + * Input: *nodenum = desired node, + * boot = whether to get nonvolatile boot (!=0) + * or volatile current (0) config + * Output: *nodenum=next node or 0xff if no more nodes + */ +static int __pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + if ( !boot & pnpbios_dont_use_current_config ) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0, + nodenum, sizeof(char), data, 65536); + return status; +} + +int pnp_bios_get_dev_node(u8 *nodenum, char boot, struct pnp_bios_node *data) +{ + int status; + status = __pnp_bios_get_dev_node( nodenum, boot, data ); + if ( status ) + pnpbios_warn_unexpected_status( "get_dev_node", status ); + return status; +} + + +/* + * Call PnP BIOS with function 0x02, "set system device node" + * Input: *nodenum = desired node, + * boot = whether to set nonvolatile boot (!=0) + * or volatile current (0) config + */ +static int __pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + if ( !boot & pnpbios_dont_use_current_config ) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0, + data, 65536, 0, 0); + return status; +} + +int pnp_bios_set_dev_node(u8 nodenum, char boot, struct pnp_bios_node *data) +{ + int status; + status = __pnp_bios_set_dev_node( nodenum, boot, data ); + if ( status ) { + pnpbios_warn_unexpected_status( "set_dev_node", status ); + return status; + } + if ( !boot ) { /* Update devlist */ + u8 thisnodenum = nodenum; + status = pnp_bios_get_dev_node( &nodenum, boot, data ); + if ( status ) + return status; + update_devlist( thisnodenum, data ); + } + return status; +} + +#if needed +/* + * Call PnP BIOS with function 0x03, "get event" + */ +static int pnp_bios_get_event(u16 *event) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0, + event, sizeof(u16), 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x04, "send message" + */ +static int pnp_bios_send_message(u16 message) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return status; +} +#endif + +#ifdef CONFIG_HOTPLUG +/* + * Call PnP BIOS with function 0x05, "get docking station information" + */ +static int pnp_bios_dock_station_info(struct pnp_docking_station_info *data) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + data, sizeof(struct pnp_docking_station_info), 0, 0); + return status; +} +#endif + +#if needed +/* + * Call PnP BIOS with function 0x09, "set statically allocated resource + * information" + */ +static int pnp_bios_set_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + info, *((u16 *) info), 0, 0); + return status; +} +#endif + +/* + * Call PnP BIOS with function 0x0a, "get statically allocated resource + * information" + */ +static int __pnp_bios_get_stat_res(char *info) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + info, 65536, 0, 0); + return status; +} + +int pnp_bios_get_stat_res(char *info) +{ + int status; + status = __pnp_bios_get_stat_res( info ); + if ( status ) + pnpbios_warn_unexpected_status( "get_stat_res", status ); + return status; +} + +#if needed +/* + * Call PnP BIOS with function 0x0b, "get APM id table" + */ +static int pnp_bios_apm_id_table(char *table, u16 *size) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0, + table, *size, size, sizeof(u16)); + return status; +} +#endif + +/* + * Call PnP BIOS with function 0x40, "get isa pnp configuration structure" + */ +static int __pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) +{ + u16 status; + if (!pnp_bios_present()) + return PNP_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0, + data, sizeof(struct pnp_isa_config_struc), 0, 0); + return status; +} + +int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data) +{ + int status; + status = __pnp_bios_isapnp_config( data ); + if ( status ) + pnpbios_warn_unexpected_status( "isapnp_config", status ); + return status; +} + +/* + * Call PnP BIOS with function 0x41, "get ESCD info" + */ +static int __pnp_bios_escd_info(struct escd_info_struc *data) +{ + u16 status; + if (!pnp_bios_present()) + return ESCD_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS, + data, sizeof(struct escd_info_struc), 0, 0); + return status; +} + +int pnp_bios_escd_info(struct escd_info_struc *data) +{ + int status; + status = __pnp_bios_escd_info( data ); + if ( status ) + pnpbios_warn_unexpected_status( "escd_info", status ); + return status; +} + +/* + * Call PnP BIOS function 0x42, "read ESCD" + * nvram_base is determined by calling escd_info + */ +static int __pnp_bios_read_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present()) + return ESCD_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0, + data, 65536, (void *)nvram_base, 65536); + return status; +} + +int pnp_bios_read_escd(char *data, u32 nvram_base) +{ + int status; + status = __pnp_bios_read_escd( data, nvram_base ); + if ( status ) + pnpbios_warn_unexpected_status( "read_escd", status ); + return status; +} + +#if needed +/* + * Call PnP BIOS function 0x43, "write ESCD" + */ +static int pnp_bios_write_escd(char *data, u32 nvram_base) +{ + u16 status; + if (!pnp_bios_present()) + return ESCD_FUNCTION_NOT_SUPPORTED; + status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0, + data, 65536, nvram_base, 65536); + return status; +} +#endif + + +/* + * + * DOCKING FUNCTIONS + * + */ + +#ifdef CONFIG_HOTPLUG + +static int unloading = 0; +static struct completion unload_sem; + +/* + * (Much of this belongs in a shared routine somewhere) + */ + +static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) +{ + char *argv [3], **envp, *buf, *scratch; + int i = 0, value; + + if (!hotplug_path [0]) + return -ENOENT; + if (!current->fs->root) { + return -EAGAIN; + } + if (!(envp = (char **) pnpbios_kmalloc (20 * sizeof (char *), GFP_KERNEL))) { + return -ENOMEM; + } + if (!(buf = pnpbios_kmalloc (256, GFP_KERNEL))) { + kfree (envp); + return -ENOMEM; + } + + /* only one standardized param to hotplug command: type */ + argv [0] = hotplug_path; + argv [1] = "dock"; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + +#ifdef DEBUG + /* hint that policy agent should enter no-stdout debug mode */ + envp [i++] = "DEBUG=kernel"; +#endif + /* extensible set of named bus-specific parameters, + * supporting multiple driver selection algorithms. + */ + scratch = buf; + + /* action: add, remove */ + envp [i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", dock?"add":"remove") + 1; + + /* Report the ident for the dock */ + envp [i++] = scratch; + scratch += sprintf (scratch, "DOCK=%x/%x/%x", + info->location_id, info->serial, info->capabilities); + envp[i] = 0; + + value = call_usermodehelper (argv [0], argv, envp); + kfree (buf); + kfree (envp); + return 0; +} + +/* + * Poll the PnP docking at regular intervals + */ +static int pnp_dock_thread(void * unused) +{ + static struct pnp_docking_station_info now; + int docked = -1, d = 0; + daemonize(); + reparent_to_init(); + strcpy(current->comm, "kpnpbiosd"); + while(!unloading && !signal_pending(current)) + { + int status; + + /* + * Poll every 2 seconds + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ*2); + if(signal_pending(current)) + break; + + status = pnp_bios_dock_station_info(&now); + + switch(status) + { + /* + * No dock to manage + */ + case PNP_FUNCTION_NOT_SUPPORTED: + complete_and_exit(&unload_sem, 0); + case PNP_SYSTEM_NOT_DOCKED: + d = 0; + break; + case PNP_SUCCESS: + d = 1; + break; + default: + pnpbios_warn_unexpected_status( "pnp_dock_thread", status ); + continue; + } + if(d != docked) + { + if(pnp_dock_event(d, &now)==0) + { + docked = d; +#if 0 + printk(KERN_INFO "PnPBIOS: Docking station %stached\n", docked?"at":"de"); +#endif + } + } + } + complete_and_exit(&unload_sem, 0); +} + +#endif /* CONFIG_HOTPLUG */ + + +/* + * + * NODE DATA PARSING FUNCTIONS + * + */ + +static void add_irqresource(struct pci_dev *dev, int irq) +{ + int i = 0; + while (!(dev->irq_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_IRQ) i++; + if (i < DEVICE_COUNT_IRQ) { + dev->irq_resource[i].start = (unsigned long) irq; + dev->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag + } +} + +static void add_dmaresource(struct pci_dev *dev, int dma) +{ + int i = 0; + while (!(dev->dma_resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_DMA) i++; + if (i < DEVICE_COUNT_DMA) { + dev->dma_resource[i].start = (unsigned long) dma; + dev->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag + } +} + +static void add_ioresource(struct pci_dev *dev, int io, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = (unsigned long) io; + dev->resource[i].end = (unsigned long)(io + len - 1); + dev->resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag + } +} + +static void add_memresource(struct pci_dev *dev, int mem, int len) +{ + int i = 0; + while (!(dev->resource[i].flags & IORESOURCE_UNSET) && i < DEVICE_COUNT_RESOURCE) i++; + if (i < DEVICE_COUNT_RESOURCE) { + dev->resource[i].start = (unsigned long) mem; + dev->resource[i].end = (unsigned long)(mem + len - 1); + dev->resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag + } +} + +static void node_resource_data_to_dev(struct pnp_bios_node *node, struct pci_dev *dev) +{ + unsigned char *p = node->data, *lastp=NULL; + int i; + + /* + * First, set resource info to default values + */ + for (i=0;iresource[i].start = 0; // "disabled" + dev->resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;iirq_resource[i].start = (unsigned long)-1; // "disabled" + dev->irq_resource[i].flags = IORESOURCE_UNSET; + } + for (i=0;idma_resource[i].start = (unsigned long)-1; // "disabled" + dev->dma_resource[i].flags = IORESOURCE_UNSET; + } + + /* + * Fill in dev resource info + */ + while ( (char *)p < ((char *)node->data + node->size )) { + if(p==lastp) break; + + if( p[0] & 0x80 ) {// large item + switch (p[0] & 0x7f) { + case 0x01: // memory + { + int io = *(short *) &p[4]; + int len = *(short *) &p[10]; + add_memresource(dev, io, len); + break; + } + case 0x02: // device name + { + int len = *(short *) &p[1]; + memcpy(dev->name, p + 3, len >= 80 ? 79 : len); + break; + } + case 0x05: // 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[16]; + add_memresource(dev, io, len); + break; + } + case 0x06: // fixed location 32-bit memory + { + int io = *(int *) &p[4]; + int len = *(int *) &p[8]; + add_memresource(dev, io, len); + break; + } + } /* switch */ + lastp = p+3; + p = p + p[1] + p[2]*256 + 3; + continue; + } + if ((p[0]>>3) == 0x0f) // end tag + break; + switch (p[0]>>3) { + case 0x04: // irq + { + int i, mask, irq = -1; + mask= p[1] + p[2]*256; + for (i=0;i<16;i++, mask=mask>>1) + if(mask & 0x01) irq=i; + add_irqresource(dev, irq); + break; + } + case 0x05: // dma + { + int i, mask, dma = -1; + mask = p[1]; + for (i=0;i<8;i++, mask = mask>>1) + if(mask & 0x01) dma=i; + add_dmaresource(dev, dma); + break; + } + case 0x08: // io + { + int io= p[2] + p[3] *256; + int len = p[7]; + add_ioresource(dev, io, len); + break; + } + case 0x09: // fixed location io + { + int io = p[1] + p[2] * 256; + int len = p[3]; + add_ioresource(dev, io, len); + break; + } + } /* switch */ + lastp=p+1; + p = p + (p[0] & 0x07) + 1; + + } /* while */ + + return; +} + + +/* + * + * DEVICE LIST MANAGEMENT FUNCTIONS + * + * + * Some of these are exported to give public access + * + * Question: Why maintain a device list when the PnP BIOS can + * list devices for us? Answer: Some PnP BIOSes can't report + * the current configuration, only the boot configuration. + * The boot configuration can be changed, so we need to keep + * a record of what the configuration was when we booted; + * presumably it continues to describe the current config. + * For those BIOSes that can change the current config, we + * keep the information in the devlist up to date. + * + * Note that it is currently assumed that the list does not + * grow or shrink in size after init time, and slot_name + * never changes. The list is protected by a spinlock. + */ + +static LIST_HEAD(pnpbios_devices); + +static spinlock_t pnpbios_devices_lock; + +static int inline insert_device(struct pci_dev *dev) +{ + + /* + * FIXME: Check for re-add of existing node; + * return -1 if node already present + */ + + /* We don't lock because we only do this at init time */ + list_add_tail(&dev->global_list, &pnpbios_devices); + + return 0; +} + +#define HEX(id,a) hex[((id)>>a) & 15] +#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) +// +static void inline pnpid32_to_pnpid(u32 id, char *str) +{ + const char *hex = "0123456789abcdef"; + + id = be32_to_cpu(id); + str[0] = CHAR(id, 26); + str[1] = CHAR(id, 21); + str[2] = CHAR(id,16); + str[3] = HEX(id, 12); + str[4] = HEX(id, 8); + str[5] = HEX(id, 4); + str[6] = HEX(id, 0); + str[7] = '\0'; + + return; +} +// +#undef CHAR +#undef HEX + +/* + * Build a linked list of pci_devs in order of ascending node number + * Called only at init time. + */ +static void __init build_devlist(void) +{ + u8 nodenum; + unsigned int nodes_got = 0; + unsigned int devs = 0; + struct pnp_bios_node *node; + struct pnp_dev_node_info node_info; + struct pci_dev *dev; + + if (!pnp_bios_present()) + return; + + if (pnp_bios_dev_node_info(&node_info) != 0) + return; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) + return; + + for(nodenum=0; nodenum<0xff; ) { + u8 thisnodenum = nodenum; + /* We build the list from the "boot" config because + * asking for the "current" config causes some + * BIOSes to crash. + */ + if (pnp_bios_get_dev_node(&nodenum, (char )1 , node)) + break; + nodes_got++; + dev = pnpbios_kmalloc(sizeof (struct pci_dev), GFP_KERNEL); + if (!dev) + break; + memset(dev,0,sizeof(struct pci_dev)); + dev->devfn = thisnodenum; + memcpy(dev->name,"PNPBIOS",8); + pnpid32_to_pnpid(node->eisa_id,dev->slot_name); + node_resource_data_to_dev(node,dev); + if(insert_device(dev)<0) + kfree(dev); + else + devs++; + if (nodenum <= thisnodenum) { + printk(KERN_ERR "PnPBIOS: build_devlist: Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", (unsigned int)nodenum, (unsigned int)thisnodenum); + break; + } + } + kfree(node); + + printk(KERN_INFO "PnPBIOS: %i node%s reported by PnP BIOS; %i recorded by driver\n", + nodes_got, nodes_got != 1 ? "s" : "", devs); +} + +static struct pci_dev *find_device_by_nodenum( u8 nodenum ) +{ + struct pci_dev *dev; + + pnpbios_for_each_dev(dev) { + if(dev->devfn == nodenum) + return dev; + } + + return NULL; +} + +static void update_devlist( u8 nodenum, struct pnp_bios_node *data ) +{ + unsigned long flags; + struct pci_dev *dev; + + spin_lock_irqsave(&pnpbios_devices_lock, flags); + dev = find_device_by_nodenum( nodenum ); + if ( dev ) { + node_resource_data_to_dev(data,dev); + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); + + return; +} + + +/* + * + * DRIVER REGISTRATION FUNCTIONS + * + * + * Exported to give public access + * + */ + +static LIST_HEAD(pnpbios_drivers); + +static const struct pnpbios_device_id * +match_device(const struct pnpbios_device_id *ids, const struct pci_dev *dev) +{ + while (*ids->id) + { + if(memcmp(ids->id, dev->slot_name, 7)==0) + return ids; + ids++; + } + return NULL; +} + +static int announce_device(struct pnpbios_driver *drv, struct pci_dev *dev) +{ + const struct pnpbios_device_id *id; + struct pci_dev tmpdev; + int ret; + + if (drv->id_table) { + id = match_device(drv->id_table, dev); + if (!id) + return 0; + } else + id = NULL; + + memcpy( &tmpdev, dev, sizeof(struct pci_dev)); + tmpdev.global_list.prev = NULL; + tmpdev.global_list.next = NULL; + + dev_probe_lock(); + /* Obviously, probe() should not call any pnpbios functions */ + ret = drv->probe(&tmpdev, id); + dev_probe_unlock(); + if (ret < 1) + return 0; + + dev->driver = (void *)drv; + + return 1; +} + +/** + * pnpbios_register_driver - register a new pci driver + * @drv: the driver structure to register + * + * Adds the driver structure to the list of registered drivers + * + * For each device in the pnpbios device list that matches one of + * the ids in drv->id_table, calls the driver's "probe" function with + * arguments (1) a pointer to a *temporary* struct pci_dev containing + * resource info for the device, and (2) a pointer to the id string + * of the device. Expects the probe function to return 1 if the + * driver claims the device (otherwise 0) in which case, marks the + * device as having this driver. + * + * Returns the number of pci devices which were claimed by the driver + * during registration. The driver remains registered even if the + * return value is zero. + */ +int pnpbios_register_driver(struct pnpbios_driver *drv) +{ + struct pci_dev *dev; + unsigned long flags; + int count = 0; + + list_add_tail(&drv->node, &pnpbios_drivers); + spin_lock_irqsave(&pnpbios_devices_lock, flags); + pnpbios_for_each_dev(dev) { + if (!pnpbios_dev_driver(dev)) + count += announce_device(drv, dev); + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); + return count; +} + +EXPORT_SYMBOL(pnpbios_register_driver); + +/** + * pnpbios_unregister_driver - unregister a pci driver + * @drv: the driver structure to unregister + * + * Deletes the driver structure from the list of registered PnPBIOS + * drivers, gives it a chance to clean up by calling its "remove" + * function for each device it was responsible for, and marks those + * devices as driverless. + */ +void pnpbios_unregister_driver(struct pnpbios_driver *drv) +{ + unsigned long flags; + struct pci_dev *dev; + + list_del(&drv->node); + spin_lock_irqsave(&pnpbios_devices_lock, flags); + pnpbios_for_each_dev(dev) { + if (dev->driver == (void *)drv) { + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } + } + spin_unlock_irqrestore(&pnpbios_devices_lock, flags); +} + +EXPORT_SYMBOL(pnpbios_unregister_driver); + + +/* + * + * RESOURCE RESERVATION FUNCTIONS + * + * + * Used only at init time + * + */ + +static void __init reserve_ioport_range(char *pnpid, int start, int end) +{ + struct resource *res; + char *regionid; + +#if 0 + /* + * TEMPORARY hack to work around the fact that the + * floppy driver inappropriately reserves ioports 0x3f0 and 0x3f1 + * Remove this once the floppy driver is fixed. + */ + if ( + (0x3f0 >= start && 0x3f0 <= end) + || (0x3f1 >= start && 0x3f1 <= end) + ) { + printk(KERN_INFO + "PnPBIOS: %s: ioport range 0x%x-0x%x NOT reserved\n", + pnpid, start, end + ); + return; + } +#endif + + regionid = pnpbios_kmalloc(16, GFP_KERNEL); + if ( regionid == NULL ) + return; + snprintf(regionid, 16, "PnPBIOS %s", pnpid); + res = request_region(start,end-start+1,regionid); + if ( res == NULL ) + kfree( regionid ); + else + res->flags &= ~IORESOURCE_BUSY; + /* + * Failures at this point are usually harmless. pci quirks for + * example do reserve stuff they know about too, so we may well + * have double reservations. + */ + printk(KERN_INFO + "PnPBIOS: %s: ioport range 0x%x-0x%x %s reserved\n", + pnpid, start, end, + NULL != res ? "has been" : "could not be" + ); + + return; +} + +static void __init reserve_resources_of_dev( struct pci_dev *dev ) +{ + int i; + + for (i=0;iresource[i].flags & IORESOURCE_UNSET ) + /* end of resources */ + break; + if (dev->resource[i].flags & IORESOURCE_IO) { + /* ioport */ + if ( dev->resource[i].start == 0 ) + /* disabled */ + /* Do nothing */ + continue; + if ( dev->resource[i].start < 0x100 ) + /* + * Below 0x100 is only standard PC hardware + * (pics, kbd, timer, dma, ...) + * We should not get resource conflicts there, + * and the kernel reserves these anyway + * (see arch/i386/kernel/setup.c). + * So, do nothing + */ + continue; + if ( dev->resource[i].end < dev->resource[i].start ) + /* invalid endpoint */ + /* Do nothing */ + continue; + reserve_ioport_range( + dev->slot_name, + dev->resource[i].start, + dev->resource[i].end + ); + } else if (dev->resource[i].flags & IORESOURCE_MEM) { + /* iomem */ + /* For now do nothing */ + continue; + } else { + /* Neither ioport nor iomem */ + /* Do nothing */ + continue; + } + } + + return; +} + +static void __init reserve_resources( void ) +{ + struct pci_dev *dev; + + pnpbios_for_each_dev(dev) { + if ( + 0 != strcmp(dev->slot_name,"PNP0c01") && /* memory controller */ + 0 != strcmp(dev->slot_name,"PNP0c02") /* system peripheral: other */ + ) { + continue; + } + reserve_resources_of_dev(dev); + } + + return; +} + + +/* + * + * INIT AND EXIT + * + */ + +extern int is_sony_vaio_laptop; + +static int pnpbios_disabled; /* = 0 */ +static int dont_reserve_resources; /* = 0 */ +int pnpbios_dont_use_current_config; /* = 0 */ + +#ifndef MODULE +static int __init pnpbios_setup(char *str) +{ + int invert; + + while ((str != NULL) && (*str != '\0')) { + if (strncmp(str, "off", 3) == 0) + pnpbios_disabled=1; + if (strncmp(str, "on", 2) == 0) + pnpbios_disabled=0; + invert = (strncmp(str, "no-", 3) == 0); + if (invert) + str += 3; + if (strncmp(str, "curr", 4) == 0) + pnpbios_dont_use_current_config = invert; + if (strncmp(str, "res", 3) == 0) + dont_reserve_resources = invert; + str = strchr(str, ','); + if (str != NULL) + str += strspn(str, ", \t"); + } + + return 1; +} + +__setup("pnpbios=", pnpbios_setup); +#endif + +int __init pnpbios_init(void) +{ + union pnp_bios_expansion_header *check; + u8 sum; + int i, length, r; + + spin_lock_init(&pnp_bios_lock); + spin_lock_init(&pnpbios_devices_lock); + + if(pnpbios_disabled || (dmi_broken & BROKEN_PNP_BIOS) ) { + printk(KERN_INFO "PnPBIOS: Disabled\n"); + return -ENODEV; + } + + if ( is_sony_vaio_laptop ) + pnpbios_dont_use_current_config = 1; + + /* + * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS + * structure and, if one is found, sets up the selectors and + * entry points + */ + for (check = (union pnp_bios_expansion_header *) __va(0xf0000); + check < (union pnp_bios_expansion_header *) __va(0xffff0); + ((void *) (check)) += 16) { + if (check->fields.signature != PNP_SIGNATURE) + continue; + length = check->fields.length; + if (!length) + continue; + for (sum = 0, i = 0; i < length; i++) + sum += check->chars[i]; + if (sum) + continue; + if (check->fields.version < 0x10) { + printk(KERN_WARNING "PnPBIOS: PnP BIOS version %d.%d is not supported\n", + check->fields.version >> 4, + check->fields.version & 15); + continue; + } + printk(KERN_INFO "PnPBIOS: Found PnP BIOS installation structure at 0x%p\n", check); + printk(KERN_INFO "PnPBIOS: PnP BIOS version %d.%d, entry 0x%x:0x%x, dseg 0x%x\n", + check->fields.version >> 4, check->fields.version & 15, + check->fields.pm16cseg, check->fields.pm16offset, + check->fields.pm16dseg); + Q2_SET_SEL(PNP_CS32, &pnp_bios_callfunc, 64 * 1024); + Q_SET_SEL(PNP_CS16, check->fields.pm16cseg, 64 * 1024); + Q_SET_SEL(PNP_DS, check->fields.pm16dseg, 64 * 1024); + pnp_bios_callpoint.offset = check->fields.pm16offset; + pnp_bios_callpoint.segment = PNP_CS16; + pnp_bios_hdr = check; + break; + } + if (!pnp_bios_present()) + return -ENODEV; + build_devlist(); + if ( ! dont_reserve_resources ) + reserve_resources(); +#ifdef CONFIG_PROC_FS + r = pnpbios_proc_init(); + if (r) + return r; +#endif + return 0; +} + +static int pnpbios_thread_init(void) +{ +#ifdef CONFIG_HOTPLUG + init_completion(&unload_sem); + if(kernel_thread(pnp_dock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL)>0) + unloading = 0; +#endif + return 0; +} + +#ifndef MODULE + +/* init/main.c calls pnpbios_init early */ + +/* Start the kernel thread later: */ +module_init(pnpbios_thread_init); + +#else + +/* + * N.B.: Building pnpbios as a module hasn't been fully implemented + */ + +MODULE_LICENSE("GPL"); + +static int pnpbios_init_all(void) +{ + int r; + r = pnpbios_init(); + if (r) + return r; + r = pnpbios_thread_init(); + if (r) + return r; + return 0; +} + +static void __exit pnpbios_exit(void) +{ +#ifdef CONFIG_HOTPLUG + unloading = 1; + wait_for_completion(&unload_sem); +#endif + pnpbios_proc_exit(); + /* We ought to free resources here */ + return; +} + +module_init(pnpbios_init_all); +module_exit(pnpbios_exit); + +#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/pnp/pnpbios_proc.c linux.21rc1-ac2/drivers/pnp/pnpbios_proc.c --- linux.21rc1/drivers/pnp/pnpbios_proc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/pnp/pnpbios_proc.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,278 @@ +/* + * /proc/bus/pnp interface for Plug and Play devices + * + * Written by David Hinds, dahinds@users.sourceforge.net + * Modified by Thomas Hood, jdthood@mail.com + * + * The .../devices and .../ and .../boot/ files are + * utilized by the lspnp and setpnp utilities, supplied with the + * pcmcia-cs package. + * http://pcmcia-cs.sourceforge.net + * + * The .../escd file is utilized by the lsescd utility written by + * Gunther Mayer. + * http://home.t-online.de/home/gunther.mayer/lsescd + * + * The .../legacy_device_resources file is not used yet. + * + * The other files are human-readable. + */ + +//#include +#define __NO_VERSION__ +//#include + +#include +#include +#include +#include +#include +#include + +static struct proc_dir_entry *proc_pnp = NULL; +static struct proc_dir_entry *proc_pnp_boot = NULL; +static struct pnp_dev_node_info node_info; + +static int proc_read_pnpconfig(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_isa_config_struc pnps; + + if (pnp_bios_isapnp_config(&pnps)) + return -EIO; + return snprintf(buf, count, + "structure_revision %d\n" + "number_of_CSNs %d\n" + "ISA_read_data_port 0x%x\n", + pnps.revision, + pnps.no_csns, + pnps.isa_rd_data_port + ); +} + +static int proc_read_escdinfo(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct escd_info_struc escd; + + if (pnp_bios_escd_info(&escd)) + return -EIO; + return snprintf(buf, count, + "min_ESCD_write_size %d\n" + "ESCD_size %d\n" + "NVRAM_base 0x%x\n", + escd.min_escd_write_size, + escd.escd_size, + escd.nv_storage_base + ); +} + +#define MAX_SANE_ESCD_SIZE (32*1024) +static int proc_read_escd(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct escd_info_struc escd; + char *tmpbuf; + int escd_size, escd_left_to_read, n; + + if (pnp_bios_escd_info(&escd)) + return -EIO; + + /* sanity check */ + if (escd.escd_size > MAX_SANE_ESCD_SIZE) { + printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great\n"); + return -EFBIG; + } + + tmpbuf = pnpbios_kmalloc(escd.escd_size, GFP_KERNEL); + if (!tmpbuf) return -ENOMEM; + + if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) + return -EIO; + + escd_size = (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1])*256; + + /* sanity check */ + if (escd_size > MAX_SANE_ESCD_SIZE) { + printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by BIOS read_escd call is too great\n"); + return -EFBIG; + } + + escd_left_to_read = escd_size - pos; + if (escd_left_to_read < 0) escd_left_to_read = 0; + if (escd_left_to_read == 0) *eof = 1; + n = min(count,escd_left_to_read); + memcpy(buf, tmpbuf + pos, n); + kfree(tmpbuf); + *start = buf; + return n; +} + +static int proc_read_legacyres(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + /* Assume that the following won't overflow the buffer */ + if (pnp_bios_get_stat_res(buf)) + return -EIO; + + return count; // FIXME: Return actual length +} + +static int proc_read_devices(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + u8 nodenum; + char *p = buf; + + if (pos >= 0xff) + return 0; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + + for (nodenum=pos; nodenum<0xff; ) { + u8 thisnodenum = nodenum; + /* 26 = the number of characters per line sprintf'ed */ + if ((p - buf + 26) > count) + break; + if (pnp_bios_get_dev_node(&nodenum, 1, node)) + break; + p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", + node->handle, node->eisa_id, + node->type_code[0], node->type_code[1], + node->type_code[2], node->flags); + if (nodenum <= thisnodenum) { + printk(KERN_ERR "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", "PnPBIOS: proc_read_devices:", (unsigned int)nodenum, (unsigned int)thisnodenum); + *eof = 1; + break; + } + } + kfree(node); + if (nodenum == 0xff) + *eof = 1; + *start = (char *)((off_t)nodenum - pos); + return p - buf; +} + +static int proc_read_node(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + int len; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if (pnp_bios_get_dev_node(&nodenum, boot, node)) + return -EIO; + len = node->size - sizeof(struct pnp_bios_node); + memcpy(buf, node->data, len); + kfree(node); + return len; +} + +static int proc_write_node(struct file *file, const char *buf, + unsigned long count, void *data) +{ + struct pnp_bios_node *node; + int boot = (long)data >> 8; + u8 nodenum = (long)data; + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) return -ENOMEM; + if ( pnp_bios_get_dev_node(&nodenum, boot, node) ) + return -EIO; + if (count != node->size - sizeof(struct pnp_bios_node)) + return -EINVAL; + memcpy(node->data, buf, count); + if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) + return -EINVAL; + kfree(node); + return count; +} + +/* + * When this is called, pnpbios functions are assumed to + * work and the pnpbios_dont_use_current_config flag + * should already have been set to the appropriate value + */ +int __init pnpbios_proc_init( void ) +{ + struct pnp_bios_node *node; + struct proc_dir_entry *ent; + char name[3]; + u8 nodenum; + + if (pnp_bios_dev_node_info(&node_info)) + return -EIO; + + proc_pnp = proc_mkdir("pnp", proc_bus); + if (!proc_pnp) + return -EIO; + proc_pnp_boot = proc_mkdir("boot", proc_pnp); + if (!proc_pnp_boot) + return -EIO; + create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL); + create_proc_read_entry("configuration_info", 0, proc_pnp, proc_read_pnpconfig, NULL); + create_proc_read_entry("escd_info", S_IRUSR, proc_pnp, proc_read_escdinfo, NULL); + create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL); + create_proc_read_entry("legacy_device_resources", 0, proc_pnp, proc_read_legacyres, NULL); + + node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); + if (!node) + return -ENOMEM; + + for (nodenum=0; nodenum<0xff; ) { + u8 thisnodenum = nodenum; + if (pnp_bios_get_dev_node(&nodenum, 1, node) != 0) + break; + sprintf(name, "%02x", node->handle); + if ( !pnpbios_dont_use_current_config ) { + ent = create_proc_entry(name, 0, proc_pnp); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle); + } + } + ent = create_proc_entry(name, 0, proc_pnp_boot); + if (ent) { + ent->read_proc = proc_read_node; + ent->write_proc = proc_write_node; + ent->data = (void *)(long)(node->handle+0x100); + } + if (nodenum <= thisnodenum) { + printk(KERN_ERR "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", "PnPBIOS: proc_init:", (unsigned int)nodenum, (unsigned int)thisnodenum); + break; + } + } + kfree(node); + + return 0; +} + +void __exit pnpbios_proc_exit(void) +{ + int i; + char name[3]; + + if (!proc_pnp) return; + + for (i=0; i<0xff; i++) { + sprintf(name, "%02x", i); + if ( !pnpbios_dont_use_current_config ) + remove_proc_entry(name, proc_pnp); + remove_proc_entry(name, proc_pnp_boot); + } + remove_proc_entry("legacy_device_resources", proc_pnp); + remove_proc_entry("escd", proc_pnp); + remove_proc_entry("escd_info", proc_pnp); + remove_proc_entry("configuration_info", proc_pnp); + remove_proc_entry("devices", proc_pnp); + remove_proc_entry("boot", proc_pnp); + remove_proc_entry("pnp", proc_bus); + + return; +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_3990_erp.c linux.21rc1-ac2/drivers/s390/block/dasd_3990_erp.c --- linux.21rc1/drivers/s390/block/dasd_3990_erp.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_3990_erp.c 2003-04-25 13:49:34.000000000 +0100 @@ -5,6 +5,8 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 * + * $Revision: 1.50 $ + * * History of changes: * 05/14/01 fixed PL030160GTO (BUG() in erp_action_5) */ @@ -41,7 +43,9 @@ char *nl, *end_cqr, *begin, - *end; + *end, + buffer[80]; + /* dump sense data */ if (device->discipline && @@ -54,83 +58,130 @@ /* log the channel program */ while (loop_cqr != NULL) { - DASD_MESSAGE (KERN_ERR, device, - "(%s) ERP chain report for req: %p", - caller == 0 ? "EXAMINE" : "ACTION", - loop_cqr); + DEV_MESSAGE (KERN_ERR, device, + "(%s) ERP chain report for req: %p", + caller == 0 ? "EXAMINE" : "ACTION", + loop_cqr); nl = (char *) loop_cqr; end_cqr = nl + sizeof (ccw_req_t); while (nl < end_cqr) { + + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); - DASD_MESSAGE (KERN_ERR, device, - "%p: %02x%02x%02x%02x %02x%02x%02x%02x " - "%02x%02x%02x%02x %02x%02x%02x%02x", - nl, - nl[0], nl[1], nl[2], nl[3], - nl[4], nl[5], nl[6], nl[7], - nl[8], nl[9], nl[10], nl[11], - nl[12], nl[13], nl[14], nl[15]); nl +=16; } - + + DEV_MESSAGE (KERN_ERR, device, + "DATA area is at: %p", + loop_cqr->data); + + nl = (char *) loop_cqr->data; + end_cqr = nl + loop_cqr->datasize; + + while (nl < end_cqr) { + + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); + + nl +=16; + } + nl = (char *) loop_cqr->cpaddr; if (loop_cqr->cplength > 40) { /* log only parts of the CP */ - DASD_MESSAGE (KERN_ERR, device, "%s", + DEV_MESSAGE (KERN_ERR, device, "%s", "Start of channel program:"); for (i = 0; i < 20; i += 2) { - DASD_MESSAGE (KERN_ERR, device, - "%p: %02x%02x%02x%02x %02x%02x%02x%02x " - "%02x%02x%02x%02x %02x%02x%02x%02x", - nl, - nl[0], nl[1], nl[2], nl[3], - nl[4], nl[5], nl[6], nl[7], - nl[8], nl[9], nl[10], nl[11], - nl[12], nl[13], nl[14], nl[15]); + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); + nl += 16; } - DASD_MESSAGE (KERN_ERR, device, "%s", - "End of channel program:"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "End of channel program:"); nl = (char *) loop_cqr->cpaddr; nl += ((loop_cqr->cplength - 10) * 8); for (i = 0; i < 20; i += 2) { - DASD_MESSAGE (KERN_ERR, device, - "%p: %02x%02x%02x%02x %02x%02x%02x%02x " - "%02x%02x%02x%02x %02x%02x%02x%02x", - nl, - nl[0], nl[1], nl[2], nl[3], - nl[4], nl[5], nl[6], nl[7], - nl[8], nl[9], nl[10], nl[11], - nl[12], nl[13], nl[14], nl[15]); + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); nl += 16; } } else { /* log the whole CP */ - DASD_MESSAGE (KERN_ERR, device, "%s", + DEV_MESSAGE (KERN_ERR, device, "%s", "Channel program (complete):"); for (i = 0; i < (loop_cqr->cplength + 4); i += 2) { - DASD_MESSAGE (KERN_ERR, device, - "%p: %02x%02x%02x%02x %02x%02x%02x%02x " - "%02x%02x%02x%02x %02x%02x%02x%02x", - nl, - nl[0], nl[1], nl[2], nl[3], - nl[4], nl[5], nl[6], nl[7], - nl[8], nl[9], nl[10], nl[11], - nl[12], nl[13], nl[14], nl[15]); + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); nl += 16; } @@ -150,29 +201,34 @@ nl -= 10*8; /* start some bytes before */ - DASD_MESSAGE (KERN_ERR, device, - "Failed CCW (%p) (area):", - (void *)(long)cpa); + DEV_MESSAGE (KERN_ERR, device, + "Failed CCW (%p) (area):", + (void *)(long)cpa); for (i = 0; i < 20; i += 2) { - DASD_MESSAGE (KERN_ERR, device, - "%p: %02x%02x%02x%02x %02x%02x%02x%02x " - "%02x%02x%02x%02x %02x%02x%02x%02x", - nl, - nl[0], nl[1], nl[2], nl[3], - nl[4], nl[5], nl[6], nl[7], - nl[8], nl[9], nl[10], nl[11], - nl[12], nl[13], nl[14], nl[15]); + sprintf (buffer, + "%p: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x", + nl, + nl[0], nl[1], nl[2], nl[3], + nl[4], nl[5], nl[6], nl[7], + nl[8], nl[9], nl[10], nl[11], + nl[12], nl[13], nl[14], nl[15]); + + DEV_MESSAGE (KERN_ERR, device, "%s", + buffer); nl += 16; } } else { - DASD_MESSAGE (KERN_ERR, device, - "Failed CCW (%p) already logged", - (void *)(long)cpa); + DEV_MESSAGE (KERN_ERR, device, + "Failed CCW (%p) already logged", + (void *)(long)cpa); } } @@ -216,9 +272,9 @@ if (( sense[0] & SNS0_CMD_REJECT ) && (!(sense[2] & SNS2_ENV_DATA_PRESENT)) ) { - DASD_MESSAGE (KERN_ERR, device, "%s", - "EXAMINE 24: Command Reject detected - " - "fatal error"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "EXAMINE 24: Command Reject detected - " + "fatal error"); return dasd_era_fatal; } @@ -227,9 +283,9 @@ if (( sense[1] & SNS1_INV_TRACK_FORMAT ) && (!(sense[2] & SNS2_ENV_DATA_PRESENT)) ) { - DASD_MESSAGE (KERN_ERR, device, "%s", - "EXAMINE 24: Invalid Track Format detected " - "- fatal error"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "EXAMINE 24: Invalid Track Format detected " + "- fatal error"); return dasd_era_fatal; } @@ -237,10 +293,10 @@ /* check for 'No Record Found' */ if (sense[1] & SNS1_NO_REC_FOUND) { - DASD_MESSAGE (KERN_ERR, device, - "EXAMINE 24: No Record Found detected %s", - cqr == device->init_cqr ? " " : - "- fatal error"); + DEV_MESSAGE (KERN_ERR, device, + "EXAMINE 24: No Record Found detected %s", + cqr == device->init_cqr ? " " : + "- fatal error"); return dasd_era_fatal; } @@ -274,8 +330,9 @@ return dasd_era_none; case 0x01: - DASD_MESSAGE (KERN_ERR, device, "%s", - "EXAMINE 32: fatal error"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "EXAMINE 32: fatal error"); + return dasd_era_fatal; default: @@ -405,20 +462,24 @@ dasd_device_t *device = erp->device; - DASD_MESSAGE (KERN_INFO, device, - "blocking request queue for %is", - (int) expires); + DEV_MESSAGE (KERN_INFO, device, + "blocking request queue for %is", + (int) expires); check_then_set (&erp->status, CQR_STATUS_ERROR, CQR_STATUS_PENDING); /* restart queue after some time */ - device->timer.function = dasd_3990_erp_restart_queue; - device->timer.data = (unsigned long) erp; - device->timer.expires = jiffies + (expires * HZ); - - add_timer(&device->timer); + if (!timer_pending(&device->blocking_timer)) { + init_timer(&device->blocking_timer); + device->blocking_timer.function = dasd_3990_erp_restart_queue; + device->blocking_timer.data = (unsigned long) erp; + device->blocking_timer.expires = jiffies + (expires * HZ); + add_timer(&device->blocking_timer); + } else { + mod_timer(&device->blocking_timer, jiffies + (expires * HZ)); + } } /* end dasd_3990_erp_block_queue */ @@ -453,8 +514,8 @@ /* 'restart' the device queue */ if (cqr->status == CQR_STATUS_PENDING) { - DASD_MESSAGE (KERN_INFO, device, "%s", - "request queue restarted by MIH"); + DEV_MESSAGE (KERN_INFO, device, "%s", + "request queue restarted by MIH"); check_then_set (&cqr->status, CQR_STATUS_PENDING, @@ -498,9 +559,9 @@ } else { /* issue a message and wait for 'device ready' interrupt */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "is offline or not installed - " - "INTERVENTION REQUIRED!!"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "is offline or not installed - " + "INTERVENTION REQUIRED!!"); dasd_3990_erp_block_queue (erp, 60); @@ -538,11 +599,11 @@ if ((erp->lpm & ioinfo[irq]->opm) != 0x00) { - DASD_MESSAGE (KERN_DEBUG, device, - "try alternate lpm=%x (lpum=%x / opm=%x)", - erp->lpm, - erp->dstat->lpum, - ioinfo[irq]->opm); + DEV_MESSAGE (KERN_DEBUG, device, + "try alternate lpm=%x (lpum=%x / opm=%x)", + erp->lpm, + erp->dstat->lpum, + ioinfo[irq]->opm); /* reset status to queued to handle the request again... */ check_then_set (&erp->status, @@ -553,11 +614,11 @@ } else { - DASD_MESSAGE (KERN_ERR, device, - "No alternate channel path left (lpum=%x / " - "opm=%x) -> permanent error", - erp->dstat->lpum, - ioinfo[irq]->opm); + DEV_MESSAGE (KERN_ERR, device, + "No alternate channel path left (lpum=%x / " + "opm=%x) -> permanent error", + erp->dstat->lpum, + ioinfo[irq]->opm); /* post request with permanent error */ check_then_set (&erp->status, @@ -598,8 +659,8 @@ if (!dctl_cqr) { - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate DCTL-CQR"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate DCTL-CQR"); check_then_set (&erp->status, CQR_STATUS_ERROR, @@ -620,8 +681,9 @@ if (dasd_set_normalized_cda(ccw, __pa (DCTL_data), dctl_cqr, erp->device)) { dasd_free_request (dctl_cqr, erp->device); - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate DCTL-CQR"); + + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate DCTL-CQR"); check_then_set (&erp->status, CQR_STATUS_ERROR, @@ -635,7 +697,8 @@ dctl_cqr->lpm = LPM_ANYPATH; dctl_cqr->expires = 5 * TOD_MIN; dctl_cqr->retries = 2; - asm volatile ("STCK %0":"=m" (dctl_cqr->buildclk)); + + dctl_cqr->buildclk = get_clock (); dctl_cqr->status = CQR_STATUS_FILLED; @@ -706,17 +769,16 @@ } else { - if (sense[25] & 0x1D) { /* state change pending */ + if (sense[25] == 0x1D) { /* state change pending */ - DASD_MESSAGE (KERN_INFO, device, "%s", - "waiting for state change pending " - "int"); + DEV_MESSAGE (KERN_INFO, device, "%s", + "waiting for state change pending " + "int"); dasd_3990_erp_block_queue (erp, 30); - } else { - + DEV_MESSAGE (KERN_INFO, device, "redriving request immediately, %d retries left", erp->retries); /* no state change pending - retry */ check_then_set (&erp->status, CQR_STATUS_ERROR, @@ -793,98 +855,98 @@ case 0x00: /* No Message */ break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Invalid Command"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Invalid Command"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Invalid Command " - "Sequence"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Invalid Command " + "Sequence"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - CCW Count less than " - "required"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - CCW Count less than " + "required"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Invalid Parameter"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Invalid Parameter"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Diagnostic of Sepecial" - " Command Violates File Mask"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Diagnostic of Sepecial" + " Command Violates File Mask"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Channel Returned with " - "Incorrect retry CCW"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Channel Returned with " + "Incorrect retry CCW"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Reset Notification"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Reset Notification"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Storage Path Restart"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Storage Path Restart"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, - "FORMAT 0 - Channel requested " - "... %02x", - sense[8]); + DEV_MESSAGE (KERN_WARNING, device, + "FORMAT 0 - Channel requested " + "... %02x", + sense[8]); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Invalid Defective/" - "Alternate Track Pointer"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Invalid Defective/" + "Alternate Track Pointer"); break; case 0x0C: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - DPS Installation " - "Check"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - DPS Installation " + "Check"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Command Invalid on " - "Secondary Address"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Command Invalid on " + "Secondary Address"); break; case 0x0F: - DASD_MESSAGE (KERN_WARNING, device, - "FORMAT 0 - Status Not As " - "Required: reason %02x", - sense[8]); + DEV_MESSAGE (KERN_WARNING, device, + "FORMAT 0 - Status Not As " + "Required: reason %02x", + sense[8]); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Reseved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Reseved"); } } else { switch (msg_no) { case 0x00: /* No Message */ break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Device Error Source"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Device Error Source"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Reserved"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, - "FORMAT 0 - Device Fenced - " - "device = %02x", - sense[4]); + DEV_MESSAGE (KERN_WARNING, device, + "FORMAT 0 - Device Fenced - " + "device = %02x", + sense[4]); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Data Pinned for " - "Device"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Data Pinned for " + "Device"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 0 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 0 - Reserved"); } } break; @@ -894,348 +956,348 @@ case 0x00: /* No Message */ break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Device Status 1 not as " - "expected"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Device Status 1 not as " + "expected"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Index missing"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Index missing"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Interruption cannot be reset"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Interruption cannot be reset"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Device did not respond to " - "selection"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Device did not respond to " + "selection"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Device check-2 error or Set " - "Sector is not complete"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Device check-2 error or Set " + "Sector is not complete"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Head address does not " - "compare"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Head address does not " + "compare"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Device status 1 not valid"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Device status 1 not valid"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Device not ready"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Device not ready"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Track physical address did " - "not compare"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Track physical address did " + "not compare"); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Missing device address bit"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Missing device address bit"); break; case 0x0C: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Drive motor switch is off"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Drive motor switch is off"); break; case 0x0D: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Seek incomplete"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Seek incomplete"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Cylinder address did not " - "compare"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Cylinder address did not " + "compare"); break; case 0x0F: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Offset active cannot be " - "reset"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Offset active cannot be " + "reset"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 1 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 1 - Reserved"); } break; case 0x20: /* Format 2 - 3990 Equipment Checks */ switch (msg_no) { case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 2 - 3990 check-2 error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 2 - 3990 check-2 error"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 2 - Support facility errors"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 2 - Support facility errors"); break; case 0x0F: - DASD_MESSAGE (KERN_WARNING, device, - "FORMAT 2 - Microcode detected error %02x", - sense[8]); + DEV_MESSAGE (KERN_WARNING, device, + "FORMAT 2 - Microcode detected error %02x", + sense[8]); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 2 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 2 - Reserved"); } break; case 0x30: /* Format 3 - 3990 Control Checks */ switch (msg_no) { case 0x0F: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 3 - Allegiance terminated"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 3 - Allegiance terminated"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 3 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 3 - Reserved"); } break; case 0x40: /* Format 4 - Data Checks */ switch (msg_no) { case 0x00: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Home address area error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Home address area error"); break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Count area error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Count area error"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Key area error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Key area error"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Data area error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Data area error"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in home address " + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in home address " "area"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in count address " - "area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in count address " + "area"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in key area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in key area"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in data area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in data area"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Home address area error; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Home address area error; " + "offset active"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Count area error; offset " - "active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Count area error; offset " + "active"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Key area error; offset " - "active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Key area error; offset " + "active"); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Data area error; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Data area error; " + "offset active"); break; case 0x0C: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in home " - "address area; offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in home " + "address area; offset active"); break; case 0x0D: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No syn byte in count " - "address area; offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No syn byte in count " + "address area; offset active"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No sync byte in key area; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No sync byte in key area; " + "offset active"); break; case 0x0F: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - No syn byte in data area; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - No syn byte in data area; " + "offset active"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 4 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 4 - Reserved"); } break; case 0x50: /* Format 5 - Data Check with displacement information */ switch (msg_no) { case 0x00: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the " - "home address area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the " + "home address area"); break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the count area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the count area"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the key area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the key area"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the data area"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the data area"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the " - "home address area; offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the " + "home address area; offset active"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the count area; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the count area; " + "offset active"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the key area; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the key area; " + "offset active"); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Data Check in the data area; " - "offset active"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Data Check in the data area; " + "offset active"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 5 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 5 - Reserved"); } break; case 0x60: /* Format 6 - Usage Statistics/Overrun Errors */ switch (msg_no) { case 0x00: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel A"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel A"); break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel B"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel B"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel C"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel C"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel D"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel D"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel E"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel E"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel F"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel F"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel G"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel G"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Overrun on channel H"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Overrun on channel H"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 6 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 6 - Reserved"); } break; case 0x70: /* Format 7 - Device Connection Control Checks */ switch (msg_no) { case 0x00: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - RCC initiated by a connection " - "check alert"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - RCC initiated by a connection " + "check alert"); break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - RCC 1 sequence not " - "successful"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - RCC 1 sequence not " + "successful"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - RCC 1 and RCC 2 sequences not " - "successful"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - RCC 1 and RCC 2 sequences not " + "successful"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Invalid tag-in during " - "selection sequence"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Invalid tag-in during " + "selection sequence"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - extra RCC required"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - extra RCC required"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Invalid DCC selection " - "response or timeout"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Invalid DCC selection " + "response or timeout"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Missing end operation; device " - "transfer complete"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Missing end operation; device " + "transfer complete"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Missing end operation; device " - "transfer incomplete"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Missing end operation; device " + "transfer incomplete"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Invalid tag-in for an " - "immediate command sequence"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Invalid tag-in for an " + "immediate command sequence"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Invalid tag-in for an " - "extended command sequence"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Invalid tag-in for an " + "extended command sequence"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - 3990 microcode time out when " - "stopping selection"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - 3990 microcode time out when " + "stopping selection"); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - No response to selection " - "after a poll interruption"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - No response to selection " + "after a poll interruption"); break; case 0x0C: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Permanent path error (DASD " - "controller not available)"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Permanent path error (DASD " + "controller not available)"); break; case 0x0D: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - DASD controller not available" - " on disconnected command chain"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - DASD controller not available" + " on disconnected command chain"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 7 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 7 - Reserved"); } break; @@ -1243,52 +1305,52 @@ switch (msg_no) { case 0x00: /* No Message */ case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - Error correction code " - "hardware fault"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - Error correction code " + "hardware fault"); break; case 0x03: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - Unexpected end operation " - "response code"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - Unexpected end operation " + "response code"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - End operation with transfer " - "count not zero"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - End operation with transfer " + "count not zero"); break; case 0x05: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - End operation with transfer " - "count zero"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - End operation with transfer " + "count zero"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - DPS checks after a system " - "reset or selective reset"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - DPS checks after a system " + "reset or selective reset"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - DPS cannot be filled"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - DPS cannot be filled"); break; case 0x08: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - Short busy time-out during " - "device selection"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - Short busy time-out during " + "device selection"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - DASD controller failed to " - "set or reset the long busy latch"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - DASD controller failed to " + "set or reset the long busy latch"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - No interruption from device " - "during a command chain"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - No interruption from device " + "during a command chain"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 8 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 8 - Reserved"); } break; @@ -1297,94 +1359,97 @@ case 0x00: break; /* No Message */ case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 9 - Device check-2 error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 9 - Device check-2 error"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 9 - Head address did not compare"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 9 - Head address did not compare"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 9 - Track physical address did " - "not compare while oriented"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 9 - Track physical address did " + "not compare while oriented"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 9 - Cylinder address did not " - "compare"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 9 - Cylinder address did not " + "compare"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT 9 - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT 9 - Reserved"); } break; case 0xF0: /* Format F - Cache Storage Checks */ switch (msg_no) { case 0x00: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Operation Terminated"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Operation Terminated"); break; case 0x01: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Subsystem Processing Error"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Subsystem Processing Error"); break; case 0x02: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Cache or nonvolatile storage " - "equipment failure"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Cache or nonvolatile storage " + "equipment failure"); break; case 0x04: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Caching terminated"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Caching terminated"); break; case 0x06: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Cache fast write access not " - "authorized"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Cache fast write access not " + "authorized"); break; case 0x07: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Track format incorrect"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Track format incorrect"); break; case 0x09: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Caching reinitiated"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Caching reinitiated"); break; case 0x0A: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Nonvolatile storage " - "terminated"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Nonvolatile storage " + "terminated"); break; case 0x0B: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Volume is suspended duplex"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Volume is suspended duplex"); break; case 0x0C: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Subsystem status connot be " - "determined"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Subsystem status connot be " + "determined"); break; case 0x0D: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - Caching status reset to " - "default"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - Caching status reset to " + "default"); break; case 0x0E: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT F - DASD Fast Write inhibited"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT F - DASD Fast Write inhibited"); break; default: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "FORMAT D - Reserved"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "FORMAT D - Reserved"); } break; default: /* unknown message format - should not happen */ - + DEV_MESSAGE (KERN_WARNING, device, + "unknown message format %02x", + msg_format); + } /* end switch message format */ - + } /* end dasd_3990_handle_env_data */ /* @@ -1412,8 +1477,8 @@ /* env data present (ACTION 10 - retry should work) */ if (sense[2] & SNS2_ENV_DATA_PRESENT) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Command Reject - environmental data present"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Command Reject - environmental data present"); dasd_3990_handle_env_data (erp, sense); @@ -1422,8 +1487,8 @@ } else { /* fatal error - set status to FAILED */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "Command Reject - Fatal error"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Command Reject - Fatal error"); erp = dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -1460,9 +1525,9 @@ } else { /* issue a message and wait for 'device ready' interrupt */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "bus out parity error or BOPC requested by " - "channel"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "bus out parity error or BOPC requested by " + "channel"); dasd_3990_erp_block_queue (erp, 60); @@ -1495,22 +1560,22 @@ if (sense[1] & SNS1_WRITE_INHIBITED) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Write inhibited path encountered"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Write inhibited path encountered"); /* vary path offline */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "Path should be varied off-line. " - "This is not implemented yet \n - please report " - "to linux390@de.ibm.com"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Path should be varied off-line. " + "This is not implemented yet \n - please report " + "to linux390@de.ibm.com"); erp = dasd_3990_erp_action_1 (erp); } else if (sense[2] & SNS2_ENV_DATA_PRESENT) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Equipment Check - " - "environmental data present"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Equipment Check - " + "environmental data present"); dasd_3990_handle_env_data (erp, sense); @@ -1519,17 +1584,18 @@ sense); } else if (sense[1] & SNS1_PERM_ERR) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Equipment Check - retry exhausted or " - "undesirable"); + + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Equipment Check - retry exhausted or " + "undesirable"); erp = dasd_3990_erp_action_1 (erp); } else { /* all other equipment checks - Action 5 */ /* rest is done when retries == 0 */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Equipment check or processing error"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Equipment check or processing error"); erp = dasd_3990_erp_action_5 (erp); } @@ -1561,9 +1627,9 @@ if (sense[2] & SNS2_CORRECTABLE) { /* correctable data check */ /* issue message that the data has been corrected */ - DASD_MESSAGE (KERN_EMERG, device, "%s", - "Data recovered during retry with PCI " - "fetch mode active"); + DEV_MESSAGE (KERN_EMERG, device, "%s", + "Data recovered during retry with PCI " + "fetch mode active"); /* not possible to handle this situation in Linux */ panic("No way to inform appliction about the possibly " @@ -1571,26 +1637,26 @@ } else if (sense[2] & SNS2_ENV_DATA_PRESENT) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Uncorrectable data check recovered secondary " - "addr of duplex pair"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Uncorrectable data check recovered secondary " + "addr of duplex pair"); erp = dasd_3990_erp_action_4 (erp, sense); } else if (sense[1] & SNS1_PERM_ERR) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Uncorrectable data check with internal " - "retry exhausted"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Uncorrectable data check with internal " + "retry exhausted"); erp = dasd_3990_erp_action_1 (erp); } else { /* all other data checks */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Uncorrectable data check with retry count " - "exhausted..."); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Uncorrectable data check with retry count " + "exhausted..."); erp = dasd_3990_erp_action_5 (erp); } @@ -1619,9 +1685,9 @@ erp->function = dasd_3990_erp_overrun; - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Overrun - service overrun or overrun" - " error requested by channel"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Overrun - service overrun or overrun" + " error requested by channel"); erp = dasd_3990_erp_action_5 (erp); @@ -1651,9 +1717,9 @@ if (sense[2] & SNS2_ENV_DATA_PRESENT) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Track format error when destaging or " - "staging data"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Track format error when destaging or " + "staging data"); dasd_3990_handle_env_data (erp, sense); @@ -1662,9 +1728,9 @@ sense); } else { - DASD_MESSAGE (KERN_ERR, device, "%s", - "Invalid Track Format - Fatal error should have " - "been handled within the interrupt handler"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Invalid Track Format - Fatal error should have " + "been handled within the interrupt handler"); erp= dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -1692,8 +1758,8 @@ dasd_device_t *device = default_erp->device; - DASD_MESSAGE (KERN_ERR, device, "%s", - "End-of-Cylinder - must never happen"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "End-of-Cylinder - must never happen"); /* implement action 7 - BUG */ return dasd_3990_erp_cleanup (default_erp, @@ -1721,8 +1787,8 @@ erp->function = dasd_3990_erp_env_data; - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Environmental data present"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Environmental data present"); dasd_3990_handle_env_data (erp, sense); @@ -1761,9 +1827,9 @@ dasd_device_t *device = default_erp->device; - DASD_MESSAGE (KERN_ERR, device, "%s", - "No Record Found - Fatal error should " - "have been handled within the interrupt handler"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "No Record Found - Fatal error should " + "have been handled within the interrupt handler"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -1789,8 +1855,8 @@ dasd_device_t *device = erp->device; - DASD_MESSAGE (KERN_ERR, device, "%s", - "File Protected"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "File Protected"); return dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -1921,8 +1987,8 @@ erp->retries = 256; erp->function = dasd_3990_erp_action_10_32; - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Perform logging requested"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Perform logging requested"); return erp; @@ -1960,8 +2026,8 @@ char *LO_data; /* LO_eckd_data_t */ ccw1_t *ccw; - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Write not finished because of unexpected condition"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Write not finished because of unexpected condition"); default_erp->function = dasd_3990_erp_action_1B_32; @@ -1975,8 +2041,8 @@ /* for imprecise ending just do default erp */ if (sense[1] & 0x01) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Imprecise ending is set - just retry"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Imprecise ending is set - just retry"); return default_erp; } @@ -1987,9 +2053,9 @@ if (cpa == 0) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Unable to determine address of the CCW " - "to be restarted"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Unable to determine address of the CCW " + "to be restarted"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -2004,8 +2070,8 @@ if (!erp) { - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate ERP"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate ERP"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -2023,8 +2089,8 @@ if ((sense[3] == 0x01) && (LO_data[1] & 0x01) ){ - DASD_MESSAGE (KERN_ERR, device, "%s", - "BUG - this should not happen"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "BUG - this should not happen"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -2060,8 +2126,8 @@ if (dasd_set_normalized_cda (ccw, __pa (DE_data), erp, device)) { dasd_free_request (erp, device); - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate ERP"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate ERP"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -2076,8 +2142,8 @@ if (dasd_set_normalized_cda (ccw, __pa (LO_data), erp, device)){ dasd_free_request (erp, device); - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate ERP"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate ERP"); return dasd_3990_erp_cleanup (default_erp, CQR_STATUS_FAILED); @@ -2132,9 +2198,9 @@ char *LO_data; /* LO_eckd_data_t */ ccw1_t *ccw; - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Write not finished because of unexpected condition" - " - follow on"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Write not finished because of unexpected condition" + " - follow on"); /* determine the original cqr */ cqr = previous_erp; @@ -2146,8 +2212,8 @@ /* for imprecise ending just do default erp */ if (sense[1] & 0x01) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Imprecise ending is set - just retry"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Imprecise ending is set - just retry"); check_then_set (&previous_erp->status, CQR_STATUS_ERROR, @@ -2162,9 +2228,9 @@ if (cpa == 0) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "Unable to determine address of the CCW " - "to be restarted"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Unable to determine address of the CCW " + "to be restarted"); check_then_set (&previous_erp->status, CQR_STATUS_ERROR, @@ -2181,8 +2247,8 @@ if ((sense[3] == 0x01) && (LO_data[1] & 0x01) ){ - DASD_MESSAGE (KERN_ERR, device, "%s", - "BUG - this should not happen"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "BUG - this should not happen"); check_then_set (&previous_erp->status, CQR_STATUS_ERROR, @@ -2337,7 +2403,7 @@ switch (sense[28]) { case 0x17: /* issue a Diagnostic Control command with an - * Inhibit Write subcommand and controler modifier */ + * Inhibit Write subcommand and controler modifier */ erp = dasd_3990_erp_DCTL (erp, 0x20); break; @@ -2351,7 +2417,7 @@ break; default: - /* should not happen - continue */ + ; /* should not happen - continue */ } } @@ -2389,11 +2455,11 @@ /* set to suspended duplex state then restart */ dasd_device_t *device = erp->device; - DASD_MESSAGE (KERN_ERR, device, "%s", - "Set device to suspended duplex state should be " - "done!\n" - "This is not implemented yet (for compound ERP)" - " - please report to linux390@de.ibm.com"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Set device to suspended duplex state should be " + "done!\n" + "This is not implemented yet (for compound ERP)" + " - please report to linux390@de.ibm.com"); } @@ -2490,10 +2556,10 @@ switch (sense[25]) { case 0x00: /* success */ - DASD_MESSAGE (KERN_DEBUG, device, - "ERP called for successful request %p" - " - NO ERP necessary", - erp); + DEV_MESSAGE (KERN_DEBUG, device, + "ERP called for successful request %p" + " - NO ERP necessary", + erp); erp = dasd_3990_erp_cleanup (erp, CQR_STATUS_DONE); @@ -2501,9 +2567,9 @@ break; case 0x01: /* fatal error */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "Fatal error should have been " - "handled within the interrupt handler"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "Fatal error should have been " + "handled within the interrupt handler"); erp = dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -2515,12 +2581,12 @@ break; case 0x0F: /* length mismatch during update write command */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "update write command error - should not " - "happen;\n" - "Please send this message together with " - "the above sense data to linux390@de." - "ibm.com"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "update write command error - should not " + "happen;\n" + "Please send this message together with " + "the above sense data to linux390@de." + "ibm.com"); erp = dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -2532,12 +2598,12 @@ break; case 0x15: /* next track outside defined extend */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "next track outside defined extend - " - "should not happen;\n" - "Please send this message together with " - "the above sense data to linux390@de." - "ibm.com"); + DEV_MESSAGE (KERN_ERR, device, "%s", + "next track outside defined extend - " + "should not happen;\n" + "Please send this message together with " + "the above sense data to linux390@de." + "ibm.com"); erp= dasd_3990_erp_cleanup (erp, CQR_STATUS_FAILED); @@ -2550,9 +2616,9 @@ break; case 0x1C: /* invalid data */ - DASD_MESSAGE (KERN_EMERG, device, "%s", - "Data recovered during retry with PCI " - "fetch mode active"); + DEV_MESSAGE (KERN_EMERG, device, "%s", + "Data recovered during retry with PCI " + "fetch mode active"); /* not possible to handle this situation in Linux */ panic("Invalid data - No way to inform appliction about " @@ -2560,16 +2626,16 @@ break; case 0x1D: /* state-change pending */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "A State change pending condition exists " - "for the subsystem or device"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "A State change pending condition exists " + "for the subsystem or device"); erp = dasd_3990_erp_action_4 (erp, sense); break; - default: /* all others errors - default erp */ - + default: + ; /* all others errors - default erp */ } } @@ -2642,25 +2708,47 @@ { dasd_device_t *device = cqr->device; + ccw1_t *ccw; /* allocate additional request block */ - ccw_req_t *erp = dasd_alloc_request ((char *) &cqr->magic, 1, 0, cqr->device); + ccw_req_t *erp = dasd_alloc_request ((char *) &cqr->magic, 2, 0, cqr->device); if (!erp) { - - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate ERP request"); + if (cqr->retries <= 0) { + DEV_MESSAGE (KERN_ERR, device, "%s", + "Unable to allocate ERP request (NO retries left)"); - check_then_set (&cqr->status, - CQR_STATUS_ERROR, - CQR_STATUS_FAILED); + check_then_set (&cqr->status, + CQR_STATUS_ERROR, + CQR_STATUS_FAILED); + cqr->stopclk = get_clock (); + + } else { + DEV_MESSAGE (KERN_ERR, device, + "Unable to allocate ERP request (%i retries left)", + cqr->retries); + + if (!timer_pending(&device->timer)) { + init_timer (&device->timer); + device->timer.function = dasd_schedule_bh_timed; + device->timer.data = (unsigned long) device; + device->timer.expires = jiffies + (HZ << 3); + add_timer (&device->timer); + } else { + mod_timer(&device->timer, jiffies + (HZ << 3)); + } + } return cqr; } /* initialize request with default TIC to current ERP/CQR */ - erp->cpaddr->cmd_code = CCW_CMD_TIC; - erp->cpaddr->cda = (long)(cqr->cpaddr); + ccw = erp->cpaddr; + ccw->cmd_code = CCW_CMD_NOOP; + ccw->flags = CCW_FLAG_CC; + ccw++; + ccw->cmd_code = CCW_CMD_TIC; + ccw->cda = (long)(cqr->cpaddr); erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; erp->device = cqr->device; @@ -2738,7 +2826,7 @@ } /* check sense data; byte 0-2,25,27 */ - if (!((strncmp (cqr1->dstat->ii.sense.data, + if (!((memcmp (cqr1->dstat->ii.sense.data, cqr2->dstat->ii.sense.data, 3) == 0) && (cqr1->dstat->ii.sense.data[27] == @@ -2859,10 +2947,10 @@ break; } default: - DASD_MESSAGE (KERN_DEBUG, device, - "invalid subcommand modifier 0x%x " - "for Diagnostic Control Command", - sense[25]); + DEV_MESSAGE (KERN_DEBUG, device, + "invalid subcommand modifier 0x%x " + "for Diagnostic Control Command", + sense[25]); } } @@ -2877,10 +2965,10 @@ } else { /* no retry left and no additional special handling necessary */ - DASD_MESSAGE (KERN_ERR, device, - "no retries left for erp %p - " - "set status to FAILED", - erp); + DEV_MESSAGE (KERN_ERR, device, + "no retries left for erp %p - " + "set status to FAILED", + erp); check_then_set (&erp->status, CQR_STATUS_ERROR, @@ -2940,7 +3028,7 @@ if (erp->retries > 0) { - char *sense = erp->dstat->ii.sense.data; + char *sense = erp->refers->dstat->ii.sense.data; /* check for special retries */ if (erp->function == dasd_3990_erp_action_4) { @@ -2959,10 +3047,10 @@ } else { /* simple retry */ - DASD_MESSAGE (KERN_DEBUG, device, - "%i retries left for erp %p", - erp->retries, - erp); + DEV_MESSAGE (KERN_DEBUG, device, + "%i retries left for erp %p", + erp->retries, + erp); /* handle the request again... */ check_then_set (&erp->status, @@ -3006,26 +3094,20 @@ __u32 cpa = cqr->dstat->cpa; #ifdef ERP_DEBUG - DASD_MESSAGE (KERN_DEBUG, device, - "entering 3990 ERP for " - "0x%04X on sch %d = /dev/%s ", - device->devinfo.devno, - device->devinfo.irq, - device->name); - /* print current erp_chain */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "ERP chain at BEGINNING of ERP-ACTION"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "ERP chain at BEGINNING of ERP-ACTION"); { ccw_req_t *temp_erp = NULL; + for (temp_erp = cqr; temp_erp != NULL; temp_erp = temp_erp->refers){ - DASD_MESSAGE (KERN_DEBUG, device, - " erp %p refers to %p", - temp_erp, - temp_erp->refers); + DEV_MESSAGE (KERN_DEBUG, device, + " erp %p refers to %p", + temp_erp, + temp_erp->refers); } } #endif /* ERP_DEBUG */ @@ -3034,10 +3116,10 @@ if ((cqr->dstat->cstat == 0x00 ) && (cqr->dstat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) ) { - DASD_MESSAGE (KERN_DEBUG, device, - "ERP called for successful request %p" - " - NO ERP necessary", - cqr); + DEV_MESSAGE (KERN_DEBUG, device, + "ERP called for successful request %p" + " - NO ERP necessary", + cqr); check_then_set (&cqr->status, CQR_STATUS_ERROR, @@ -3047,10 +3129,10 @@ } /* check if sense data are available */ if (!cqr->dstat->ii.sense.data) { - DASD_MESSAGE (KERN_DEBUG, device, - "ERP called witout sense data avail ..." - "request %p - NO ERP possible", - cqr); + DEV_MESSAGE (KERN_DEBUG, device, + "ERP called witout sense data avail ..." + "request %p - NO ERP possible", + cqr); check_then_set (&cqr->status, CQR_STATUS_ERROR, @@ -3074,18 +3156,18 @@ #ifdef ERP_DEBUG /* print current erp_chain */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "ERP chain at END of ERP-ACTION"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "ERP chain at END of ERP-ACTION"); { ccw_req_t *temp_erp = NULL; for (temp_erp = erp; temp_erp != NULL; temp_erp = temp_erp->refers) { - DASD_MESSAGE (KERN_DEBUG, device, - " erp %p refers to %p", - temp_erp, - temp_erp->refers); + DEV_MESSAGE (KERN_DEBUG, device, + " erp %p refers to %p", + temp_erp, + temp_erp->refers); } } #endif /* ERP_DEBUG */ @@ -3098,35 +3180,32 @@ } /* enqueue added ERP request */ - if ((erp != cqr ) && + if ((erp != device->queue.head ) && (erp->status == CQR_STATUS_FILLED) ){ dasd_chanq_enq_head (&device->queue, erp); } else { - if ((erp->status == CQR_STATUS_FILLED )|| - (erp != cqr ) ){ - /* something strange happened - log the error and throw a BUG() */ - DASD_MESSAGE (KERN_ERR, device, "%s", - "Problems with ERP chain!!! BUG"); - + if ((erp->status == CQR_STATUS_FILLED ) || (erp != device->queue.head)) { + /* something strange happened - log the error and panic */ /* print current erp_chain */ - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "ERP chain at END of ERP-ACTION"); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "ERP chain at END of ERP-ACTION"); { ccw_req_t *temp_erp = NULL; for (temp_erp = erp; temp_erp != NULL; temp_erp = temp_erp->refers) { - DASD_MESSAGE (KERN_DEBUG, device, - " erp %p (function %p) refers to %p", - temp_erp, - temp_erp->function, - temp_erp->refers); + DEV_MESSAGE (KERN_DEBUG, device, + " erp %p (function %p)" + " refers to %p", + temp_erp, + temp_erp->function, + temp_erp->refers); } } - BUG(); + panic ("Problems with ERP chain!!! Please report to linux390@de.ibm.com"); } } @@ -3136,112 +3215,6 @@ } /* end dasd_3990_erp_action */ /* - * DASD_3990_ERP_POSTACTION - * - * DESCRIPTION - * Frees all ERPs of the current ERP Chain and set the status - * of the original CQR either to CQR_STATUS_DONE if ERP was successful - * or to CQR_STATUS_FAILED if ERP was NOT successful. - * - * PARAMETER - * erp current erp_head - * - * RETURN VALUES - * cqr pointer to the original CQR - */ -ccw_req_t * -dasd_3990_erp_postaction (ccw_req_t *erp) -{ - - ccw_req_t *cqr = NULL, - *free_erp = NULL; - dasd_device_t *device = erp->device; - int success; - - if (erp->refers == NULL || - erp->function == NULL ) { - - BUG (); - } - - if (erp->status == CQR_STATUS_DONE) - success = 1; - else - success = 0; - -#ifdef ERP_DEBUG - - /* print current erp_chain */ - printk (KERN_DEBUG PRINTK_HEADER - "3990 ERP postaction called for erp chain:\n"); - { - ccw_req_t *temp_erp = NULL; - - for (temp_erp = erp; - temp_erp != NULL; - temp_erp = temp_erp->refers) { - - printk (KERN_DEBUG PRINTK_HEADER - " erp %p refers to %p with erp function %p\n", - temp_erp, temp_erp->refers, temp_erp->function); - } - } - -#endif /* ERP_DEBUG */ - - /* free all ERPs - but NOT the original cqr */ - while (erp->refers != NULL) { - - free_erp = erp; - erp = erp->refers; - - /* remove the request from the device queue */ - dasd_chanq_deq (&device->queue, - free_erp); - - /* free the finished erp request */ - dasd_free_request (free_erp, free_erp->device); - } - - /* save ptr to original cqr */ - cqr = erp; - - /* set corresponding status to original cqr */ - if (success) { - - check_then_set (&cqr->status, - CQR_STATUS_ERROR, - CQR_STATUS_DONE); - } else { - - check_then_set (&cqr->status, - CQR_STATUS_ERROR, - CQR_STATUS_FAILED); - } - -#ifdef ERP_DEBUG - /* print current erp_chain */ - printk (KERN_DEBUG PRINTK_HEADER - "3990 ERP postaction finished with remaining chain:\n"); - { - ccw_req_t *temp_erp = NULL; - - for (temp_erp = cqr; - temp_erp != NULL; - temp_erp = temp_erp->refers) { - - printk (KERN_DEBUG PRINTK_HEADER - " erp %p refers to %p \n", temp_erp, - temp_erp->refers); - } - } -#endif /* ERP_DEBUG */ - - return cqr; - -} /* end dasd_3990_erp_postaction */ - -/* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd.c linux.21rc1-ac2/drivers/s390/block/dasd.c --- linux.21rc1/drivers/s390/block/dasd.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd.c 2003-04-25 13:49:33.000000000 +0100 @@ -6,6 +6,8 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * + * $Revision: 1.289 $ + * * History of changes (starts July 2000) * 11/09/00 complete redesign after code review * 02/01/01 added dynamic registration of ioctls @@ -33,6 +35,7 @@ * 06/26/01 hopefully fixed PL030172SBA,PL030234SBA * 07/09/01 fixed PL030324MSH (wrong statistics output) * 07/16/01 merged in new fixes for handling low-mem situations + * 01/22/01 fixed PL030579KBE (wrong statistics) */ #include @@ -70,7 +73,6 @@ #include #include #include -#include #include "dasd_int.h" @@ -84,8 +86,9 @@ #include "dasd_diag.h" #endif /* CONFIG_DASD_DIAG */ -/* SECTION: exported variables of dasd.c */ - +/******************************************************************************** + * SECTION: exported variables of dasd.c + ********************************************************************************/ debug_info_t *dasd_debug_area; MODULE_AUTHOR ("Holger Smolinski "); @@ -94,6 +97,9 @@ MODULE_SUPPORTED_DEVICE ("dasd"); MODULE_PARM (dasd, "1-" __MODULE_STRING (256) "s"); MODULE_PARM (dasd_disciplines, "1-" __MODULE_STRING (8) "s"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12)) +MODULE_LICENSE ("GPL"); +#endif EXPORT_SYMBOL (dasd_chanq_enq_head); EXPORT_SYMBOL (dasd_debug_area); EXPORT_SYMBOL (dasd_chanq_enq); @@ -103,6 +109,7 @@ EXPORT_SYMBOL (dasd_start_IO); EXPORT_SYMBOL (dasd_term_IO); EXPORT_SYMBOL (dasd_schedule_bh); +EXPORT_SYMBOL (dasd_schedule_bh_timed); EXPORT_SYMBOL (dasd_int_handler); EXPORT_SYMBOL (dasd_oper_handler); EXPORT_SYMBOL (dasd_alloc_request); @@ -115,17 +122,24 @@ EXPORT_SYMBOL (dasd_set_normalized_cda); EXPORT_SYMBOL (dasd_device_from_kdev); -/* SECTION: Constant definitions to be used within this file */ + +/******************************************************************************** + * SECTION: Constant definitions to be used within this file + ********************************************************************************/ #define PRINTK_HEADER DASD_NAME":" -#undef DASD_PROFILE /* fill profile information - used for */ +#define DASD_PROFILE /* fill profile information - used for */ /* statistics and perfomance */ -#define DASD_MIN_SIZE_FOR_QUEUE 32 -#undef CONFIG_DYNAMIC_QUEUE_MIN_SIZE -#define DASD_CHANQ_MAX_SIZE 6 - -/* SECTION: prototypes for static functions of dasd.c */ +#ifndef CONFIG_PROC_FS /* DASD_PROFILE doesn't make sense */ +#undef DASD_PROFILE /* without procfs */ +#endif /* not CONFIG_PROC_FS */ + +#define DASD_CHANQ_MAX_SIZE 4 + +/******************************************************************************** + * SECTION: prototypes for static functions of dasd.c + ********************************************************************************/ static request_fn_proc do_dasd_request; static int dasd_set_device_level (unsigned int, dasd_discipline_t *, int); @@ -147,7 +161,11 @@ static struct block_device_operations dasd_device_operations; static inline dasd_device_t ** dasd_device_from_devno (int); static void dasd_process_queues (dasd_device_t * device); -/* SECTION: static variables of dasd.c */ +static int dasd_sleep_on_immediate (ccw_req_t *cqr); + +/******************************************************************************** + * SECTION: static variables of dasd.c + ********************************************************************************/ static devfs_handle_t dasd_devfs_handle; static wait_queue_head_t dasd_init_waitq; @@ -155,7 +173,9 @@ #ifdef CONFIG_DASD_DYNAMIC -/* SECTION: managing dynamic configuration of dasd_driver */ +/******************************************************************************** + * SECTION: managing dynamic configuration of dasd_driver + ********************************************************************************/ static struct list_head dasd_devreg_head = LIST_HEAD_INIT (dasd_devreg_head); @@ -188,15 +208,17 @@ #endif /* CONFIG_DASD_DYNAMIC */ -/* SECTION: managing setup of dasd_driver */ +/******************************************************************************** + * SECTION: managing setup of dasd_driver + ********************************************************************************/ /* default setting is probeonly, autodetect */ -static int dasd_probeonly = 1; /* is true, when probeonly mode is active */ -static int dasd_autodetect = 1; /* is true, when autodetection is active */ +static int dasd_probeonly = 0; /* is true, when probeonly mode is active */ +static int dasd_autodetect = 0; /* is true, when autodetection is active */ static dasd_range_t dasd_range_head = { list:LIST_HEAD_INIT (dasd_range_head.list) }; -static spinlock_t range_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t range_lock = SPIN_LOCK_UNLOCKED; /* * function: dasd_create_range @@ -210,20 +232,24 @@ int i; if ( from > to ) { - printk (KERN_WARNING PRINTK_HEADER - "Adding device range %04x-%04x: range invalid, ignoring.\n", - from, - to); + + MESSAGE (KERN_WARNING, + "Adding device range %04x-%04x: " + "range invalid, ignoring.", + from, + to); return NULL; } for (i=from;i<=to;i++) { if (dasd_device_from_devno(i)) { - printk (KERN_WARNING PRINTK_HEADER - "device range %04x-%04x: device %04x is already in a range.\n", - from, - to, - i); + + MESSAGE (KERN_WARNING, + "device range %04x-%04x: device " + "%04x is already in a range.", + from, + to, + i); } } range = (dasd_range_t *) kmalloc (sizeof (dasd_range_t), GFP_KERNEL); @@ -392,7 +418,9 @@ return -ENODEV; } -/* SECTION: parsing the dasd= parameter of the parmline/insmod cmdline */ +/******************************************************************************** + * SECTION: parsing the dasd= parameter of the parmline/insmod cmdline + ********************************************************************************/ /* * char *dasd[] is intended to hold the ranges supplied by the dasd= statement @@ -426,11 +454,14 @@ *end = '\0'; end++; } - dasd[count] = kmalloc (len * sizeof (char), GFP_ATOMIC); + dasd[count] = kmalloc (len * sizeof (char), + GFP_ATOMIC); if (dasd[count] == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "can't store dasd= parameter no %d\n", - count + 1); + + MESSAGE (KERN_WARNING, + "can't store dasd= parameter no" + " %d", + count + 1); break; } memset (dasd[count], 0, len * sizeof (char)); @@ -500,10 +531,13 @@ int val,i,start; buffer=(char*)kmalloc((strlen(str)+1)*sizeof(char),GFP_ATOMIC); + if (buffer==NULL) { - printk (KERN_WARNING PRINTK_HEADER - "can't parse dasd= parameter %s due to low memory\n", - str); + + MESSAGE (KERN_WARNING, + "can't parse dasd= parameter %s due " + "to low memory", + str); } /* remove leading '0x' */ @@ -528,7 +562,7 @@ val = simple_strtoul (buffer, &buffer, 16); /* check for features - e.g. (ro) ; the '\0', ')' and '-' stops check */ - *features = DASD_DEFAULT_FEATURES; + *features = DASD_FEATURE_DEFAULT; if (temp[i]=='(') { @@ -545,14 +579,18 @@ (*features) |= DASD_FEATURE_READONLY; break; } - printk (KERN_WARNING PRINTK_HEADER - "unsupported feature: %s, ignoring setting", - buffer); + + MESSAGE (KERN_WARNING, + "unsupported feature: %s, " + "ignoring setting", + buffer); } } } *stra = temp+i; + if ((val > 65535) || (val < 0)) + return -EINVAL; return val; } @@ -569,22 +607,23 @@ int features; int rc = 0; - if (*str) { - /* turn off probeonly mode, if any dasd parameter is present */ - dasd_probeonly = 0; - dasd_autodetect = 0; - } while (*str) { temp = *str; from = 0; to = 0; if (strcmp ("autodetect", *str) == 0) { dasd_autodetect = 1; - printk (KERN_INFO "turning to autodetection mode\n"); + + MESSAGE (KERN_INFO, "%s", + "turning to autodetection mode"); + break; } else if (strcmp ("probeonly", *str) == 0) { dasd_probeonly = 1; - printk (KERN_INFO "turning to probeonly mode\n"); + + MESSAGE (KERN_INFO, "%s", + "turning to probeonly mode"); + break; } else { /* turn off autodetect mode, if any range is present */ @@ -609,20 +648,16 @@ return rc; } -/* SECTION: Dealing with devices registered to multiple major numbers */ +/******************************************************************************** + * SECTION: Dealing with devices registered to multiple major numbers + ********************************************************************************/ static spinlock_t dasd_major_lock = SPIN_LOCK_UNLOCKED; -static major_info_t dasd_major_info[] = { - { - list:LIST_HEAD_INIT (dasd_major_info[1].list) - }, - { - list:LIST_HEAD_INIT (dasd_major_info[0].list), - gendisk:{ - INIT_GENDISK (94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR) - }, - flags:DASD_MAJOR_INFO_IS_STATIC} +static struct list_head dasd_major_info = LIST_HEAD_INIT(dasd_major_info); +static major_info_t dasd_major_static = { + gendisk:{INIT_GENDISK(94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR)}, + flags: DASD_MAJOR_INFO_IS_STATIC }; static major_info_t * @@ -658,8 +693,11 @@ if (major_info == NULL) { major_info = get_new_major_info (); if (!major_info) { - printk (KERN_WARNING PRINTK_HEADER - "Cannot get memory to allocate another major number\n"); + + MESSAGE (KERN_WARNING, "%s", + "Cannot get memory to allocate another " + "major number"); + return -ENOMEM; } } @@ -686,10 +724,12 @@ /* register blockdevice */ rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations); if (rc < 0) { - printk (KERN_WARNING PRINTK_HEADER - "Cannot register to major no %d, rc = %d\n", - major, - rc); + + MESSAGE (KERN_WARNING, + "Cannot register to major no %d, rc = %d", + major, + rc); + goto out_reg_blkdev; } else { major_info->flags |= DASD_MAJOR_INFO_REGISTERED; @@ -698,7 +738,7 @@ /* Insert the new major info into dasd_major_info if needed (dynamic major) */ if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) { spin_lock_irqsave (&dasd_major_lock, flags); - list_add_tail (&major_info->list, &dasd_major_info[0].list); + list_add_tail (&major_info->list, &dasd_major_info); spin_unlock_irqrestore (&dasd_major_lock, flags); } @@ -791,10 +831,11 @@ /* unregister blockdevice */ rc = devfs_unregister_blkdev (major, DASD_NAME); if (rc < 0) { - printk (KERN_WARNING PRINTK_HEADER - "Unable to unregister from major no %d, rc = %d\n", - major, - rc); + + MESSAGE (KERN_WARNING, + "Unable to unregister from major no %d, rc = %d", + major, + rc); } else { major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED; } @@ -844,10 +885,12 @@ rc = devfs_unregister_blkdev (major, DASD_NAME); if (rc < 0) { - printk (KERN_WARNING PRINTK_HEADER - "Cannot unregister from major no %d, rc = %d\n", - major, - rc); + + MESSAGE (KERN_WARNING, + "Cannot unregister from major no %d, rc = %d", + major, + rc); + return rc; } else { major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED; @@ -874,20 +917,23 @@ dasd_device_t * dasd_device_from_kdev (kdev_t kdev) { - major_info_t *major_info = NULL; + major_info_t *major_info; + dasd_device_t *device; struct list_head *l; unsigned long flags; + device = NULL; spin_lock_irqsave (&dasd_major_lock, flags); - list_for_each (l, &dasd_major_info[0].list) { + list_for_each (l, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); - if (major_info->gendisk.major == MAJOR (kdev)) + if (major_info->gendisk.major == MAJOR (kdev)) { + device = major_info->dasd_device[MINOR (kdev) >> + DASD_PARTN_BITS]; break; + } } spin_unlock_irqrestore (&dasd_major_lock, flags); - if (major_info != &dasd_major_info[0]) - return major_info->dasd_device[MINOR (kdev) >> DASD_PARTN_BITS]; - return NULL; + return device; } /* @@ -900,21 +946,24 @@ dasd_device_from_devno (int devno) { major_info_t *major_info; + dasd_device_t **device; struct list_head *l; - int devindex = dasd_devindex_from_devno (devno); + int devindex; unsigned long flags; spin_lock_irqsave (&dasd_major_lock, flags); - list_for_each (l, &dasd_major_info[0].list) { + devindex = dasd_devindex_from_devno (devno); + device = NULL; + list_for_each (l, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); if (devindex < DASD_PER_MAJOR) { - spin_unlock_irqrestore (&dasd_major_lock, flags); - return &major_info->dasd_device[devindex]; + device = &major_info->dasd_device[devindex]; + break; } devindex -= DASD_PER_MAJOR; } spin_unlock_irqrestore (&dasd_major_lock, flags); - return NULL; + return device; } /* @@ -945,9 +994,33 @@ return -ENODEV; } +/* + * function: dasd_check_bp_block + * checks the blocksize and returns 0 if valid. + */ + +static int +dasd_check_bp_block (dasd_device_t *device) +{ + int rc; + switch (device->sizes.bp_block) { + case 512: + case 1024: + case 2048: + case 4096: + rc = 0; + break; + default: + rc = -EMEDIUMTYPE; + } + + return rc; +} -/* SECTION: managing dasd disciplines */ +/******************************************************************************** + * SECTION: managing dasd disciplines + ********************************************************************************/ /* anchor and spinlock for list of disciplines */ static struct list_head dasd_disc_head = LIST_HEAD_INIT(dasd_disc_head); @@ -955,14 +1028,12 @@ /* * function dasd_discipline_enq - * chains the discpline given as argument to the head of disiplines - * head chaining policy is required to allow module disciplines to - * be preferred against those, who are statically linked + * chains the discpline given as argument to the tail of disiplines */ static inline void -dasd_discipline_enq (dasd_discipline_t * d) +dasd_discipline_enq (dasd_discipline_t * discipline) { - list_add(&d->list, &dasd_disc_head); + list_add_tail (&discipline->list, &dasd_disc_head); } /* @@ -970,59 +1041,88 @@ * removes the discipline given as argument from the list of disciplines */ static inline void -dasd_discipline_deq (dasd_discipline_t * d) +dasd_discipline_deq (dasd_discipline_t * discipline) { - list_del(&d->list); + if (&discipline->list) { + list_del (&discipline->list); + } } void -dasd_discipline_add (dasd_discipline_t * d) +dasd_discipline_add (dasd_discipline_t * discipline) { unsigned long flags; MOD_INC_USE_COUNT; spin_lock_irqsave (&discipline_lock,flags); - dasd_discipline_enq (d); + dasd_discipline_enq (discipline); spin_unlock_irqrestore (&discipline_lock,flags); - dasd_enable_ranges (&dasd_range_head, d, DASD_STATE_ONLINE); + + dasd_enable_ranges (&dasd_range_head, + discipline, + DASD_STATE_ONLINE); } -void dasd_discipline_del (dasd_discipline_t * d) +void dasd_discipline_del (dasd_discipline_t * discipline) { unsigned long flags; + + dasd_disable_ranges(&dasd_range_head, + discipline, + DASD_STATE_DEL, + 1); + spin_lock_irqsave (&discipline_lock,flags); - dasd_disable_ranges(&dasd_range_head, d, DASD_STATE_DEL, 1); - dasd_discipline_deq (d); + dasd_discipline_deq (discipline); spin_unlock_irqrestore (&discipline_lock,flags); MOD_DEC_USE_COUNT; } +/* + * function dasd_find_disc + * checks the list of disciplines for the first one able to access the device + */ static inline dasd_discipline_t * -dasd_find_disc (dasd_device_t * device, dasd_discipline_t *d) +dasd_find_disc (dasd_device_t * device, dasd_discipline_t *discipline) { dasd_discipline_t *t; - struct list_head *l = d ? &d->list : dasd_disc_head.next; + struct list_head *l = discipline ? + &discipline->list : dasd_disc_head.next; + do { t = list_entry(l,dasd_discipline_t,list); + if ( ( t->id_check == NULL || t->id_check (&device->devinfo) == 0 ) && ( t->check_characteristics == NULL || t->check_characteristics (device) == 0 ) ) break; l = l->next; - if ( d || + if ( discipline || l == &dasd_disc_head ) { t = NULL; break; } } while ( 1 ); + return t; } -/* SECTION: profiling stuff */ +/******************************************************************************** + * SECTION: profiling stuff + ********************************************************************************/ + +#ifdef CONFIG_PROC_FS static dasd_profile_info_t dasd_global_profile; +#endif /* CONFIG_PROC_FS */ #ifdef DASD_PROFILE + +#define DASD_PROFILE_ON 1 +#define DASD_PROFILE_OFF 0 + +static unsigned int dasd_profile_level = DASD_PROFILE_OFF; + /* * macro: dasd_profile_add_counter * increments counter in global and local profiling structures @@ -1032,7 +1132,7 @@ { \ int ind; \ long help; \ - for (ind = 0, help = value >> 3; \ + for (ind = 0, help = value >> 2; \ ind < 31 && help; \ help = help >> 1, ind++) {} \ dasd_global_profile.counter[ind]++; \ @@ -1086,56 +1186,150 @@ dasd_profile_add_counter (irqtime / sectors, dasd_io_time2ps, device); dasd_profile_add_counter (endtime, dasd_io_time3, device); } -#endif +#endif /* DASD_PROFILE */ -/* SECTION: All the gendisk stuff */ +/******************************************************************************** + * SECTION: All the gendisk stuff + ********************************************************************************/ -/* SECTION: Managing wrappers for ccwcache */ +/******************************************************************************** + * SECTION: Managing wrappers for ccwcache + ********************************************************************************/ /* * function dasd_alloc_request * tries to return space for a channel program of length cplength with * additional data of size datasize. - * If the ccwcache cannot fulfill the request it tries the emergeny requests - * before giving up finally + * If the ccwcache cannot fulfill the request it tries the lowmem requests + * before giving up finally. * FIXME: initialization of ccw_req_t should be done by function of ccwcache */ ccw_req_t * -dasd_alloc_request (char *magic, int cplength, int datasize, dasd_device_t* device) +dasd_alloc_request (char *magic, int cplength, int datasize, + dasd_device_t *device) { - ccw_req_t *rv = NULL; + ccw_req_t *cqr; + unsigned long size_needed; + unsigned long data_offset, ccw_offset; + dasd_lowmem_t *lowmem; + + if ((cqr = ccw_alloc_request (magic, cplength, datasize)) != NULL) { + return cqr; + } + + /* Sanity checks */ + if (magic == NULL || datasize > PAGE_SIZE || + cplength == 0 || (cplength * sizeof(ccw1_t)) > PAGE_SIZE) + BUG(); - if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) { - return rv; + /* use lowmem page only for ERP or */ + /* if there are less than 2 requests on queue */ + if (device->queue.head != NULL && + device->queue.head->next != NULL && + device->queue.head->status != CQR_STATUS_ERROR) { + DEV_MESSAGE (KERN_DEBUG, device, + "Refusing emergency mem for request " + "(enough requests in queue) " + "head %p, status %x next %p", + device->queue.head, + device->queue.head->status, + device->queue.head->next); + return NULL; + } + + /* We try to keep things together in memory */ + size_needed = (sizeof (ccw_req_t) + 7) & (~7L); + data_offset = ccw_offset = 0; + if (size_needed + datasize <= PAGE_SIZE) { + /* Keep data with the request */ + data_offset = size_needed; + size_needed += (datasize + 7) & (~7L); + } + if (size_needed + cplength*sizeof(ccw1_t) <= PAGE_SIZE) { + /* Keep CCWs with request */ + ccw_offset = size_needed; + size_needed += (cplength*sizeof(ccw1_t)) & (~7L); } - if ((((sizeof (ccw_req_t) + 7) & -8) + - cplength * sizeof (ccw1_t) + datasize) > PAGE_SIZE) { - BUG (); + + /* take page from lowmem_pool for request */ + list_for_each_entry (lowmem, &device->lowmem_pool, list) { + DEV_MESSAGE (KERN_DEBUG, device, + "Take lowmem page %p for request", + lowmem); + list_del (&lowmem->list); + cqr = (ccw_req_t *) lowmem; + memset (cqr, 0, PAGE_SIZE); + cqr->flags |= CQR_FLAGS_LM_CQR; + break; + } + if (cqr == NULL) + return NULL; + + /* take page from lowmem_pool for the extra data */ + if (data_offset == 0) { + + list_for_each_entry (lowmem, &device->lowmem_pool, list) { + DEV_MESSAGE (KERN_DEBUG, device, + "Take lowmem page %p for extra data", + lowmem); + list_del (&lowmem->list); + cqr->data = (void *) lowmem; + memset (cqr->data, 0, PAGE_SIZE); + break; + } + if (cqr->data == NULL) { + printk(KERN_DEBUG PRINTK_HEADER + "Couldn't allocate data area\n"); + + lowmem = (dasd_lowmem_t *) cqr; + list_add (&lowmem->list, &device->lowmem_pool); + return NULL; } - if (device->lowmem_cqr==NULL) { - DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request, - "(%04x) Low memory! Using emergency request %p.", - device->devinfo.devno, - device->lowmem_ccws); - - device->lowmem_cqr=device->lowmem_ccws; - rv = device->lowmem_ccws; - memset (rv, 0, PAGE_SIZE); - strncpy ((char *) (&rv->magic), magic, 4); - ASCEBC ((char *) (&rv->magic), 4); - rv->cplength = cplength; - rv->datasize = datasize; - rv->data = (void *) ((long) rv + PAGE_SIZE - datasize); - rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t)); - } else { - DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request, - "(%04x) Refusing emergency mem for request " - "NULL, already in use at %p.", - device->devinfo.devno, - device->lowmem_ccws); - } - return rv; + } else { + /* Extra data already allocated with the request */ + cqr->data = (void *) ((addr_t) cqr + data_offset); + } + + /* take page from lowmem_pool for the channel program */ + if (ccw_offset == 0) { + + list_for_each_entry (lowmem, &device->lowmem_pool, list) { + DEV_MESSAGE (KERN_DEBUG, device, + "Take lowmem page %p for channel program", + lowmem); + list_del (&lowmem->list); + cqr->cpaddr = (ccw1_t *) lowmem; + memset (cqr->cpaddr, 0, PAGE_SIZE); + break; + } + + if (cqr->cpaddr == NULL) { + printk (KERN_DEBUG PRINTK_HEADER + "Couldn't allocate channel program area\n"); + if (data_offset == 0) { + lowmem = (dasd_lowmem_t *) cqr->data; + list_add (&lowmem->list, &device->lowmem_pool); + } + lowmem = (dasd_lowmem_t *) cqr; + list_add (&lowmem->list, &device->lowmem_pool); + return NULL; + } + } else { + /* Channel program already allocated with the request */ + cqr->cpaddr = (ccw1_t *) ((addr_t) cqr + ccw_offset); + } + + /* use the remaining memory of the cqr page for IDALs */ + cqr->lowmem_idal_ptr = (void *) ((addr_t) cqr + size_needed); + + strncpy ((char *)(&cqr->magic), magic, 4); + + ASCEBC((char *)(&cqr->magic), 4); + cqr->cplength = cplength; + cqr->datasize = datasize; + + return cqr; } /* @@ -1143,87 +1337,168 @@ * returns a ccw_req_t to the appropriate cache or emergeny request line */ void -dasd_free_request (ccw_req_t * request, dasd_device_t* device) +dasd_free_request (ccw_req_t *cqr, dasd_device_t* device) { + unsigned long size_needed; + dasd_lowmem_t *lowmem; + #ifdef CONFIG_ARCH_S390X ccw1_t* ccw; - /* clear any idals used for chain */ - ccw=request->cpaddr-1; + /* clear any idals used for chain (might be in lowmen cqr page, */ + /* in seperate lowmen page or kmalloced */ + ccw=cqr->cpaddr-1; do { ccw++; - if ((ccw->cda < (unsigned long) device->lowmem_idals ) || - (ccw->cda >= (unsigned long) device->lowmem_idals+PAGE_SIZE) ) - clear_normalized_cda (ccw); - else { - if (device->lowmem_idal_ptr != device->lowmem_idals) - DASD_MESSAGE (KERN_WARNING, device, - "Freeing emergency idals from request at %p.", - request); - device->lowmem_idal_ptr = device->lowmem_idals; - device->lowmem_cqr=NULL; + if ((cqr->flags & CQR_FLAGS_LM_CQR) && + (ccw->cda >= (unsigned long) cqr) && + (ccw->cda < (unsigned long) cqr + PAGE_SIZE)) { + /* IDAL is on the car lowmem page */ + continue; + } + + if ((cqr->flags & CQR_FLAGS_LM_IDAL) && + (ccw->cda >= (unsigned long) cqr->lowmem_idal) && + (ccw->cda < (unsigned long) cqr->lowmem_idal + PAGE_SIZE)) { + /* IDAL is on seperate lowmem page */ + continue; } + + /* IDAL was build by set_normalized_cda */ + clear_normalized_cda (ccw); + } while ((ccw->flags & CCW_FLAG_CC) || (ccw->flags & CCW_FLAG_DC) ); #endif - if (request != device->lowmem_ccws) { - /* compare to lowmem_ccws to protect usage of lowmem_cqr for IDAL only ! */ - ccw_free_request (request); - } else { - DASD_MESSAGE (KERN_WARNING, device, - "Freeing emergency request at %p", - request); - device->lowmem_cqr=NULL; - } + /* give idal lowmem page back to lowmem_pool */ + if (cqr->flags & CQR_FLAGS_LM_IDAL) { + DEV_MESSAGE (KERN_DEBUG, device, + "Freeing lowmem idal page %p from request %p.", + cqr->lowmem_idal, cqr); + + lowmem = (dasd_lowmem_t *) cqr->lowmem_idal; + list_add (&lowmem->list, &device->lowmem_pool); + cqr->flags &= ~CQR_FLAGS_LM_IDAL; + } + + /* give cqr lowmem pages back to lowmem_pool */ + if (cqr->flags & CQR_FLAGS_LM_CQR) { + + /* make the same decisions as in dasd_alloc_request */ + size_needed = (sizeof (ccw_req_t) + 7) & (~7L); + if (size_needed + cqr->datasize <= PAGE_SIZE) { + /* We kept the data with the request */ + size_needed += (cqr->datasize + 7) & (~7L); + } else { + DEV_MESSAGE (KERN_DEBUG, device, + "Freeing lowmem data page %p", + cqr->data); + lowmem = (dasd_lowmem_t *) cqr->data; + list_add (&lowmem->list, &device->lowmem_pool); + } + + if (size_needed + cqr->cplength * sizeof(ccw1_t) > PAGE_SIZE) { + /* We didn't keep the CCWs with request */ + DEV_MESSAGE (KERN_DEBUG, device, + "Freeing lowmem channel program page %p", + cqr->cpaddr); + lowmem = (dasd_lowmem_t *) cqr->cpaddr; + list_add (&lowmem->list, &device->lowmem_pool); + } + DEV_MESSAGE (KERN_DEBUG, device, + "Freeing lowmem request page %p", + cqr); + lowmem = (dasd_lowmem_t *) cqr; + list_add (&lowmem->list, &device->lowmem_pool); + } else { + ccw_free_request (cqr); + } } +/* + * function dasd_set_normalized_cda + * calls set_normalized_cda to build IDALs. + * If this did not work because of low memory, we try to use memory from the + * lowmem pool. + */ int -dasd_set_normalized_cda (ccw1_t * cp, unsigned long address, - ccw_req_t* request, dasd_device_t* device ) +dasd_set_normalized_cda (ccw1_t *cp, unsigned long address, + ccw_req_t *cqr, dasd_device_t *device) { #ifdef CONFIG_ARCH_S390X + int rc; int nridaws; + dasd_lowmem_t *lowmem; int count = cp->count; - if (set_normalized_cda (cp, address)!=-ENOMEM) { - return 0; - } - - if ((device->lowmem_cqr!=NULL) && (device->lowmem_cqr!=request)) { - DASD_MESSAGE (KERN_WARNING, device, - "Refusing emergency idals for request %p, memory" - " is already in use for request %p", - request, - device->lowmem_cqr); - return -ENOMEM; - } - device->lowmem_cqr=request; - if (device->lowmem_idal_ptr == device->lowmem_idals) { - DASD_MESSAGE (KERN_WARNING,device, - "Low memory! Using emergency IDALs for request %p.\n", - request); + /* use lowmem idal page if already assinged */ + if (!(cqr->flags & CQR_FLAGS_LM_IDAL)) { + rc = set_normalized_cda (cp, (void *)address); + if (rc !=-ENOMEM) { + return rc; + } } + + /* get number of idal words needed */ nridaws = ((address & (IDA_BLOCK_SIZE-1)) + count + (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG; - if ( device->lowmem_idal_ptr>=device->lowmem_idals + PAGE_SIZE ) { - /* Ouch! No Idals left for emergency request */ - BUG(); - } + + /* check if we need an additional IDALs page */ + if (!(cqr->flags & CQR_FLAGS_LM_IDAL)) { + /* we got no lowmem cqr page OR */ + /* there is no space left for IDALs */ + if ((!(cqr->flags & CQR_FLAGS_LM_CQR)) || + ((cqr->lowmem_idal_ptr + nridaws * sizeof(unsigned long)) > + ((void *) cqr + PAGE_SIZE))) { + + /* use lowmem page only for ERP or */ + /* if there are less than 2 requests on queue */ + if (device->queue.head != NULL && + device->queue.head->next != NULL && + device->queue.head->status != CQR_STATUS_ERROR) { + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Refusing emergency idals for " + "request ((enough requests in " + "queue)"); + return -ENOMEM; + } + + list_for_each_entry (lowmem, &device->lowmem_pool, + list) { + DEV_MESSAGE (KERN_DEBUG, device, + "take lowmem page %p for IDALs of " + "request %p.", + lowmem, cqr); + list_del (&lowmem->list); + cqr->lowmem_idal = (void *)lowmem; + cqr->lowmem_idal_ptr = (void *) lowmem; + memset (cqr->lowmem_idal, 0, PAGE_SIZE); + cqr->flags |= CQR_FLAGS_LM_IDAL; + break; + } + } + + } + + /* now we (should) have an valid lowmem_idal_ptr and enough space for */ + /* the IDALs - fill the idals table */ cp->flags |= CCW_FLAG_IDA; - cp->cda = (__u32)(unsigned long)device->lowmem_idal_ptr; + cp->cda = (__u32)(unsigned long)cqr->lowmem_idal_ptr; do { - *((long*)device->lowmem_idal_ptr) = address; - address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE); + *((long*)cqr->lowmem_idal_ptr) = address; + address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE); + cqr->lowmem_idal_ptr += sizeof(unsigned long); nridaws --; - device->lowmem_idal_ptr += sizeof(unsigned long); } while ( nridaws > 0 ); #else - cp -> cda = address; + cp->cda = address; #endif return 0; } -/* SECTION: (de)queueing of requests to channel program queues */ +/******************************************************************************** + * SECTION: (de)queueing of requests to channel program queues + ********************************************************************************/ /* * function dasd_chanq_enq @@ -1245,22 +1520,26 @@ #ifdef DASD_PROFILE - /* save profile information for non erp cqr */ - if (cqr->refers == NULL) { - unsigned int counter = 0; - ccw_req_t *ptr; - dasd_device_t *device = cqr->device; - - /* count the length of the chanq for statistics */ - for (ptr = q->head; - ptr->next != NULL && counter <=31; - ptr = ptr->next) { - counter++; - } - - dasd_global_profile.dasd_io_nr_req[counter]++; - device->profile.dasd_io_nr_req[counter]++; - } + if (dasd_profile_level == DASD_PROFILE_ON) { + + /* save profile information for non erp cqr */ + if (cqr->refers == NULL) { + unsigned int counter = 0; + ccw_req_t *ptr; + dasd_device_t *device = cqr->device; + + /* count the length of the chanq for statistics */ + for (ptr = q->head; + ptr->next != NULL && counter <=31; + ptr = ptr->next) { + counter++; + } + + dasd_global_profile.dasd_io_nr_req[counter]++; + device->profile.dasd_io_nr_req[counter]++; + } + + } /* end if DASD_PROFILE_ON */ #endif } @@ -1276,7 +1555,10 @@ q->head = cqr; if (q->tail == NULL) q->tail = cqr; - check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED); + + check_then_set (&cqr->status, + CQR_STATUS_FILLED, + CQR_STATUS_QUEUED); } /* @@ -1302,7 +1584,7 @@ while (prev && prev->next != cqr) prev = prev->next; if (prev == NULL) - return; + return; /* request not in chanq */ prev->next = cqr->next; if (prev->next == NULL) q->tail = prev; @@ -1310,7 +1592,87 @@ cqr->next = NULL; } -/* SECTION: Managing the device queues etc. */ +/******************************************************************************** + * SECTION: Managing the device queues etc. + ********************************************************************************/ + +/* + * DASD_RESREL_TIMEOUT + * + * A timer is used to suspend the current reserve/release request + * if it doesn't return within a certain time. + */ +void +dasd_resrel_timeout (unsigned long cqr_ptr) +{ + dasd_device_t *device = ((ccw_req_t *) cqr_ptr)->device; + ccw_req_t *cqr; + unsigned long flags; + + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + cqr = device->queue.head; + + switch (cqr->status) { + case CQR_STATUS_FILLED: + case CQR_STATUS_QUEUED: + /* request was not started - just set to failed */ + cqr->status = CQR_STATUS_FAILED; + break; + + case CQR_STATUS_IN_IO: + case CQR_STATUS_ERROR: + if (device->discipline->term_IO (cqr) != 0); + cqr->status = CQR_STATUS_FAILED; + break; + + default: + ; /* DONE and FAILED are ok */ + } + + dasd_schedule_bh (device); + + s390irq_spin_unlock_irqrestore (device->devinfo.irq, + flags); + +} /* end dasd_resrel_timeout */ + +/* + * Call unconditional reserve to break the reserve of an other system. + * Timeout the request if it doesn't succseed within a certain time. + */ +static int +dasd_steal_lock (dasd_device_t *device) +{ + ccw_req_t *cqr; + int rc = 0; + + if (!device->discipline->steal_lock) + rc = -EINVAL; + + cqr = device->discipline->steal_lock (device); + + if (cqr) { + struct timer_list res_timer; + + init_timer(&res_timer); + res_timer.function = dasd_resrel_timeout; + res_timer.data = (unsigned long) cqr; + res_timer.expires = jiffies + 4 * HZ; + add_timer(&res_timer); + + rc = dasd_sleep_on_immediate (cqr); + + del_timer_sync(&res_timer); + dasd_free_request (cqr, + device); + } else { + rc = -ENOMEM; + } + + return rc; + +} /* end dasd_steal_lock */ /* * DASD_TERM_IO @@ -1331,55 +1693,57 @@ BUG (); } irq = device->devinfo.irq; - if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) { - DASD_MESSAGE (KERN_WARNING, device, - " ccw_req_t 0x%08x magic doesn't match" - " discipline 0x%08x\n", - cqr->magic, - *(unsigned int *) device->discipline->name); + if (strncmp ((char *) &cqr->magic, + device->discipline->ebcname, 4)) { + + DEV_MESSAGE (KERN_WARNING, device, + " ccw_req_t 0x%08x magic doesn't match" + " discipline 0x%08x", + cqr->magic, + *(unsigned int *) device->discipline->name); + return -EINVAL; } while ((retries < 5 ) && (cqr->status == CQR_STATUS_IN_IO) ) { - if ( retries < 2 ) - rc = halt_IO(irq, (long)cqr, - cqr->options | DOIO_WAIT_FOR_INTERRUPT); - else - rc = clear_IO(irq, (long)cqr, - cqr->options | DOIO_WAIT_FOR_INTERRUPT); - + rc = clear_IO (irq, + (long)cqr, + cqr->options); + switch (rc) { case 0: /* termination successful */ check_then_set (&cqr->status, CQR_STATUS_IN_IO, CQR_STATUS_FAILED); - asm volatile ("STCK %0":"=m" (cqr->stopclk)); + cqr->stopclk = get_clock (); + break; case -ENODEV: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "device gone, retry\n"); + DBF_DEV_EVENT (DBF_ERR, device, "%s", + "device gone, retry"); break; case -EIO: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "I/O error, retry\n"); + DBF_DEV_EVENT (DBF_ERR, device, "%s", + "I/O error, retry"); break; case -EBUSY: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "device busy, retry later\n"); + DBF_DEV_EVENT (DBF_ERR, device, "%s", + "device busy, retry later"); break; default: - DASD_MESSAGE (KERN_ERR, device, - "line %d unknown RC=%d, please report" - " to linux390@de.ibm.com\n", - __LINE__, - rc); + DEV_MESSAGE (KERN_ERR, device, + "line %d unknown RC=%d, please " + "report to linux390@de.ibm.com", + __LINE__, + rc); BUG (); break; } + dasd_schedule_bh (device); retries ++; } return rc; @@ -1401,27 +1765,30 @@ BUG (); } irq = device->devinfo.irq; - if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) { - DASD_MESSAGE (KERN_WARNING, device, - " ccw_req_t 0x%08x magic doesn't match" - " discipline 0x%08x\n", - cqr->magic, - *(unsigned int *) device->discipline->name); + if (strncmp ((char *) &cqr->magic, + device->discipline->ebcname, 4)) { + + DEV_MESSAGE (KERN_ERR, device, + " ccw_req_t 0x%08x magic doesn't match" + " discipline 0x%08x", + cqr->magic, + *(unsigned int *) device->discipline->name); + return -EINVAL; } - asm volatile ("STCK %0":"=m" (now)); - cqr->startclk = now; + now = get_clock (); - rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options); + cqr->startclk = now; + if (device->accessible) + rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options); + else + rc = -EBUSY; switch (rc) { case 0: if (cqr->options & DOIO_WAIT_FOR_INTERRUPT) { /* request already finished (synchronous IO) */ - DASD_MESSAGE (KERN_ERR, device, "%s", - " do_IO finished request... " - "DOIO_WAIT_FOR_INTERRUPT was set"); check_then_set (&cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_DONE); @@ -1436,12 +1803,22 @@ } break; case -EBUSY: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "device busy, retry later\n"); + DBF_DEV_EVENT (DBF_ERR, device, "%s", + "device busy, retry later"); + + if (!timer_pending(&device->timer)) { + init_timer (&device->timer); + device->timer.function = dasd_schedule_bh_timed; + device->timer.data = (unsigned long) device; + device->timer.expires = jiffies + (HZ >> 4); + add_timer (&device->timer); + } else { + mod_timer(&device->timer, jiffies + (HZ >> 4)); + } break; case -ETIMEDOUT: - DASD_MESSAGE (KERN_WARNING, device, "%s", - "request timeout - terminated\n"); + DBF_DEV_EVENT (DBF_ERR, device, "%s", + "request timeout - terminated"); case -ENODEV: case -EIO: check_then_set (&cqr->status, @@ -1452,9 +1829,11 @@ dasd_schedule_bh (device); break; default: - DASD_MESSAGE (KERN_ERR, device, - "line %d unknown RC=%d, please report" - " to linux390@de.ibm.com\n", __LINE__, rc); + DEV_MESSAGE (KERN_ERR, device, + "line %d unknown RC=%d, please report" + " to linux390@de.ibm.com", + __LINE__, + rc); BUG (); break; } @@ -1465,43 +1844,83 @@ /* * function dasd_sleep_on_req * attempts to start the IO and waits for completion - * FIXME: replace handmade sleeping by wait_event */ int -dasd_sleep_on_req (ccw_req_t * req) +dasd_sleep_on_req (ccw_req_t * cqr) { unsigned long flags; - int cs; - int rc = 0; - dasd_device_t *device = (dasd_device_t *) req->device; + dasd_device_t *device = (dasd_device_t *) cqr->device; - if ( signal_pending(current) ) { + if (signal_pending(current)) { return -ERESTARTSYS; } - s390irq_spin_lock_irqsave (device->devinfo.irq, flags); - dasd_chanq_enq (&device->queue, req); + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + + dasd_chanq_enq (&device->queue, + cqr); + /* let the bh start the request to keep them in order */ dasd_schedule_bh (device); - do { - s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags); - wait_event ( device->wait_q, - (((cs = req->status) == CQR_STATUS_DONE) || - (cs == CQR_STATUS_FAILED) || - signal_pending(current))); - s390irq_spin_lock_irqsave (device->devinfo.irq, flags); - if ( signal_pending(current) ) { - rc = -ERESTARTSYS; - if (req->status == CQR_STATUS_IN_IO ) - device->discipline->term_IO(req); - break; - } else if ( req->status == CQR_STATUS_FAILED) { - rc = -EIO; - break; - } - } while (cs != CQR_STATUS_DONE && cs != CQR_STATUS_FAILED); - s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags); - return rc; -} /* end dasd_sleep_on_req */ + + s390irq_spin_unlock_irqrestore (device->devinfo.irq, + flags); + + wait_event (device->wait_q, + cqr->flags & CQR_FLAGS_FINALIZED); + + if (cqr->status == CQR_STATUS_FAILED) { + return -EIO; + } + + return 0; + +} /* end dasd_sleep_on_req */ + +/* + * function dasd_sleep_on_immediate + * same as dasd_sleep_on_req, but attempts to start the IO immediately + * (killing the actual running IO). + */ +static int +dasd_sleep_on_immediate (ccw_req_t *cqr) +{ + unsigned long flags; + dasd_device_t *device = (dasd_device_t *) cqr->device; + + if (signal_pending(current)) + return -ERESTARTSYS; + + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + + /* terminate currently running IO */ + if (device->queue.head->status == CQR_STATUS_IN_IO) { + + device->discipline->term_IO (device->queue.head); + + device->queue.head->status = CQR_STATUS_QUEUED; + } + + dasd_chanq_enq_head (&device->queue, + cqr); + + /* let the bh start the request to keep them in order */ + dasd_schedule_bh (device); + + s390irq_spin_unlock_irqrestore (device->devinfo.irq, + flags); + + wait_event (device->wait_q, + cqr->flags & CQR_FLAGS_FINALIZED); + + if (cqr->status == CQR_STATUS_FAILED) { + return -EIO; + } + + return 0; + +} /* end dasd_sleep_on_immediate */ /* * function dasd_end_request @@ -1547,12 +1966,17 @@ unsigned long long now; int rc = 0; - asm volatile ("STCK %0":"=m" (now)); - if (cqr->expires && cqr->expires + cqr->startclk < now) { - DASD_MESSAGE (KERN_ERR, ((dasd_device_t *) cqr->device), - "IO timeout 0x%08lx%08lx usecs in req %p\n", - (long) (cqr->expires >> 44), - (long) (cqr->expires >> 12), cqr); + now = get_clock (); + + if (cqr->expires && + cqr->expires + cqr->startclk < now) { + + DBF_DEV_EVENT (DBF_WARNING, ((dasd_device_t *) cqr->device), + "IO timeout 0x%08lx%08lx usecs in req %p", + (long) (cqr->expires >> 44), + (long) (cqr->expires >> 12), + cqr); + cqr->expires <<= 1; rc = -EIO; } @@ -1569,22 +1993,36 @@ { dasd_device_t *device = cqr->device; - asm volatile ("STCK %0":"=m" (cqr->endclk)); + cqr->endclk = get_clock (); + if (cqr->req) { + #ifdef DASD_PROFILE - dasd_profile_add (cqr); + if (dasd_profile_level == DASD_PROFILE_ON) { + dasd_profile_add (cqr); + } #endif + dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE)); /* free request if nobody is waiting on it */ dasd_free_request (cqr, cqr->device); } else { - if ( cqr == device->init_cqr && /* bring late devices online */ - device->level <= DASD_STATE_ONLINE ) { - device->timer.function = dasd_enable_single_device; - device->timer.data = (unsigned long) device; - device->timer.expires = jiffies; - add_timer(&device->timer); + if (cqr == device->init_cqr && /* bring late devices online */ + device->level <= DASD_STATE_ONLINE ) { + if (!timer_pending(&device->late_timer)) { + init_timer(&device->late_timer); + device->late_timer.function = dasd_enable_single_device; + device->late_timer.data = (unsigned long) device; + device->late_timer.expires = jiffies; + add_timer(&device->late_timer); + } else { + mod_timer(&device->late_timer, jiffies); + } + } else { + /* notify sleep_on_xxx about finished cqr */ + cqr->flags |= CQR_FLAGS_FINALIZED; } + /* notify sleeping task about finished postprocessing */ wake_up (&device->wait_q); @@ -1606,12 +2044,10 @@ dasd_chanq_t *qp = &device->queue; int irq = device->devinfo.irq; ccw_req_t *final_requests = NULL; - static int chanq_min_size = DASD_MIN_SIZE_FOR_QUEUE; int chanq_max_size = DASD_CHANQ_MAX_SIZE; ccw_req_t *cqr = NULL, *temp; dasd_erp_postaction_fn_t erp_postaction; - s390irq_spin_lock_irqsave (irq, flags); /* First we dechain the requests, processed with completed status */ @@ -1628,16 +2064,10 @@ qp->head->retries--; - if (qp->head->dstat->flag & DEVSTAT_HALT_FUNCTION) { - - check_then_set (&qp->head->status, - CQR_STATUS_ERROR, - CQR_STATUS_FAILED); - - asm volatile ("STCK %0":"=m" (qp->head->stopclk)); - - } else if ((device->discipline->erp_action == NULL ) || - ((erp_action = device->discipline->erp_action (qp->head)) == NULL) ) { + if ((qp->head->dstat == NULL ) || + ((qp->head->dstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) == 0 ) || + (device->discipline->erp_action == NULL ) || + ((erp_action = device->discipline->erp_action (qp->head)) == NULL) ) { erp_cqr = dasd_default_erp_action (qp->head); @@ -1651,12 +2081,12 @@ if (qp->head->status == CQR_STATUS_DONE) { - DASD_MESSAGE (KERN_DEBUG, device, "%s", - "ERP successful"); + DBF_DEV_EVENT (DBF_NOTICE, device, "%s", + "ERP successful"); } else { - DASD_MESSAGE (KERN_ERR, device, "%s", - "ERP unsuccessful"); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "ERP unsuccessful"); } if ((device->discipline->erp_postaction == NULL )|| @@ -1685,65 +2115,60 @@ } /* end while over completed requests */ if (cqr) - cqr->next = NULL; + cqr->next = NULL; /* terminate final_requests queue */ + /* Now clean the requests with final status */ while (final_requests) { temp = final_requests; final_requests = temp->next; dasd_finalize_request (temp); } + /* Now we try to fetch requests from the request queue */ - for (temp = cqr; temp != NULL; temp = temp->next) + for (temp = qp->head; temp != NULL; temp = temp->next) { if (temp->status == CQR_STATUS_QUEUED) chanq_max_size--; + } + while ((atomic_read(&device->plugged) == 0) && + (queue) && (!queue->plugged) && (!list_empty (&queue->queue_head)) && - (req = dasd_next_request (queue)) != NULL) { + (req = dasd_next_request (queue)) && + (qp->head == NULL || chanq_max_size > 0)) { /* queue empty or certain critera fulfilled -> transfer */ - if (qp->head == NULL || - chanq_max_size > 0 || (req->nr_sectors >= chanq_min_size)) { - ccw_req_t *cqr = NULL; - if (is_read_only(device->kdev) && req->cmd == WRITE) { - - DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler, - "(%04x) Rejecting write request %p\n", - device->devinfo.devno, - req); + cqr = NULL; + if (is_read_only(device->kdev) && req->cmd == WRITE) { - dasd_end_request (req, 0); - dasd_dequeue_request (queue,req); - } else { - /* relocate request according to partition table */ - req->sector += - device->major_info->gendisk. - part[MINOR (req->rq_dev)].start_sect; - cqr = device->discipline->build_cp_from_req (device, req); - if (cqr == NULL) { - - DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler, - "(%04x) CCW creation failed " - "on request %p\n", - device->devinfo.devno, - req); - /* revert relocation of request */ - req->sector -= - device->major_info->gendisk. - part[MINOR (req->rq_dev)].start_sect; - break; /* terminate request queue loop */ - - } -#ifdef CONFIG_DYNAMIC_QUEUE_MIN_SIZE - chanq_min_size = - (chanq_min_size + req->nr_sectors) >> 1; -#endif /* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */ - dasd_dequeue_request (queue, req); - dasd_chanq_enq (qp, cqr); + DBF_EVENT (DBF_ERR, + "(%04x) Rejecting write request %p", + device->devinfo.devno, + req); + dasd_dequeue_request (queue,req); + dasd_end_request (req, 0); + continue; + } + cqr = device->discipline->build_cp_from_req (device, req); + + if (cqr == NULL || IS_ERR(cqr)) { + if (cqr == ERR_PTR(-ENOMEM)) { + break; } - } else { /* queue not empty OR criteria not met */ - break; /* terminate request queue loop */ - } + + MESSAGE (KERN_EMERG, + "(%04x) CCW creation failed " + "on request %p", + device->devinfo.devno, req); + dasd_dequeue_request (queue,req); + dasd_end_request (req, 0); + continue; + } + dasd_dequeue_request (queue, req); + dasd_chanq_enq (qp, cqr); + chanq_max_size--; + } + /* we process the requests with non-final status */ if (qp->head) { switch (qp->head->status) { @@ -1764,6 +2189,10 @@ /* just wait */ break; default: + MESSAGE (KERN_EMERG, + "invalid cqr (%p) detected with status %02x ", + qp->head, + qp->head->status); BUG (); } } @@ -1786,6 +2215,18 @@ } /* + * function dasd_schedule_bh_timed + * retriggers the dasd_schedule_bh function (called by timer queue) + */ +void +dasd_schedule_bh_timed (unsigned long device_ptr) +{ + dasd_device_t *device = (dasd_device_t *) device_ptr; + + dasd_schedule_bh (device); +} + +/* * function dasd_schedule_bh * schedules the request_fn to run with next run_bh cycle */ @@ -1842,30 +2283,43 @@ if (device_addr == NULL) { - printk (KERN_DEBUG PRINTK_HEADER - "unable to find device for state change pending " - "interrupt: devno%04x\n", - stat->devno); + MESSAGE (KERN_DEBUG, + "unable to find device for state change pending " + "interrupt: devno%04x", + stat->devno); return; } /* re-activate first request in queue */ cqr = (*device_addr)->queue.head; + + if (cqr == NULL) { + MESSAGE (KERN_DEBUG, + "got state change pending interrupt on" + "idle device: %04x", + stat->devno); + return; + } if (cqr->status == CQR_STATUS_PENDING) { - DASD_MESSAGE (KERN_DEBUG, (*device_addr), "%s", - "device request queue restarted by " - "state change pending interrupt\n"); + DEV_MESSAGE (KERN_DEBUG, (*device_addr), "%s", + "device request queue restarted by " + "state change pending interrupt"); - del_timer (&(*device_addr)->timer); + del_timer_sync (&(*device_addr)->blocking_timer); check_then_set (&cqr->status, CQR_STATUS_PENDING, CQR_STATUS_QUEUED); - dasd_schedule_bh (*device_addr); - } + if (cqr->status == CQR_STATUS_IN_IO) { + cqr->status = CQR_STATUS_QUEUED; + DEV_MESSAGE (KERN_WARNING, (*device_addr), "%s", + "redriving state change pending condition while in IO"); + } + + dasd_schedule_bh (*device_addr); } /* end dasd_handle_state_change_pending */ @@ -1879,50 +2333,50 @@ int ip; ccw_req_t *cqr; dasd_device_t *device; - unsigned long long now; - dasd_era_t era = dasd_era_none; /* default is everything is okay */ + dasd_era_t era; devstat_t *stat = (devstat_t *)ds; if (stat == NULL) { BUG(); } - DASD_DRIVER_DEBUG_EVENT (6, dasd_int_handler, - "Interrupt: IRQ %02x, stat %02x, devno %04x", - irq, - stat->dstat, - stat->devno); - asm volatile ("STCK %0":"=m" (now)); + + DBF_EVENT (DBF_DEBUG, + "Int: IRQ %02x, CS/DS %04x, flag %08x, devno %04x, ip %08x", + irq, + ((stat->cstat<<8)|stat->dstat), + stat->flag, + stat->devno, + stat->intparm); /* first of all check for state change pending interrupt */ if ((stat->dstat & DEV_STAT_ATTENTION ) && (stat->dstat & DEV_STAT_DEV_END ) && (stat->dstat & DEV_STAT_UNIT_EXCEP) ) { - DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler, - "State change Interrupt: %04x", - stat->devno); + + DBF_EVENT (DBF_NOTICE, + "State change Interrupt: %04x", + stat->devno); + dasd_handle_state_change_pending (stat); return; } ip = stat->intparm; if (!ip) { /* no intparm: unsolicited interrupt */ - DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler, - "Unsolicited Interrupt: %04x", - stat->devno); - printk (KERN_DEBUG PRINTK_HEADER - "unsolicited interrupt: irq 0x%x devno %04x\n", - irq, - stat->devno); + + MESSAGE (KERN_DEBUG, + "unsolicited interrupt: irq 0x%x devno %04x", + irq, + stat->devno); return; } - if (ip & 0x80000001) { - DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler, - "spurious Interrupt: %04x", - stat->devno); - printk (KERN_DEBUG PRINTK_HEADER - "spurious interrupt: irq 0x%x devno %04x, parm %08x\n", - irq, - stat->devno,ip); + + if (ip & 0x80000001) { /* check for invalid 'cqr' address */ + + MESSAGE (KERN_DEBUG, + "spurious interrupt: irq 0x%x devno %04x, parm %08x", + irq, + stat->devno,ip); return; } @@ -1930,19 +2384,16 @@ /* check status - the request might have been killed because of dyn dettach */ if (cqr->status != CQR_STATUS_IN_IO) { - DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler, - "invalid status %02x on device %04x", - cqr->status, - stat->devno); - - printk (KERN_DEBUG PRINTK_HEADER - "invalid status: irq 0x%x devno %04x, status %02x\n", - irq, - stat->devno, - cqr->status); + + MESSAGE (KERN_DEBUG, + "invalid status: irq 0x%x devno %04x, status %02x", + irq, + stat->devno, + cqr->status); return; } + /* some consistency checks */ device = (dasd_device_t *) cqr->device; if (device == NULL || device != ds-offsetof(dasd_device_t,dev_status)) { @@ -1955,73 +2406,98 @@ BUG(); } - /* first of all lets try to find out the appropriate era_action */ - DASD_DEVICE_DEBUG_EVENT (4, device," Int: CS/DS 0x%04x", - ((stat->cstat<<8)|stat->dstat)); - /* first of all lets try to find out the appropriate era_action */ - if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL || - stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) { - /* anything abnormal ? */ - if (device->discipline->examine_error == NULL || - stat->flag & DEVSTAT_HALT_FUNCTION) { - era = dasd_era_fatal; - } else { - era = device->discipline->examine_error (cqr, stat); - } - DASD_DRIVER_DEBUG_EVENT (1, dasd_int_handler," era_code %d", - era); - } - if ( era == dasd_era_none ) { - check_then_set(&cqr->status, - CQR_STATUS_IN_IO, - CQR_STATUS_DONE); - - cqr->stopclk=now; - /* start the next queued request if possible -> fast_io */ - if (cqr->next && - cqr->next->status == CQR_STATUS_QUEUED) { - if (device->discipline->start_IO (cqr->next) != 0) { - printk (KERN_WARNING PRINTK_HEADER - "Interrupt fastpath failed!\n"); - } - } - } else { /* error */ + if (stat->flag & DEVSTAT_HALT_FUNCTION) { + era = dasd_era_fatal; + + } else if (stat->flag & DEVSTAT_FINAL_STATUS && + stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + stat->cstat == 0) { + /* received 'ok' for running IO */ + era = dasd_era_none; + + } else if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL) { + /* got sense data */ if (cqr->dstat == NULL) cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC); if (cqr->dstat) { memcpy (cqr->dstat, stat, sizeof (devstat_t)); } else { - PRINT_ERR ("no memory for dstat...ignoring\n"); + MESSAGE (KERN_DEBUG, "%s", + "no memory for dstat...ignoring"); } - #ifdef ERP_DEBUG - /* dump sense data */ - if (device->discipline && + if (device->discipline && device->discipline->dump_sense ) { - + device->discipline->dump_sense (device, cqr); } #endif + if (device->discipline->examine_error == NULL) { + era = dasd_era_recover; + } else { + era = device->discipline->examine_error (cqr, stat); + } - switch (era) { - case dasd_era_fatal: - check_then_set (&cqr->status, - CQR_STATUS_IN_IO, - CQR_STATUS_FAILED); + } else if (stat->flag & DEVSTAT_NOT_OPER) { + /* path became offline or similar */ + /* => retry to see if there are any other pathes available */ + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "Device or a path became not operational while in IO"); + era = dasd_era_recover; + + } else if (stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) || + stat->cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN) ) { + /* received device state apart from (channel end & device end) */ + /* OR any kind of channel check (e.g. IFCC, DATA_CHECK or ..... */ + /* we got no sense data, therefore we just retry */ + DEV_MESSAGE (KERN_DEBUG, device, + "Status without sense (IFCC,...) CS/DS %04x flag %08x", + ((stat->cstat<<8)|stat->dstat), + stat->flag); + era = dasd_era_recover; - cqr->stopclk = now; - break; - case dasd_era_recover: - check_then_set (&cqr->status, - CQR_STATUS_IN_IO, - CQR_STATUS_ERROR); - break; - default: - BUG (); + } else { + /* any other kind of interrupt - just retry */ + DEV_MESSAGE (KERN_DEBUG, device, + "Got unclassified interrupt CS/DS %04x flag %08x", + ((stat->cstat<<8)|stat->dstat), + stat->flag); + era = dasd_era_recover; + } + + switch (era) { + case dasd_era_none: + check_then_set(&cqr->status, + CQR_STATUS_IN_IO, + CQR_STATUS_DONE); + cqr->stopclk = get_clock (); + /* start the next queued request if possible -> fast_io */ + if (cqr->next && + cqr->next->status == CQR_STATUS_QUEUED) { + if (device->discipline->start_IO (cqr->next) != 0) { + MESSAGE (KERN_WARNING, "%s", + "Interrupt fastpath failed!"); + } } - } + break; + case dasd_era_fatal: + check_then_set (&cqr->status, + CQR_STATUS_IN_IO, + CQR_STATUS_FAILED); + cqr->stopclk = get_clock (); + break; + case dasd_era_recover: + check_then_set (&cqr->status, + CQR_STATUS_IN_IO, + CQR_STATUS_ERROR); + break; + default: + BUG (); + } + + /* handle special device initialization request */ if ( cqr == device->init_cqr && ( cqr->status == CQR_STATUS_DONE || cqr->status == CQR_STATUS_FAILED )){ @@ -2033,59 +2509,47 @@ } /* end dasd_int_handler */ -/* SECTION: Some stuff related to error recovery */ +/******************************************************************************** + * SECTION: Some stuff related to error recovery + ********************************************************************************/ /* * DEFAULT_ERP_ACTION * * DESCRIPTION - * sets up the default-ERP ccw_req_t, namely one, which performs a TIC - * to the original channel program with a retry counter of 16 + * just retries the current cqr * * PARAMETER * cqr failed CQR * * RETURN VALUES - * erp CQR performing the ERP + * cqr modified CQR */ ccw_req_t * dasd_default_erp_action (ccw_req_t * cqr) { dasd_device_t *device = cqr->device; - ccw_req_t *erp = dasd_alloc_request ((char *) &cqr->magic, 1, 0, cqr->device); - - printk (KERN_DEBUG PRINTK_HEADER "Default ERP called... \n"); - - if (!erp) { - - DASD_MESSAGE (KERN_ERR, device, "%s", - "Unable to allocate ERP request"); - + // just retry - there is nothing to save ... I got no sense data.... + if (cqr->retries > 0) { + DEV_MESSAGE (KERN_DEBUG, device, + "default ERP called (%i retries left)", + cqr->retries); + check_then_set (&cqr->status, CQR_STATUS_ERROR, - CQR_STATUS_FAILED); - - asm volatile ("STCK %0":"=m" (cqr->stopclk)); - - return cqr; - } - - erp->cpaddr->cmd_code = CCW_CMD_TIC; - erp->cpaddr->cda = (__u32) (addr_t) cqr->cpaddr; - erp->function = dasd_default_erp_action; - erp->refers = cqr; - erp->device = cqr->device; - erp->magic = cqr->magic; - erp->retries = 16; - - erp->status = CQR_STATUS_FILLED; - - dasd_chanq_enq_head (&device->queue, - erp); - - return erp; - + CQR_STATUS_QUEUED); + } else { + DEV_MESSAGE (KERN_WARNING, device, "%s", + "default ERP called (NO retry left)"); + + check_then_set (&cqr->status, + CQR_STATUS_ERROR, + CQR_STATUS_FAILED); + + cqr->stopclk = get_clock (); + } + return cqr; } /* end dasd_default_erp_action */ /* @@ -2141,26 +2605,21 @@ /* save ptr to original cqr */ cqr = erp; - /* set corresponding status to original cqr */ + /* set corresponding status for original cqr */ if (success) { - - check_then_set (&cqr->status, - CQR_STATUS_ERROR, - CQR_STATUS_DONE); + cqr->status = CQR_STATUS_DONE; } else { - - check_then_set (&cqr->status, - CQR_STATUS_ERROR, - CQR_STATUS_FAILED); - - asm volatile ("STCK %0":"=m" (cqr->stopclk)); + cqr->status = CQR_STATUS_FAILED; + cqr->stopclk = get_clock (); } return cqr; } /* end default_erp_postaction */ -/* SECTION: The helpers of the struct file_operations */ +/******************************************************************************** + * SECTION: The helpers of the struct file_operations + ********************************************************************************/ /* * function dasd_format @@ -2175,39 +2634,41 @@ { int rc = 0; int openct = atomic_read (&device->open_count); + ccw_req_t *req; if (openct > 1) { - DASD_MESSAGE (KERN_WARNING, device, "%s", - "dasd_format: device is open! expect errors."); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "dasd_format: device is open! " + "expect errors."); } - DASD_MESSAGE (KERN_INFO, device, - "formatting units %d to %d (%d B blocks) flags %d", - fdata->start_unit, - fdata->stop_unit, - fdata->blksize, - fdata->intensity); + + DBF_DEV_EVENT (DBF_NOTICE, device, + "formatting units %d to %d (%d B blocks) flags %d", + fdata->start_unit, + fdata->stop_unit, + fdata->blksize, + fdata->intensity); + while ((!rc) && (fdata->start_unit <= fdata->stop_unit)) { - ccw_req_t *req; - dasd_format_fn_t ffn = device->discipline->format_device; - ffn = device->discipline->format_device; - if (ffn == NULL) + + if (device->discipline->format_device == NULL) break; - req = ffn (device, fdata); + + req = device->discipline->format_device (device, fdata); if (req == NULL) { rc = -ENOMEM; break; } if ((rc = dasd_sleep_on_req (req)) != 0) { - DASD_MESSAGE (KERN_WARNING, device, - " Formatting of unit %d failed with rc = %d\n", - fdata->start_unit, rc); + + DEV_MESSAGE (KERN_WARNING, device, + " Formatting of unit %d failed " + "with rc = %d", + fdata->start_unit, rc); break; } dasd_free_request (req, device); /* request is no longer used */ - if ( signal_pending(current) ) { - rc = -ERESTARTSYS; - break; - } fdata->start_unit++; } return rc; @@ -2258,6 +2719,9 @@ return 0; } +/* + * handle the re-read partition table IOCTL (BLKRRPART) + */ static int dasd_revalidate (dasd_device_t * device) { @@ -2267,8 +2731,9 @@ int openct = atomic_read (&device->open_count); int start = MINOR (kdev); if (openct != 1) { - DASD_MESSAGE (KERN_WARNING, device, "%s", - "BLKRRPART: device is open! expect errors."); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "BLKRRPART: device is open! expect errors."); } for (i = (1 << DASD_PARTN_BITS) - 1; i >= 0; i--) { int major = device->major_info->gendisk.major; @@ -2279,6 +2744,15 @@ return rc; } + +/* + * function do_dasd_ioctl + * Implementation of the DASD API. + * Changes to the API should be binary compatible to privous versions + * of the user-space applications by means of any already existing tool + * (e.g. dasdfmt) must work with the new kernel API. + */ + static int do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data) { @@ -2287,10 +2761,12 @@ major_info_t *major_info; if (!device) { - printk (KERN_WARNING PRINTK_HEADER - "No device registered as device (%d:%d)\n", - MAJOR (inp->i_rdev), - MINOR (inp->i_rdev)); + + MESSAGE (KERN_WARNING, + "No device registered as device (%d:%d)", + MAJOR (inp->i_rdev), + MINOR (inp->i_rdev)); + return -EINVAL; } if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) { @@ -2298,225 +2774,356 @@ return -EINVAL; } major_info = device->major_info; -#if 0 - printk (KERN_DEBUG PRINTK_HEADER - "ioctl 0x%08x %s'0x%x'%d(%d) on /dev/%s (%d:%d," - " devno 0x%04x on irq %d) with data %8lx\n", - no, - _IOC_DIR (no) == _IOC_NONE ? "0" : - _IOC_DIR (no) == _IOC_READ ? "r" : - _IOC_DIR (no) == _IOC_WRITE ? "w" : - _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u", - _IOC_TYPE (no), - _IOC_NR (no), - _IOC_SIZE (no), - device->name, - MAJOR (inp->i_rdev), - MINOR (inp->i_rdev), - device->devinfo.devno, - device->devinfo.irq, - data); -#endif + + DBF_DEV_EVENT (DBF_DEBUG, device, + "ioctl 0x%08x %s'0x%x'%d(%d) with data %8lx", + no, + (_IOC_DIR (no) == _IOC_NONE ? "0" : + _IOC_DIR (no) == _IOC_READ ? "r" : + _IOC_DIR (no) == _IOC_WRITE ? "w" : + _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u"), + _IOC_TYPE (no), + _IOC_NR (no), + _IOC_SIZE (no), + data); + switch (no) { - case DASDAPIVER: { - int ver = DASD_API_VERSION; - rc = put_user(ver, (int *) data); - break; + case DASDAPIVER: { /* retrun dasd API version */ + int ver = DASD_API_VERSION; + rc = put_user(ver, (int *) data); + break; + } + case BLKGETSIZE: { /* Return device size in # of sectors */ + long blocks = major_info->gendisk.sizes + [MINOR (inp->i_rdev)] << 1; + rc = put_user(blocks, (long *) data); + break; + } + case BLKGETSIZE64:{ + u64 blocks = major_info->gendisk.sizes + [MINOR (inp->i_rdev)]; + rc = put_user(blocks << 10, (u64 *) data); + break; + } + case BLKRRPART: { /* reread partition table */ + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + rc = dasd_revalidate (device); + break; } - case BLKGETSIZE:{ /* Return device size */ - long blocks = major_info->gendisk.sizes - [MINOR (inp->i_rdev)] << 1; - rc = put_user(blocks, (long *) data); - break; - } - case BLKGETSIZE64:{ - u64 blocks = major_info->gendisk.sizes - [MINOR (inp->i_rdev)]; - rc = put_user(blocks << 10, (u64 *) data); - break; - } - case BLKRRPART:{ - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - rc = dasd_revalidate (device); - break; - } - case HDIO_GETGEO:{ - struct hd_geometry geo = { 0, }; - rc = dasd_fillgeo (inp->i_rdev, &geo); - if (rc) - break; + case HDIO_GETGEO: { /* return disk geometry */ + struct hd_geometry geo = { 0, }; + rc = dasd_fillgeo (inp->i_rdev, &geo); + if (rc) + break; - rc = copy_to_user ((struct hd_geometry *) data, &geo, - sizeof (struct hd_geometry)); - if (rc) - rc = -EFAULT; - break; - } - case BIODASDDISABLE:{ - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - if ( device->level > DASD_STATE_ACCEPT) { - dasd_deactivate_queue(device); - if ( device->request_queue) - dasd_flush_request_queues(device,0); - dasd_flush_chanq(device,0); - dasd_disable_blkdev(device); - dasd_set_device_level (device->devinfo.devno, - device->discipline, - DASD_STATE_ACCEPT); - } + rc = copy_to_user ((struct hd_geometry *) data, &geo, + sizeof (struct hd_geometry)); + if (rc) + rc = -EFAULT; + break; + } + case BIODASDDISABLE: { /* disable device */ + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + if ( device->level > DASD_STATE_ACCEPT) { + dasd_deactivate_queue(device); + if ( device->request_queue) + dasd_flush_request_queues(device,0); + dasd_flush_chanq(device,0); + dasd_disable_blkdev(device); + dasd_set_device_level (device->devinfo.devno, + device->discipline, + DASD_STATE_ACCEPT); + } + + break; + } + case BIODASDENABLE: { /* enable device */ + dasd_range_t range = { + from: device->devinfo.devno, + to: device->devinfo.devno + }; + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; break; + } + dasd_enable_ranges (&range, device->discipline, 0); + break; } - case BIODASDENABLE:{ - dasd_range_t range = { - from: device->devinfo.devno, - to: device->devinfo.devno - }; - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - dasd_enable_ranges (&range, device->discipline, 0); + case BIODASDFMT: { /* format device */ + /* fdata == NULL is no longer a valid arg to dasd_format ! */ + int partn = MINOR (inp->i_rdev) & + ((1 << major_info->gendisk.minor_shift) - 1); + format_data_t fdata; + + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) { + rc = -EROFS; + break; + } + if (!data) { + rc = -EINVAL; break; + } + rc = copy_from_user (&fdata, (void *) data, + sizeof (format_data_t)); + if (rc) { + rc = -EFAULT; + break; + } + if (partn != 0) { + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "Cannot low-level format a partition"); + + return -EINVAL; + } + rc = dasd_format (device, &fdata); + break; } - case BIODASDFMT:{ - /* fdata == NULL is no longer a valid arg to dasd_format ! */ - int partn = MINOR (inp->i_rdev) & - ((1 << major_info->gendisk.minor_shift) - 1); - format_data_t fdata; + case BIODASDSATTR: { /* Set Attributes (cache operations) */ - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) { - rc = -EROFS; - break; + attrib_data_t attrib; + + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + if (!data) { + rc = -EINVAL; + break; + } + + if (!device->discipline->set_attrib) { + rc = -EINVAL; + break; + } + + rc = copy_from_user (&attrib, (void *) data, + sizeof (attrib_data_t)); + if (rc) { + rc = -EFAULT; + break; + } + + rc = device->discipline->set_attrib (device, + &attrib); + break; + } + case BIODASDPRRST: { /* reset device profile information */ + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + memset (&device->profile, 0, + sizeof (dasd_profile_info_t)); + break; + } + case BIODASDPRRD: { /* return device profile information */ + rc = copy_to_user((long *)data, + (long *)&device->profile, + sizeof(dasd_profile_info_t)); + if (rc) + rc = -EFAULT; + break; + } + case BIODASDRSRV: { /* reserve device */ + ccw_req_t *cqr; + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + if (!device->discipline->reserve) { + rc = -EINVAL; + break; + } + + cqr = device->discipline->reserve (device); + + if (cqr) { + struct timer_list res_timer; + + init_timer (&res_timer); + res_timer.function = dasd_resrel_timeout; + res_timer.data = (unsigned long) cqr; + res_timer.expires = jiffies + 2 * HZ; + add_timer (&res_timer); + + rc = dasd_sleep_on_immediate (cqr); + + del_timer_sync (&res_timer); + dasd_free_request (cqr, + device); + } else { + rc = -ENOMEM; + } + break; + } + case BIODASDRLSE: { /* release device */ + ccw_req_t *cqr; + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + if (!device->discipline->release) { + rc = -EINVAL; + break; + } + + cqr = device->discipline->release (device); + + if (cqr) { + struct timer_list rel_timer; + + init_timer (&rel_timer); + rel_timer.function = dasd_resrel_timeout; + rel_timer.data = (unsigned long) cqr; + rel_timer.expires = jiffies + 2 * HZ; + add_timer (&rel_timer); + + rc = dasd_sleep_on_immediate (cqr); + + del_timer_sync (&rel_timer); /* in case of interrupt */ + dasd_free_request (cqr, + device); + } else { + rc = -ENOMEM; + } + break; + } + case BIODASDSLCK: { /* steal lock - unconditional reserve device */ + if (!capable (CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + rc = dasd_steal_lock (device); + break; + } + case BIODASDINFO: /* return dasd information */ + case BIODASDINFO2: { /* return dasd information2 (incl. format and features) */ + dasd_information2_t dasd_info; + + unsigned long flags; + + if (!device->discipline->fill_info) { + rc = -EINVAL; + break; + } + + rc = device->discipline->fill_info (device, + &dasd_info); + + dasd_info.label_block = device->sizes.pt_block; + dasd_info.devno = device->devinfo.devno; + dasd_info.schid = device->devinfo.irq; + dasd_info.cu_type = device->devinfo.sid_data.cu_type; + dasd_info.cu_model = device->devinfo.sid_data.cu_model; + dasd_info.dev_type = device->devinfo.sid_data.dev_type; + dasd_info.dev_model = device->devinfo.sid_data.dev_model; + dasd_info.open_count = + atomic_read (&device->open_count); + dasd_info.status = device->level; + + /* check if device is really formatted - LDL / CDL was returned by 'fill_info' */ + if ((device->level < DASD_STATE_READY) || + (dasd_check_bp_block (device) ) ) { + dasd_info.format = DASD_FORMAT_NONE; + } + + dasd_info.features = + dasd_features_from_devno (device->devinfo.devno); + + if (device->discipline) { + memcpy (dasd_info.type, + device->discipline->name, 4); + } else { + memcpy (dasd_info.type, "none", 4); + } + dasd_info.req_queue_len = 0; + dasd_info.chanq_len = 0; + + if ((device->request_queue ) && + (device->request_queue->request_fn) ) { + struct list_head *l; + ccw_req_t *cqr = device->queue.head; + spin_lock_irqsave (&io_request_lock, flags); + list_for_each (l, + &device->request_queue-> + queue_head) { + dasd_info.req_queue_len++; } - if (!data) { - rc = -EINVAL; - break; - } - rc = copy_from_user (&fdata, (void *) data, - sizeof (format_data_t)); - if (rc) { - rc = -EFAULT; - break; - } - if (partn != 0) { - DASD_MESSAGE (KERN_WARNING, device, "%s", - "Cannot low-level format a partition"); - return -EINVAL; - } - rc = dasd_format (device, &fdata); - break; - } - case BIODASDPRRST:{ /* reset device profile information */ - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - memset (&device->profile, 0, - sizeof (dasd_profile_info_t)); - break; - } - case BIODASDPRRD:{ /* retrun device profile information */ - rc = copy_to_user((long *)data, - (long *)&device->profile, - sizeof(dasd_profile_info_t)); - if (rc) - rc = -EFAULT; - break; - } - case BIODASDRSRV:{ /* reserve */ - ccw_req_t *req; - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - req = device->discipline->reserve (device); - rc = dasd_sleep_on_req (req); - dasd_free_request (req, device); - break; - } - case BIODASDRLSE:{ /* release */ - ccw_req_t *req; - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - req = device->discipline->release (device); - rc = dasd_sleep_on_req (req); - dasd_free_request (req, device); - break; - } - case BIODASDSLCK:{ /* steal lock - unconditional reserve */ - ccw_req_t *req; - if (!capable (CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - req = device->discipline->steal_lock (device); - rc = dasd_sleep_on_req (req); - dasd_free_request (req, device); - break; - } - case BIODASDINFO:{ - dasd_information_t dasd_info; - unsigned long flags; - rc = device->discipline->fill_info (device, &dasd_info); - dasd_info.label_block = device->sizes.pt_block; - dasd_info.devno = device->devinfo.devno; - dasd_info.schid = device->devinfo.irq; - dasd_info.cu_type = device->devinfo.sid_data.cu_type; - dasd_info.cu_model = device->devinfo.sid_data.cu_model; - dasd_info.dev_type = device->devinfo.sid_data.dev_type; - dasd_info.dev_model = device->devinfo.sid_data.dev_model; - dasd_info.open_count = - atomic_read (&device->open_count); - dasd_info.status = device->level; - if (device->discipline) { - memcpy (dasd_info.type, - device->discipline->name, 4); - } else { - memcpy (dasd_info.type, "none", 4); - } - dasd_info.req_queue_len = 0; - dasd_info.chanq_len = 0; - if (device->request_queue->request_fn) { - struct list_head *l; - ccw_req_t *cqr = device->queue.head; - spin_lock_irqsave (&io_request_lock, flags); - list_for_each (l, - &device->request_queue-> - queue_head) { - dasd_info.req_queue_len++; - } - spin_unlock_irqrestore (&io_request_lock, - flags); - s390irq_spin_lock_irqsave (device->devinfo.irq, - flags); - while (cqr) { - cqr = cqr->next; - dasd_info.chanq_len++; - } - s390irq_spin_unlock_irqrestore (device->devinfo. - irq, flags); - } - rc = - copy_to_user ((long *) data, (long *) &dasd_info, - sizeof (dasd_information_t)); - if (rc) - rc = -EFAULT; - break; - } + spin_unlock_irqrestore (&io_request_lock, + flags); + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + while (cqr) { + cqr = cqr->next; + dasd_info.chanq_len++; + } + s390irq_spin_unlock_irqrestore (device->devinfo. + irq, flags); + } + + rc = copy_to_user ((long *) data, (long *) &dasd_info, + ((no == (unsigned int) BIODASDINFO2) ? + sizeof (dasd_information2_t) : + sizeof (dasd_information_t))); + + if (rc) + rc = -EFAULT; + break; + } + case BIODASDPSRD: { /* Performance Statistics Read */ + + ccw_req_t *cqr; + dasd_rssd_perf_stats_t *stats; + + if ((!device->discipline->read_stats) || + (!device->discipline->ret_stats ) ) { + rc = -EINVAL; + break; + } + + cqr = device->discipline->read_stats (device); + + if (cqr) { + + if ((rc = dasd_sleep_on_req (cqr)) == 0) { + + if ((stats = device->discipline->ret_stats (cqr)) != NULL) { + + rc = copy_to_user ((long *) data, + (long *) stats, + sizeof (dasd_rssd_perf_stats_t)); + } else { + + rc = -EFAULT; + } + } + + dasd_free_request (cqr, + device); + + } else { + rc = -ENOMEM; + } + break; + } #if 0 /* needed for XFS */ - case BLKBSZSET:{ + case BLKBSZSET: { int bsz; rc = copy_from_user ((long *)&bsz,(long *)data,sizeof(int)); if ( rc ) { @@ -2528,10 +3135,46 @@ rc = -EINVAL; } break; - } + } #endif /* 0 */ + case BLKROSET: { + int intval; + dasd_range_t *temp; + int devindex = 0; + unsigned long flags; + struct list_head *l; + int major=MAJOR(device->kdev); + int minor; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (inp->i_rdev != device->kdev) + // ro setting is not allowed for partitions + return -EINVAL; + if (get_user(intval, (int *)(data))) + return -EFAULT; + spin_lock_irqsave (&range_lock, flags); + list_for_each (l, &dasd_range_head.list) { + temp = list_entry (l, dasd_range_t, list); + if (device->devinfo.devno >= temp->from && device->devinfo.devno <= temp->to) { + spin_unlock_irqrestore (&range_lock, flags); + if (intval) + temp->features |= DASD_FEATURE_READONLY; + else + temp->features &= ~DASD_FEATURE_READONLY; + goto continue_blkroset; + } + devindex += temp->to - temp->from + 1; + } + spin_unlock_irqrestore (&range_lock, flags); + return(-ENODEV); +continue_blkroset: + for (minor = MINOR(device->kdev); minor < MINOR(device->kdev) + (1 << DASD_PARTN_BITS); minor++) + set_device_ro(MKDEV(major,minor), intval); + return 0; + } + case BLKBSZGET: case BLKSSZGET: - case BLKROSET: case BLKROGET: case BLKRASET: case BLKRAGET: @@ -2541,37 +3184,41 @@ case BLKELVSET: return blk_ioctl (inp->i_rdev, no, data); break; - default:{ + default: { - dasd_ioctl_list_t *old = dasd_find_ioctl (no); - if (old) { - if ( old->owner ) - __MOD_INC_USE_COUNT(old->owner); - rc = old->handler (inp, no, data); - if ( old->owner ) - __MOD_DEC_USE_COUNT(old->owner); - } else { - DASD_MESSAGE (KERN_INFO, device, - "ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx\n", - no, - _IOC_DIR (no) == _IOC_NONE ? "0" : - _IOC_DIR (no) == _IOC_READ ? "r" : - _IOC_DIR (no) == _IOC_WRITE ? "w" : - _IOC_DIR (no) == - (_IOC_READ | _IOC_WRITE) ? "rw" : "u", - _IOC_TYPE (no), - _IOC_NR (no), - _IOC_SIZE (no), - data); - rc = -ENOTTY; - } - break; + dasd_ioctl_list_t *old = dasd_find_ioctl (no); + if (old) { + if ( old->owner ) + __MOD_INC_USE_COUNT(old->owner); + rc = old->handler (inp, no, data); + if ( old->owner ) + __MOD_DEC_USE_COUNT(old->owner); + } else { + + DBF_DEV_EVENT (DBF_INFO, device, + "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", + no, + (_IOC_DIR (no) == _IOC_NONE ? "0" : + _IOC_DIR (no) == _IOC_READ ? "r" : + _IOC_DIR (no) == _IOC_WRITE ? "w" : + _IOC_DIR (no) == + (_IOC_READ | _IOC_WRITE) ? "rw" : "u"), + _IOC_TYPE (no), + _IOC_NR (no), + _IOC_SIZE (no), + data); + + rc = -ENOTTY; + } + break; } } return rc; } -/* SECTION: The members of the struct file_operations */ +/******************************************************************************** + * SECTION: The members of the struct file_operations + ********************************************************************************/ static int dasd_ioctl (struct inode *inp, struct file *filp, @@ -2597,26 +3244,32 @@ goto fail; } if (dasd_probeonly) { - printk ("\n" KERN_INFO PRINTK_HEADER - "No access to device (%d:%d) due to probeonly mode\n", - MAJOR (inp->i_rdev), - MINOR (inp->i_rdev)); + + MESSAGE (KERN_INFO, + "No access to device (%d:%d) due to probeonly mode", + MAJOR (inp->i_rdev), + MINOR (inp->i_rdev)); + rc = -EPERM; goto fail; } spin_lock_irqsave(&discipline_lock,flags); device = dasd_device_from_kdev (inp->i_rdev); if (!device) { - printk (KERN_WARNING PRINTK_HEADER - "No device registered as (%d:%d)\n", - MAJOR (inp->i_rdev), - MINOR (inp->i_rdev)); + + MESSAGE (KERN_WARNING, + "No device registered as (%d:%d)", + MAJOR (inp->i_rdev), + MINOR (inp->i_rdev)); + rc = -ENODEV; goto unlock; } if (device->level <= DASD_STATE_ACCEPT ) { - DASD_MESSAGE (KERN_WARNING, device, " %s", - " Cannot open unrecognized device\n"); + + DBF_DEV_EVENT (DBF_ERR, device, " %s", + " Cannot open unrecognized device"); + rc = -ENODEV; goto unlock; } @@ -2648,17 +3301,21 @@ } device = dasd_device_from_kdev (inp->i_rdev); if (!device) { - printk (KERN_WARNING PRINTK_HEADER - "No device registered as %d:%d\n", - MAJOR (inp->i_rdev), - MINOR (inp->i_rdev)); + + MESSAGE (KERN_WARNING, + "No device registered as %d:%d", + MAJOR (inp->i_rdev), + MINOR (inp->i_rdev)); + rc = -EINVAL; goto out; } if (device->level < DASD_STATE_ACCEPT ) { - DASD_MESSAGE (KERN_WARNING, device, " %s", - " Cannot release unrecognized device\n"); + + DBF_DEV_EVENT (DBF_ERR, device, " %s", + " Cannot release unrecognized device"); + rc = -ENODEV; goto out; } @@ -2669,8 +3326,9 @@ __MOD_DEC_USE_COUNT(device->discipline->owner); } else if ( count == -1 ) { /* paranoia only */ atomic_set (&device->open_count,0); - printk (KERN_WARNING PRINTK_HEADER - "release called with open count==0\n"); + + MESSAGE (KERN_WARNING, "%s", + "release called with open count==0"); } out: return rc; @@ -2685,7 +3343,9 @@ ioctl:dasd_ioctl, }; -/* SECTION: Management of device list */ +/******************************************************************************** + * SECTION: Management of device list + ********************************************************************************/ int dasd_fillgeo(int kdev,struct hd_geometry *geo) { @@ -2708,24 +3368,27 @@ int dasd_device_name (char *str, int index, int partition, struct gendisk *hd) { - int len = 0; + major_info_t *major_info; + struct list_head *l; char first, second, third; + int len; - if (hd) { - major_info_t *major_info = NULL; - struct list_head *l; - - list_for_each (l, &dasd_major_info[0].list) { - major_info = list_entry (l, major_info_t, list); - if (&major_info->gendisk == hd) { - break; - } - index += DASD_PER_MAJOR; - } - if (major_info == &dasd_major_info[0]) { - return -EINVAL; - } - } + if (hd == NULL) + return -EINVAL; + + major_info = NULL; + list_for_each (l, &dasd_major_info) { + major_info = list_entry (l, major_info_t, list); + if (&major_info->gendisk == hd) + break; + index += DASD_PER_MAJOR; + } + if (major_info == NULL || &major_info->gendisk != hd) { + /* list empty or hd not found in list */ + return -EINVAL; + } + + len = 0; third = index % 26; second = ((index - 26) / 26) % 26; first = (((index - 702) / 26) / 26) % 26; @@ -2777,7 +3440,8 @@ cqr->status != CQR_STATUS_FAILED ) { cqr->status = CQR_STATUS_FAILED; - asm volatile ("STCK %0":"=m" (cqr->stopclk)); + + cqr->stopclk = get_clock (); } dasd_schedule_bh(device); @@ -2802,6 +3466,56 @@ } } +static inline void dasd_do_hotplug_event (dasd_device_t* device, int eventid) { +#ifdef CONFIG_HOTPLUG + int i; + char *argv[3], *envp[8]; + char devno[20],major[20],minor[20],devname[26],action[20]; + + /* setup command line arguments */ + i=0; + argv[i++] = hotplug_path; + argv[i++] = "dasd"; + argv[i++] = 0; + + /* minimal environment */ + i=0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* device information and event*/ + sprintf (devno, "DEVNO=%04x", device->devinfo.devno); + sprintf (major, "MAJOR=%d", MAJOR(device->kdev)); + sprintf (minor, "MINOR=%d", MINOR(device->kdev)); + sprintf (devname, "DASDNAME=%s",device->name); + switch (eventid) { + case DASD_HOTPLUG_EVENT_ADD: + sprintf (action,"ACTION=add"); + break; + case DASD_HOTPLUG_EVENT_REMOVE: + sprintf (action,"ACTION=remove"); + break; + case DASD_HOTPLUG_EVENT_PARTCHK: + sprintf (action,"ACTION=partchk"); + break; + case DASD_HOTPLUG_EVENT_PARTREMOVE: + sprintf (action,"ACTION=partremove"); + break; + default: + BUG(); + } + envp[i++] = devno; + envp[i++] = major; + envp[i++] = minor; + envp[i++] = devname; + envp[i++] = action; + envp[i++] = 0; + + call_usermodehelper (argv [0], argv, envp); +#endif +} + + static int dasd_disable_volume ( dasd_device_t * device, int force ) { @@ -2810,8 +3524,9 @@ int count = atomic_read (&device->open_count); if ( count ) { - DASD_MESSAGE (KERN_EMERG, device, "%s", - "device has vanished although it was open!"); + + DEV_MESSAGE (KERN_EMERG, device, "%s", + "device has vanished although it was open!"); } if ( force ) { dasd_deactivate_queue(device); @@ -2824,9 +3539,11 @@ /* unregister partitions ('ungrok_partitions') */ devfs_register_partitions(&device->major_info->gendisk, MINOR(device->kdev),1); + dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTREMOVE); - DASD_MESSAGE (KERN_WARNING, device, - "disabling device, target state: %d",target); + DBF_DEV_EVENT (DBF_ERR, device, + "disabling device, target state: %d", + target); dasd_set_device_level (device->devinfo.devno, device->discipline, @@ -2836,7 +3553,7 @@ static void dasd_disable_ranges (dasd_range_t *range, - dasd_discipline_t *d, + dasd_discipline_t *discipline, int all, int force ) { dasd_range_t *rrange; @@ -2858,14 +3575,15 @@ } device = *dptr; if (device == NULL || - (d != NULL && - device -> discipline != d)) + (discipline != NULL && + device -> discipline != discipline)) continue; dasd_disable_volume(device, force); } rrange = list_entry (rrange->list.next, dasd_range_t, list); } while ( all && rrange && rrange != range ); + } static void @@ -2877,10 +3595,13 @@ } static void -dasd_enable_ranges (dasd_range_t *range, dasd_discipline_t *d, int all ) +dasd_enable_ranges (dasd_range_t *range, + dasd_discipline_t *discipline, + int all) { int retries = 0; int j; + int do_again; kdev_t tempdev; dasd_range_t *rrange; @@ -2888,6 +3609,7 @@ return; do { + do_again = 0; if (range == &dasd_range_head) { rrange = list_entry (range->list.next, dasd_range_t, list); @@ -2898,26 +3620,40 @@ for (j = rrange->from; j <= rrange->to; j++) { if ( dasd_devindex_from_devno(j) < 0 ) continue; - dasd_set_device_level (j, d, DASD_STATE_ONLINE); + if (-EAGAIN == dasd_set_device_level + (j, discipline, DASD_STATE_ONLINE)) + do_again = 1; } rrange = list_entry (rrange->list.next, dasd_range_t, list); } while ( all && rrange && rrange != range ); - if (atomic_read (&dasd_init_pending) == 0) /* we are done, exit loop */ + if ((atomic_read (&dasd_init_pending) == 0) && + (!do_again)) /* we are done, exit loop */ break; if ( retries == 0 ) { - printk (KERN_INFO PRINTK_HEADER - "waiting for responses...\n"); + + MESSAGE (KERN_INFO, "%s", + "waiting for responses..."); + } else if ( retries < 5 ) { - printk (KERN_INFO PRINTK_HEADER - "waiting a little bit longer...\n"); + + DBF_EVENT (DBF_NOTICE, "%s", + "waiting a little bit longer..."); + } else { - printk (KERN_INFO PRINTK_HEADER - "giving up, enable late devices manually!\n"); + + MESSAGE (KERN_INFO, "%s", + "giving up, enable late devices manually!"); break; } - interruptible_sleep_on_timeout (&dasd_init_waitq, (1 * HZ)); + + /* prevent scheduling if called by bh (timer) */ + if (!in_interrupt()) { + interruptible_sleep_on_timeout (&dasd_init_waitq, + (1 * HZ) ); + } + retries ++; } while (1); /* now setup block devices */ @@ -2939,9 +3675,9 @@ device = *dptr; if (device == NULL ) continue; - if ( ((d == NULL && device->discipline != NULL) || - (device->discipline == d )) && - device->level >= DASD_STATE_READY && + if ( ((discipline == NULL && device->discipline != NULL) || + (device->discipline == discipline )) && + device->level == DASD_STATE_ONLINE && device->request_queue == NULL ) { if (dasd_features_from_devno(j)&DASD_FEATURE_READONLY) { for (tempdev=device->kdev; @@ -2949,9 +3685,8 @@ tempdev++) set_device_ro (tempdev, 1); - printk (KERN_WARNING PRINTK_HEADER - "setting read-only mode for device /dev/%s\n", - device->name); + DEV_MESSAGE (KERN_WARNING, device, "%s", + "setting read-only mode "); } dasd_setup_blkdev(device); dasd_setup_partitions(device); @@ -2971,13 +3706,17 @@ static void dasd_not_oper_handler (int irq, int status) { - dasd_device_t *device = NULL; - major_info_t *major_info = NULL; + dasd_device_t *device; + major_info_t *major_info; + ccw_req_t* cqr; struct list_head *l; - int i, devno = -ENODEV; + unsigned long flags; + int i, devno; /* find out devno of leaving device: CIO has already deleted this information ! */ - list_for_each (l, &dasd_major_info[0].list) { + devno = -ENODEV; + device = NULL; + list_for_each (l, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); for (i = 0; i < DASD_PER_MAJOR; i++) { device = major_info->dasd_device[i]; @@ -2990,17 +3729,44 @@ break; } - DASD_DRIVER_DEBUG_EVENT (5, dasd_not_oper_handler, - "called for devno %04x", - devno); - if (devno < 0) { - printk (KERN_WARNING PRINTK_HEADER - "not_oper_handler called on irq 0x%04x no devno!\n", - irq); + + MESSAGE (KERN_WARNING, + "not_oper_handler called on irq 0x%04x no devno!", + irq); return; } - dasd_disable_volume(device, 1); + switch (status) { + case DEVSTAT_DEVICE_GONE: + case DEVSTAT_REVALIDATE: //FIXME + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "device is gone, disabling it permanently\n"); + dasd_disable_volume(device, 1); + break; + case DEVSTAT_NOT_ACC: + case DEVSTAT_NOT_ACC_ERR: + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "device is not accessible, disabling it temporary\n"); + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + device->accessible = 0; + if (status == DEVSTAT_NOT_ACC_ERR) { + cqr = device->queue.head; + while (cqr) { + if (cqr->status == CQR_STATUS_QUEUED) + break; + if (cqr->status == CQR_STATUS_IN_IO) + cqr->status = CQR_STATUS_QUEUED; + cqr = cqr->next; + } + } + s390irq_spin_unlock_irqrestore(device->devinfo.irq, + flags); + + break; + default: + panic ("dasd not operational handler was called with illegal status\n"); + } } /* @@ -3014,56 +3780,62 @@ { int devno; int rc = 0; - major_info_t *major_info = NULL; + major_info_t *major_info; dasd_range_t *rptr,range; - dasd_device_t *device = NULL; + dasd_device_t *device; struct list_head *l; + unsigned long flags; int i; + devno = get_devno_by_irq (irq); if (devno == -ENODEV) { rc = -ENODEV; goto out; } - DASD_DRIVER_DEBUG_EVENT (5, dasd_oper_handler, - "called for devno %04x", - devno); - /* find out devno of device */ - list_for_each (l, &dasd_major_info[0].list) { + device = NULL; + list_for_each (l, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); for (i = 0; i < DASD_PER_MAJOR; i++) { device = major_info->dasd_device[i]; - if (device && device->devinfo.irq == irq) { - devno = device->devinfo.devno; + if (device && device->devinfo.irq == irq) break; - } + else + device = NULL; } - if (devno != -ENODEV) + if (device) break; } - if (devno < 0) { - BUG(); - } + if ( device && - device->level == DASD_STATE_READY ) { - dasd_set_device_level (device->devinfo.devno, - device->discipline, DASD_STATE_ONLINE); + (device->level == DASD_STATE_ONLINE) && + (!device->accessible) ) { + s390irq_spin_lock_irqsave (device->devinfo.irq, + flags); + DEV_MESSAGE (KERN_DEBUG, device, "%s", + "device is accessible again, reenabling it\n"); + device->accessible = 1; + s390irq_spin_unlock_irqrestore(device->devinfo.irq, + flags); + + dasd_schedule_bh(device); } else { - if (dasd_autodetect) { - rptr = dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES); - if ( rptr == NULL ) { - rc = -ENOMEM; - goto out; - } - } else { - range.from = devno; - range.to = devno; - rptr = ⦥ - } - dasd_enable_ranges (rptr, NULL, 0); + + if (dasd_autodetect) { + rptr = dasd_add_range (devno, devno, DASD_FEATURE_DEFAULT); + if ( rptr == NULL ) { + rc = -ENOMEM; + goto out; + } + } else { + range.from = devno; + range.to = devno; + rptr = ⦥ + } + dasd_enable_ranges (rptr, NULL, 0); } out: return rc; @@ -3075,13 +3847,16 @@ { dasd_device_t **device_addr; - DASD_DRIVER_DEBUG_EVENT (1, dasd_find_device_addr, - "devno %04x", - devno); + DBF_EVENT (DBF_INFO, + "devno %04x", + devno); + if ( dasd_devindex_from_devno (devno) < 0 ) { - DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, - "no dasd: devno %04x", - devno); + + DBF_EXC (DBF_ALERT, + "no dasd: devno %04x", + devno); + return NULL; } /* allocate major numbers on demand for new devices */ @@ -3090,9 +3865,8 @@ if ((rc = dasd_register_major (NULL)) <= 0) { - DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr, - "%s", - "out of major numbers!"); + DBF_EXC (DBF_ALERT, "%s", + "out of major numbers!"); break; } } @@ -3100,86 +3874,116 @@ } static inline int -dasd_state_del_to_new (dasd_device_t **addr ) +dasd_state_del_to_new (dasd_device_t **addr, int devno) { + int i; dasd_device_t* device; - int rc = 0; - if (*addr == NULL) { /* allocate device descriptor on demand for new device */ - device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC); - if (device == NULL ) { - rc = -ENOMEM; - goto out; - } - memset (device, 0, sizeof (dasd_device_t)); + dasd_lowmem_t *lowmem; + int rc; + + + /* allocate device descriptor on demand for new device */ + if (*addr != NULL) { + BUG (); + } + + device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC); + if (device == NULL) { + return -ENOMEM; + } + + memset (device, 0, sizeof (dasd_device_t)); + dasd_plug_device (device); + device->accessible = 1; + INIT_LIST_HEAD (&device->lowmem_pool); + + /* allocate pages for lowmem pool */ + for (i = 0; i < DASD_LOWMEM_PAGES; i++) { + + lowmem = (void *) get_free_page (GFP_ATOMIC|GFP_DMA); + if (lowmem == NULL) { + break; + } + + MESSAGE (KERN_DEBUG, + " Add lowmem page :%p", + devno, lowmem); + list_add (&lowmem->list, &device->lowmem_pool); + } + + if (i < DASD_LOWMEM_PAGES) { + /* didn't get the needed lowmem pages */ + list_for_each_entry (lowmem, &device->lowmem_pool, list) { + MESSAGE (KERN_DEBUG, + " not enough memory - " + "Free page again :%p", + devno, lowmem); + free_page ((unsigned long) lowmem); + } + kfree (device); + rc = -ENOMEM; + } else { *addr = device; - device->lowmem_ccws = (void*)get_free_page (GFP_ATOMIC|GFP_DMA); - if (device->lowmem_ccws == NULL) { - rc = -ENOMEM; - goto noccw; - } -#ifdef CONFIG_ARCH_S390X - device->lowmem_idals = - device->lowmem_idal_ptr = (void*) get_free_page (GFP_ATOMIC|GFP_DMA); - if (device->lowmem_idals == NULL) { - rc = -ENOMEM; - goto noidal; - } -#endif -} - goto out; -#ifdef CONFIG_ARCH_S390X - noidal: - free_page ((long) device->lowmem_ccws); -#endif - noccw: - kfree(device); - out: + rc = 0; + } return rc; } static inline int -dasd_state_new_to_del (dasd_device_t **addr ) +dasd_state_new_to_del (dasd_device_t **addr, int devno) { + dasd_lowmem_t *lowmem; + dasd_device_t *device = *addr; + + /* free private area */ if (device && device->private) { kfree(device->private); - device->private = NULL; } -#ifdef CONFIG_ARCH_S390X - free_page ((long)(device->lowmem_idals)); -#endif - free_page((long)(device->lowmem_ccws)); + + /* free lowmem_pool */ + list_for_each_entry (lowmem, &device->lowmem_pool, list) { + MESSAGE (KERN_DEBUG, + " Free lowmem page :%p", + devno, lowmem); + free_page ((unsigned long) lowmem); + } + + /* free device */ kfree(device); *addr = NULL; return 0; } static inline int -dasd_state_new_to_known (dasd_device_t **dptr, int devno, dasd_discipline_t *disc) +dasd_state_new_to_known (dasd_device_t **dptr, + int devno, + dasd_discipline_t *discipline) { int rc = 0; umode_t devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR; struct list_head *l; - major_info_t *major_info = NULL; + major_info_t *major_info, *tmp; int i; dasd_device_t *device = *dptr; devfs_handle_t dir; char buffer[5]; - - list_for_each (l, &dasd_major_info[0].list) { - major_info = list_entry (l, major_info_t, list); + major_info = NULL; + list_for_each (l, &dasd_major_info) { + tmp = list_entry (l, major_info_t, list); for (i = 0; i < DASD_PER_MAJOR; i++) { - if (major_info->dasd_device[i] == device) { - device->kdev = MKDEV (major_info->gendisk.major, + if (tmp->dasd_device[i] == device) { + device->kdev = MKDEV (tmp->gendisk.major, i << DASD_PARTN_BITS); + major_info = tmp; break; } } - if (i < DASD_PER_MAJOR) /* we found one */ + if (major_info != NULL) /* we found one */ break; } - if ( major_info == NULL || major_info == &dasd_major_info[0] ) + if ( major_info == NULL ) BUG(); device->major_info = major_info; @@ -3192,18 +3996,24 @@ rc = get_dev_info_by_devno (devno, &device->devinfo); if ( rc ) { + /* returns -EUSERS if boxed !!*/ + if (rc == -EUSERS) { + device->level = DASD_STATE_BOXED; + } goto out; } - DASD_DRIVER_DEBUG_EVENT (5, dasd_state_new_to_known, - "got devinfo CU-type %04x and dev-type %04x", - device->devinfo.sid_data.cu_type, - device->devinfo.sid_data.dev_type); - + DBF_EVENT (DBF_NOTICE, + "got devinfo CU-type %04x and dev-type %04x", + device->devinfo.sid_data.cu_type, + device->devinfo.sid_data.dev_type); + if ( devno != device->devinfo.devno ) BUG(); - device->discipline = dasd_find_disc (device, disc); + + device->discipline = dasd_find_disc (device, + discipline); if ( device->discipline == NULL ) { rc = -ENODEV; goto out; @@ -3221,6 +4031,7 @@ MINOR(device->kdev), devfs_perm, &dasd_device_operations,NULL); + dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_ADD); device->level = DASD_STATE_KNOWN; out: return rc; @@ -3233,7 +4044,7 @@ /* don't reset to zeros because of persistent data durich detach/attach! */ devfs_unregister(device->devfs_entry); devfs_unregister(device->major_info->gendisk.de_arr[MINOR(device->kdev) >> DASD_PARTN_BITS]); - + dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_REMOVE); return rc; } @@ -3241,12 +4052,21 @@ dasd_state_known_to_accept (dasd_device_t *device) { int rc = 0; - device->debug_area = debug_register (device->name, 0, 2, - 3 * sizeof (long)); - debug_register_view (device->debug_area, &debug_sprintf_view); - debug_register_view (device->debug_area, &debug_hex_ascii_view); - DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area created", - device); + + /* register 'device' debug area, used for all DBF_DEV_XXX calls*/ + device->debug_area = debug_register (device->name, + 0, /* size of debug area */ + 2, /* number of areas */ + 8 * sizeof (long)); + + debug_register_view (device->debug_area, + &debug_sprintf_view); + + debug_set_level (device->debug_area, + DBF_ERR); + + DBF_DEV_EVENT (DBF_EMERG, device, "%s", + "debug area created"); if (device->discipline->int_handler) { rc = s390_request_irq_special (device->devinfo.irq, @@ -3255,7 +4075,10 @@ 0, DASD_NAME, &device->dev_status); if ( rc ) { - printk("No request IRQ\n"); + + MESSAGE (KERN_DEBUG, "%s", + "No request IRQ"); + goto out; } } @@ -3272,10 +4095,15 @@ if (device->discipline->int_handler) { free_irq (device->devinfo.irq, &device->dev_status); } - DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area deleted", - device); - if ( device->debug_area != NULL ) + + DBF_DEV_EVENT (DBF_EMERG, device, + "%p debug area deleted", + device); + + if (device->debug_area != NULL) { debug_unregister (device->debug_area); + device->debug_area = NULL; + } device->discipline = NULL; device->level = DASD_STATE_KNOWN; out: @@ -3297,18 +4125,17 @@ s390irq_spin_lock_irqsave (device->devinfo.irq, flags); rc = device->discipline->start_IO (device->init_cqr); + if ( ! rc ) + device->level = DASD_STATE_INIT; s390irq_spin_unlock_irqrestore(device->devinfo.irq, flags); - if ( rc ) - goto out; - device->level = DASD_STATE_INIT; } else { rc = -ENOMEM; } } else { rc = dasd_state_init_to_ready ( device ); } - out: + return rc; } @@ -3318,15 +4145,8 @@ int rc = 0; if (device->discipline->do_analysis != NULL) if ( device->discipline->do_analysis (device) == 0 ) - switch (device->sizes.bp_block) { - case 512: - case 1024: - case 2048: - case 4096: - break; - default: - rc = -EMEDIUMTYPE; - } + rc = dasd_check_bp_block (device); + if ( device->init_cqr ) { /* This pointer is no longer needed, BUT dont't free the */ /* memory, because this is done in bh for finished request!!!! */ @@ -3362,8 +4182,10 @@ dasd_state_ready_to_online (dasd_device_t *device ) { int rc = 0; - dasd_unplug_device (device); - device->level = DASD_STATE_ONLINE; + if (!(rc = dasd_check_bp_block (device))) { + dasd_unplug_device (device); + device->level = DASD_STATE_ONLINE; + } return rc; } @@ -3383,6 +4205,7 @@ int i; int major = MAJOR(device->kdev); int minor = MINOR(device->kdev); + request_queue_t *request_queue; for (i = 0; i < (1 << DASD_PARTN_BITS); i++) { if (i == 0) @@ -3399,11 +4222,16 @@ device->major_info->gendisk.part[minor+i].start_sect = 0; device->major_info->gendisk.part[minor+i].nr_sects = 0; } - device->request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL); - device->request_queue->queuedata = device; - blk_init_queue (device->request_queue, do_dasd_request); - blk_queue_headactive (device->request_queue, 0); - elevator_init (&(device->request_queue->elevator),ELEVATOR_NOOP); + + request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL); + if (request_queue) { + request_queue->queuedata = device; + blk_init_queue (request_queue, do_dasd_request); + blk_queue_headactive (request_queue, 1); + elevator_init (&(request_queue->elevator),ELEVATOR_NOOP); + } + device->request_queue = request_queue; + return rc; } @@ -3424,6 +4252,19 @@ int i; int major = MAJOR(device->kdev); int minor = MINOR(device->kdev); + request_queue_t *q = device->request_queue; + struct request *req; + long flags; + + spin_lock_irqsave(&io_request_lock, flags); + while (q && + !list_empty(&q->queue_head) && + (req = dasd_next_request(q)) != NULL) { + + dasd_end_request(req, 0); + dasd_dequeue_request(q, req); + } + spin_unlock_irqrestore(&io_request_lock, flags); for (i = 0; i < (1 << DASD_PARTN_BITS); i++) { destroy_buffers(MKDEV(major,minor+i)); @@ -3453,6 +4294,7 @@ 1 << DASD_PARTN_BITS, &dasd_device_operations, (device->sizes.blocks << device->sizes.s2b_shift)); + dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTCHK); } static inline void @@ -3467,14 +4309,7 @@ } devfs_register_partitions(&device->major_info->gendisk, MINOR(device->kdev),1); -} - -static inline void -dasd_resetup_partitions ( dasd_device_t * device ) -{ - BUG(); - dasd_destroy_partitions ( device ) ; - dasd_setup_partitions ( device ) ; + dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTREMOVE); } /* @@ -3505,11 +4340,11 @@ from_state = device->level; } - DASD_DRIVER_DEBUG_EVENT (3, dasd_set_device_level, - "devno %04x; from %i to %i", - devno, - from_state, - to_state); + DBF_EVENT (DBF_INFO, + "devno %04x; from %i to %i", + devno, + from_state, + to_state); if ( from_state == to_state ) goto out; @@ -3520,20 +4355,30 @@ /* First check for bringup */ if ( from_state <= DASD_STATE_DEL && to_state >= DASD_STATE_NEW ) { - rc = dasd_state_del_to_new(device_addr); + rc = dasd_state_del_to_new(device_addr, devno); if ( rc ) { goto bringup_fail; } device = *device_addr; } - if ( from_state <= DASD_STATE_NEW && + + /* reprobe boxed devices */ + if (device->level == DASD_STATE_BOXED) { + rc = s390_trigger_resense (device->devinfo.irq); + if ( rc ) { + goto bringup_fail; + } + } + + if ( device->level <= DASD_STATE_BOXED && to_state >= DASD_STATE_KNOWN ) { rc = dasd_state_new_to_known( device_addr, devno, discipline ); if ( rc ) { goto bringup_fail; } } - if ( from_state <= DASD_STATE_KNOWN && + + if ( device->level <= DASD_STATE_KNOWN && to_state >= DASD_STATE_ACCEPT ) { rc = dasd_state_known_to_accept(device); if ( rc ) { @@ -3543,19 +4388,21 @@ if ( dasd_probeonly ) { goto out; } - if ( from_state <= DASD_STATE_ACCEPT && + + if ( device->level <= DASD_STATE_ACCEPT && to_state >= DASD_STATE_INIT ) { rc = dasd_state_accept_to_init(device); if ( rc ) { goto bringup_fail; } } - if ( from_state <= DASD_STATE_INIT && + if ( device->level <= DASD_STATE_INIT && to_state >= DASD_STATE_READY ) { rc = -EAGAIN; goto out; } - if ( from_state <= DASD_STATE_READY && + + if ( device->level <= DASD_STATE_READY && to_state >= DASD_STATE_ONLINE ) { rc = dasd_state_ready_to_online(device); if ( rc ) { @@ -3564,50 +4411,57 @@ } goto out; bringup_fail: /* revert changes */ -#if 0 - printk (KERN_DEBUG PRINTK_HEADER - "failed to set device from state %d to %d at " - "level %d rc %d. Reverting...\n", - from_state, - to_state, - device->level, - rc); -#endif - to_state = from_state; - from_state = device->level; + + DBF_DEV_EVENT (DBF_ERR, device, + "failed to set device from state %d to %d at " + "level %d rc %d. Reverting...", + from_state, + to_state, + device->level, + rc); + + if (device->level <= DASD_STATE_NEW) { + /* Revert - device can not be accessed */ + to_state = from_state; + from_state = device->level; + } /* now do a shutdown */ shutdown: - if ( from_state >= DASD_STATE_ONLINE && + if ( device->level >= DASD_STATE_ONLINE && to_state <= DASD_STATE_READY ) if (dasd_state_online_to_ready(device)) BUG(); - if ( from_state >= DASD_STATE_READY && + if ( device->level >= DASD_STATE_READY && to_state <= DASD_STATE_ACCEPT ) if ( dasd_state_ready_to_accept(device)) BUG(); - if ( from_state >= DASD_STATE_ACCEPT && + if ( device->level >= DASD_STATE_ACCEPT && to_state <= DASD_STATE_KNOWN ) if ( dasd_state_accept_to_known(device)) BUG(); - if ( from_state >= DASD_STATE_KNOWN && + if ( device->level >= DASD_STATE_KNOWN && to_state <= DASD_STATE_NEW ) if ( dasd_state_known_to_new(device)) BUG(); - if ( from_state >= DASD_STATE_NEW && + if ( device->level >= DASD_STATE_NEW && to_state <= DASD_STATE_DEL) - if (dasd_state_new_to_del(device_addr)) + if (dasd_state_new_to_del(device_addr, devno)) BUG(); goto out; out: return rc; } -/* SECTION: Procfs stuff */ +/******************************************************************************** + * SECTION: Procfs stuff + ********************************************************************************/ +#ifdef CONFIG_PROC_FS + typedef struct { char *data; int len; @@ -3640,29 +4494,53 @@ int index = 0; MOD_INC_USE_COUNT; - spin_lock_irqsave(&discipline_lock,flags); + + spin_lock_irqsave(&discipline_lock, + flags); + info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t)); + if (info == NULL) { - printk (KERN_WARNING "No memory available for data\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory available for data (tempinfo)"); + + spin_unlock_irqrestore(&discipline_lock, + flags); + MOD_DEC_USE_COUNT; + return -ENOMEM; + } else { file->private_data = (void *) info; } - list_for_each (l, &dasd_major_info[0].list) { + list_for_each (l, &dasd_major_info) { size += 128 * 1 << (MINORBITS - DASD_PARTN_BITS); } info->data = (char *) vmalloc (size); - DASD_DRIVER_DEBUG_EVENT (1, dasd_devices_open, "area: %p, size 0x%x", - info->data, - size); + if (size && info->data == NULL) { - printk (KERN_WARNING "No memory available for data\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory available for data (info->data)"); + vfree (info); + + spin_unlock_irqrestore(&discipline_lock, + flags); + MOD_DEC_USE_COUNT; + return -ENOMEM; } - list_for_each (l, &dasd_major_info[0].list) { + + DBF_EVENT (DBF_NOTICE, + "procfs-area: %p, size 0x%x allocated", + info->data, + size); + + list_for_each (l, &dasd_major_info) { temp = list_entry (l, major_info_t, list); for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) { dasd_device_t *device; @@ -3673,8 +4551,8 @@ continue; features = dasd_features_from_devno(devno); - if (features < DASD_DEFAULT_FEATURES) - features = DASD_DEFAULT_FEATURES; + if (features < DASD_FEATURE_DEFAULT) + features = DASD_FEATURE_DEFAULT; device = temp->dasd_device[i]; if (device) { @@ -3702,45 +4580,45 @@ sprintf (info->data + len, "detected"); break; + case DASD_STATE_BOXED: + len += + sprintf (info->data + len, + "boxed"); + break; case DASD_STATE_ACCEPT: - len += sprintf (info->data + len,"accepted"); + len += sprintf (info->data + len, + "accepted"); break; case DASD_STATE_INIT: - len += - sprintf (info->data + len, - "busy "); + len += sprintf (info->data + len, + "busy "); break; case DASD_STATE_READY: + len += sprintf (info->data + len, + "ready "); + break; case DASD_STATE_ONLINE: - if ( atomic_read(&device->plugged) ) - len += - sprintf (info->data + len, - "fenced "); - else - len += - sprintf (info->data + len, - "active "); - if ( device->sizes.bp_block == 512 || - device->sizes.bp_block == 1024 || - device->sizes.bp_block == 2048 || - device->sizes.bp_block == 4096 ) - len += - sprintf (info->data + len, - "at blocksize: %d, %ld blocks, %ld MB", - device->sizes.bp_block, - device->sizes.blocks, - ((device-> - sizes.bp_block >> 9) * - device->sizes. - blocks) >> 11); - else - len += - sprintf (info->data + len, - "n/f "); + len += sprintf (info->data + len, + "active "); + + if (dasd_check_bp_block (device)) + len += + sprintf (info->data + len, + "n/f "); + else + len += + sprintf (info->data + len, + "at blocksize: %d, %ld blocks, %ld MB", + device->sizes.bp_block, + device->sizes.blocks, + ((device-> + sizes.bp_block >> 9) * + device->sizes. + blocks) >> 11); break; default: len += - sprintf (info->data + len, + sprintf (info->data + len, "no stat"); break; } @@ -3769,7 +4647,9 @@ index += 1 << (MINORBITS - DASD_PARTN_BITS); } info->len = len; - spin_unlock_irqrestore(&discipline_lock,flags); + + spin_unlock_irqrestore(&discipline_lock, + flags); return rc; } @@ -3793,22 +4673,250 @@ } } +/* + * scan for device range in given string (e.g. 0x0150-0x0155). + * devnos are always hex and leading 0x are ignored. + */ +static char * +dasd_parse_range (char *buffer, dasd_range_t *range) +{ + char *str; + + /* remove optional 'device ' and 'range=' and search for nexet digit */ + for (str = buffer + 4; isspace(*str); str++); + + if (strncmp (str, "device ", 7) == 0) + for (str = str + 7; isspace(*str); str++); + + if (strncmp (str, "range=", 6) == 0) + for (str = str + 6; isspace(*str); str++); + + range->to = range->from = dasd_strtoul (str, + &str, + &(range->features)); + + if (*str == '-') { + str++; + range->to = dasd_strtoul (str, + &str, + &(range->features)); + } + + /* remove blanks after device range */ + for (; isspace(*str); str++); + + if (range->from < 0 || range->to < 0) { + MESSAGE (KERN_WARNING, + "/proc/dasd/devices: range parse error in '%s'", + buffer); + return ERR_PTR (-EINVAL); + } + + return str; + +} /* end dasd_parse_range */ + +/* + * Enable / Disable the given devices + */ +static void +dasd_proc_set (char *buffer) +{ + dasd_range_t range; + char *str; + + str = dasd_parse_range (buffer, + &range); + + /* Negative numbers in str/from/to indicate errors */ + if (IS_ERR (str) || (range.from < 0) || (range.to < 0) + || (range.from > 65535) || (range.to > 65535)) + return; + + if (strncmp (str, "on", 2) == 0) { + dasd_enable_ranges (&range, NULL, 0); + + } else if (strncmp (str, "off", 3) == 0) { + dasd_disable_ranges (&range, NULL, 0, 1); + + } else { + MESSAGE (KERN_WARNING, + "/proc/dasd/devices: " + "only 'on' and 'off' are alowed in 'set' " + "command ('%s'/'%s')", + buffer, + str); + } + + return; + +} /* end dasd_proc_set */ + +/* + * Add the given devices + */ +static void +dasd_proc_add (char *buffer) +{ + dasd_range_t range; + char *str; + + str = dasd_parse_range (buffer, + &range); + + /* Negative numbers in str/from/to indicate errors */ + if (IS_ERR (str) || (range.from < 0) || (range.to < 0) + || (range.from > 65535) || (range.to > 65535)) + return; + + dasd_add_range (range.from, range.to, range.features); + dasd_enable_ranges (&range, NULL, 0); + + return; + +} /* end dasd_proc_add */ + +/* + * Break the lock of a given 'boxed' dasd. + * If the dasd in not in status 'boxed' just return. + */ +static int +dasd_break_boxed (dasd_range_t *range, + dasd_device_t *device) +{ + int rc = 0; + dasd_discipline_t *discipline; + struct list_head *lh = dasd_disc_head.next; + + /* check devixe status */ + if (device->level != DASD_STATE_BOXED) { + MESSAGE (KERN_WARNING, + "/proc/dasd/devices: the given device (%04X) " + "is not 'boxed')", + device->devinfo.devno); + rc = -EINVAL; + goto out; + } + + /* force eckd discipline */ + do { + discipline = list_entry(lh, + dasd_discipline_t, + list); + + if (strncmp (discipline->name, range->discipline, 4) == 0) + break; /* discipline found */ + + lh = lh->next; /* check next discipline in list */ + if (lh == &dasd_disc_head) { + discipline = NULL; + break; + } + } while ( 1 ); + device->discipline = discipline; + + if (device->discipline == NULL) { + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/devices: discipline not found " + "in discipline list"); + rc = -EINVAL; + goto out; + } + + /* register the int handler to enable IO */ + rc = s390_request_irq_special (device->devinfo.irq, + device->discipline->int_handler, + dasd_not_oper_handler, + 0, + DASD_NAME, + &device->dev_status); + if ( rc ) + goto out; + + rc = dasd_steal_lock (device); + + /* unregister the int handler to enable re-sensing */ + free_irq (device->devinfo.irq, + &device->dev_status); + + device->discipline = NULL; + device->level = DASD_STATE_NEW; + + out: + return rc; + +} /* end dasd_break_boxed */ + +/* + * Handle the procfs call 'brk . + */ +static void +dasd_proc_brk (char *buffer) +{ + char *str; + dasd_range_t range; + dasd_device_t *device; + int rc = 0; + + str = dasd_parse_range (buffer, + &range); + + if (IS_ERR (str)) + return; + + if (range.from != range.to) { + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/devices: 'brk " + "is only allowed for a single device (no ranges)"); + return; + } + + /* check for discipline = 'eckd' */ + if (strncmp(str, "eckd", 4) != 0) { + MESSAGE (KERN_WARNING, + "/proc/dasd/devices: 'brk " + "is only allowed for 'eckd' (%s)", + str); + return; + } + + memcpy (range.discipline, "ECKD", 4); + + device = *(dasd_device_from_devno (range.from)); + if (device == NULL) { + MESSAGE (KERN_WARNING, + "/proc/dasd/devices: no device found for devno (%04X)", + range.from); + return; + } + + rc = dasd_break_boxed (&range, + device); + if (rc == 0) { + /* trigger CIO to resense the device */ + s390_trigger_resense (device->devinfo.irq); + + // get the device online now + dasd_enable_ranges (&range, + NULL, + 0); + } + +} /* end dasd_proc_brk */ + static ssize_t dasd_devices_write (struct file *file, const char *user_buf, size_t user_len, loff_t * offset) { char *buffer; - int off = 0; - char *temp; - dasd_range_t range; - int features; if (user_len > PAGE_SIZE) return -EINVAL; - + buffer = vmalloc (user_len+1); if (buffer == NULL) return -ENOMEM; + if (copy_from_user (buffer, user_buf, user_len)) { vfree (buffer); return -EFAULT; @@ -3821,60 +4929,29 @@ buffer[user_len] = '\0'; } - printk (KERN_INFO PRINTK_HEADER "/proc/dasd/devices: '%s'\n", buffer); - if (strncmp (buffer, "set ", 4) && strncmp (buffer, "add ", 4)) { - printk (KERN_WARNING PRINTK_HEADER - "/proc/dasd/devices: only 'set' and 'add' are supported verbs\n"); - return -EINVAL; - } - off += 4; - while (buffer[off] && !isalnum (buffer[off])) - off++; - if (!strncmp (buffer + off, "device", strlen ("device"))) { - off += strlen ("device"); - while (buffer[off] && !isalnum (buffer[off])) - off++; - } - if (!strncmp (buffer + off, "range=", strlen ("range="))) { - off += strlen ("range="); - while (buffer[off] && !isalnum (buffer[off])) - off++; - } - - temp = buffer + off; - range.from = dasd_strtoul (temp, &temp, &features); - range.to = range.from; - - if (*temp == '-') { - temp++; - range.to = dasd_strtoul (temp, &temp, &features); - } + MESSAGE (KERN_INFO, + "/proc/dasd/devices: '%s'", + buffer); + + if (strncmp (buffer, "set ", 4) == 0) { + /* handle 'set on/off' */ + dasd_proc_set (buffer); + + } else if (strncmp (buffer, "add ", 4) == 0) { + /* handle 'add ' */ + dasd_proc_add (buffer); + + } else if (strncmp (buffer, "brk ", 4) == 0) { + /* handle 'brk ' */ + dasd_proc_brk (buffer); - if (range.from == -EINVAL || - range.to == -EINVAL ) { - - printk (KERN_WARNING PRINTK_HEADER - "/proc/dasd/devices: range parse error in '%s'\n", - buffer); } else { - off = (long) temp - (long) buffer; - if (!strncmp (buffer, "add", strlen ("add"))) { - dasd_add_range (range.from, range.to, features); - dasd_enable_ranges (&range, NULL, 0); - } else { - while (buffer[off] && !isalnum (buffer[off])) - off++; - if (!strncmp (buffer + off, "on", strlen ("on"))) { - dasd_enable_ranges (&range, NULL, 0); - } else if (!strncmp (buffer + off, "off", strlen ("off"))) { - dasd_disable_ranges (&range, NULL, 0, 1); - } else { - printk (KERN_WARNING PRINTK_HEADER - "/proc/dasd/devices: parse error in '%s'\n", - buffer); - } - } - } + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/devices: only 'set' ,'add' and " + "'brk' are supported verbs"); + vfree (buffer); + return -EINVAL; + } vfree (buffer); return user_len; @@ -3895,9 +4972,9 @@ } static struct file_operations dasd_devices_file_ops = { - read:dasd_generic_read, /* read */ + read:dasd_generic_read, /* read */ write:dasd_devices_write, /* write */ - open:dasd_devices_open, /* open */ + open:dasd_devices_open, /* open */ release:dasd_devices_close, /* close */ }; @@ -3914,22 +4991,41 @@ MOD_INC_USE_COUNT; info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t)); + if (info == NULL) { - printk (KERN_WARNING "No memory available for data\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory available for data (tempinfo)"); + MOD_DEC_USE_COUNT; return -ENOMEM; } else { file->private_data = (void *) info; } - info->data = (char *) vmalloc (PAGE_SIZE); /* FIXME! determine space needed in a better way */ + /* FIXME! determine space needed in a better way */ + info->data = (char *) vmalloc (PAGE_SIZE); + if (info->data == NULL) { - printk (KERN_WARNING "No memory available for data\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory available for data (info->data)"); + vfree (info); file->private_data = NULL; MOD_DEC_USE_COUNT; return -ENOMEM; } + /* check for active profiling */ + if (dasd_profile_level == DASD_PROFILE_OFF) { + + info->len = sprintf (info->data, + "Statistics are off - they might be " + "switched on using 'echo set on > " + "/proc/dasd/statistics'\n"); + return rc; + } + /* prevent couter 'ouverflow' on output */ for (shift = 0, help = dasd_global_profile.dasd_io_reqs; help > 9999999; help = help >> 1, shift++) ; @@ -3958,7 +5054,15 @@ } len += sprintf (info->data + len, "\n"); - len += sprintf (info->data + len, "Histogram of I/O times (microseconds)\n"); + for (; i < 32; i++) { + len += sprintf (info->data + len, "%7d ", + dasd_global_profile.dasd_io_secs[i] >> shift); + } + len += sprintf (info->data + len, "\n"); + + len += sprintf (info->data + len, + "Histogram of I/O times (microseconds)\n"); + for (i = 0; i < 16; i++) { len += sprintf (info->data + len, "%7d ", dasd_global_profile.dasd_io_times[i] >> shift); @@ -4008,7 +5112,9 @@ len += sprintf (info->data + len, "\n"); len += sprintf (info->data + len, - "Histogram of I/O time between ssch and irq per sector\n"); + "Histogram of I/O time between ssch and irq per " + "sector\n"); + for (i = 0; i < 16; i++) { len += sprintf (info->data + len, "%7d ", dasd_global_profile.dasd_io_time2ps[i] >> shift); @@ -4064,23 +5170,89 @@ if (buffer == NULL) return -ENOMEM; + if (copy_from_user (buffer, user_buf, user_len)) { vfree (buffer); return -EFAULT; } + buffer[user_len] = 0; - printk (KERN_INFO PRINTK_HEADER "/proc/dasd/statictics: '%s'\n", - buffer); - if (strncmp (buffer, "reset", 4)) { - memset (&dasd_global_profile, 0, sizeof (dasd_profile_info_t)); + + MESSAGE (KERN_INFO, + "/proc/dasd/statictics: '%s'", + buffer); + +#ifdef DASD_PROFILE + /* check for valid verbs */ + if (strncmp (buffer, "reset", 5) && + strncmp (buffer, "set ", 4) ) { + + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/statistics: only 'set' and " + "'reset' are supported verbs"); + + return -EINVAL; } + + if (!strncmp (buffer, "reset", 5)) { + + /* reset the statistics */ + memset (&dasd_global_profile, + 0, + sizeof (dasd_profile_info_t)); + + MESSAGE (KERN_INFO, "%s", + "Statictics reset"); + + } else { + + /* 'set xxx' was given */ + int offset = 4; + + while (buffer[offset] && !isalnum (buffer[offset])) + offset++; + + if (!strncmp (buffer + offset, "on", 2)) { + + /* switch on statistics profiling */ + dasd_profile_level = DASD_PROFILE_ON; + + MESSAGE (KERN_INFO, "%s", + "Statictics switched on"); + + } else if (!strncmp (buffer + offset, "off", 3)) { + + /* switch off and reset statistics profiling */ + memset (&dasd_global_profile, + 0, + sizeof (dasd_profile_info_t)); + + dasd_profile_level = DASD_PROFILE_OFF; + + MESSAGE (KERN_INFO, "%s", + "Statictics switched off"); + + } else { + + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/statistics: only 'set on' and " + "'set off' are supported verbs"); + } + } +#else + MESSAGE (KERN_WARNING, "%s", + "/proc/dasd/statistics: is not activated in this " + "kernel"); + + +#endif /* DASD_PROFILE */ return user_len; } static struct file_operations dasd_statistics_file_ops = { - read:dasd_generic_read, /* read */ - open:dasd_statistics_open, /* open */ + read:dasd_generic_read, /* read */ write:dasd_statistics_write, /* write */ + open:dasd_statistics_open, /* open */ release:dasd_devices_close, /* close */ }; @@ -4113,6 +5285,11 @@ remove_proc_entry ("dasd", &proc_root); } +#endif /* CONFIG_PROC_FS */ + +/******************************************************************************** + * SECTION: Initializing the driver + ********************************************************************************/ int dasd_request_module ( void *name ) { int rc = -ERESTARTSYS; @@ -4124,16 +5301,17 @@ } while ( (rc=request_module(name)) != 0 ) { DECLARE_WAIT_QUEUE_HEAD(wait); - printk ( KERN_INFO "request_module returned %d for %s\n", + + MESSAGE (KERN_INFO, + "request_module returned %d for %s", rc, (char*)name); + sleep_on_timeout(&wait,5* HZ); /* wait in steps of 5 seconds */ } return rc; } - -/* SECTION: Initializing the driver */ int __init dasd_init (void) { @@ -4142,40 +5320,56 @@ major_info_t *major_info = NULL; struct list_head *l; - printk (KERN_INFO PRINTK_HEADER "initializing...\n"); - dasd_debug_area = debug_register (DASD_NAME, 0, 2, 5 * sizeof (long)); - debug_register_view (dasd_debug_area, &debug_sprintf_view); - debug_register_view (dasd_debug_area, &debug_hex_ascii_view); + MESSAGE (KERN_INFO, "%s", + "initializing..."); init_waitqueue_head (&dasd_init_waitq); + /* register 'common' DASD debug area, used faor all DBF_XXX calls*/ + dasd_debug_area = debug_register (DASD_NAME, + 0, /* size of debug area */ + 2, /* number of areas */ + 8 * sizeof (long)); + + debug_register_view (dasd_debug_area, + &debug_sprintf_view); + if (dasd_debug_area == NULL) { goto failed; } - DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", - "ENTRY"); - dasd_devfs_handle = devfs_mk_dir (NULL, DASD_NAME, NULL); + + debug_set_level (dasd_debug_area, + DBF_ERR); + + DBF_EVENT (DBF_EMERG, "%s", + "debug area created"); + + dasd_devfs_handle = devfs_mk_dir (NULL, + DASD_NAME, + NULL); + if (dasd_devfs_handle < 0) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", - "no devfs"); + + DBF_EVENT (DBF_ALERT, "%s", + "no devfs"); goto failed; } - list_for_each (l, &dasd_major_info[0].list) { + + list_add_tail(&dasd_major_static.list, &dasd_major_info); + list_for_each (l, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); if ((rc = dasd_register_major (major_info)) > 0) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "major %d: success", - major_info->gendisk.major); - printk (KERN_INFO PRINTK_HEADER - "Registered successfully to major no %u\n", - major_info->gendisk.major); + + MESSAGE (KERN_INFO, + "Registered successfully to major no %u", + major_info->gendisk.major); } else { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "major %d: failed", - major_info->gendisk.major); - printk (KERN_WARNING PRINTK_HEADER - "Couldn't register successfully to major no %d\n", - major_info->gendisk.major); + + MESSAGE (KERN_WARNING, + "Couldn't register successfully to " + "major no %d", + major_info->gendisk.major); + /* revert registration of major infos */ goto failed; } @@ -4185,18 +5379,24 @@ #endif /* ! MODULE */ rc = dasd_parse (dasd); if (rc) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", - "invalid range found"); + + DBF_EVENT (DBF_ALERT, "%s", + "invalid range found"); goto failed; } +#ifdef CONFIG_PROC_FS rc = dasd_proc_init (); if (rc) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", "no proc-FS"); + + DBF_EVENT (DBF_ALERT, "%s", + "no proc-FS"); goto failed; } +#endif /* CONFIG_PROC_FS */ + genhd_dasd_name = dasd_device_name; - genhd_dasd_ioctl = dasd_ioctl; + genhd_dasd_ioctl = dasd_ioctl; if (dasd_autodetect) { /* update device range to all devices */ for (irq = get_irq_first (); irq != -ENODEV; @@ -4204,10 +5404,14 @@ int devno = get_devno_by_irq (irq); int index = dasd_devindex_from_devno (devno); if (index == -ENODEV) { /* not included in ranges */ - DASD_DRIVER_DEBUG_EVENT (2, dasd_init, - "add %04x to range", - devno); - dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES); + + DBF_EVENT (DBF_CRIT, + "add %04x to range", + devno); + + dasd_add_range (devno, + devno, + DASD_FEATURE_DEFAULT); } } } @@ -4216,15 +5420,15 @@ #ifdef CONFIG_DASD_DIAG rc = dasd_diag_init (); if (rc == 0) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "DIAG discipline %s", - "success"); - printk (KERN_INFO PRINTK_HEADER - "Registered DIAG discipline successfully\n"); + + MESSAGE (KERN_INFO, "%s", + "Registered DIAG discipline successfully"); + } else { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "DIAG discipline %s", - "failed"); + + DBF_EVENT (DBF_ALERT, "%s", + "Register DIAG discipline failed"); + goto failed; } #endif /* CONFIG_DASD_DIAG */ @@ -4235,13 +5439,14 @@ #ifdef CONFIG_DASD_ECKD rc = dasd_eckd_init (); if (rc == 0) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "ECKD discipline %s", "success"); - printk (KERN_INFO PRINTK_HEADER - "Registered ECKD discipline successfully\n"); + + MESSAGE (KERN_INFO, "%s", + "Registered ECKD discipline successfully"); } else { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "ECKD discipline %s", "failed"); + + DBF_EVENT (DBF_ALERT, "%s", + "Register ECKD discipline failed"); + goto failed; } #endif /* CONFIG_DASD_ECKD */ @@ -4251,14 +5456,14 @@ #ifdef CONFIG_DASD_FBA rc = dasd_fba_init (); if (rc == 0) { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "FBA discipline %s", "success"); - - printk (KERN_INFO PRINTK_HEADER - "Registered FBA discipline successfully\n"); + + MESSAGE (KERN_INFO, "%s", + "Registered FBA discipline successfully"); } else { - DASD_DRIVER_DEBUG_EVENT (1, dasd_init, - "FBA discipline %s", "failed"); + + DBF_EVENT (DBF_ALERT, "%s", + "Register FBA discipline failed"); + goto failed; } #endif /* CONFIG_DASD_FBA */ @@ -4276,12 +5481,16 @@ rc = 0; goto out; failed: - printk (KERN_INFO PRINTK_HEADER - "initialization not performed due to errors\n"); + + MESSAGE (KERN_INFO, "%s", + "initialization not performed due to errors"); + cleanup_dasd (); out: - DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", "LEAVE"); - printk (KERN_INFO PRINTK_HEADER "initialization finished\n"); + + MESSAGE (KERN_INFO, "%s", + "initialization finished"); + return rc; } @@ -4293,57 +5502,60 @@ struct list_head *l,*n; dasd_range_t *range; - printk (KERN_INFO PRINTK_HEADER "shutting down\n"); - DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","ENTRY"); - dasd_disable_ranges (&dasd_range_head, NULL, 1, 1); - if (MACHINE_IS_VM) { + MESSAGE (KERN_INFO, "%s", + "shutting down"); + + dasd_disable_ranges (&dasd_range_head, + NULL, 1, 1); + #ifdef CONFIG_DASD_DIAG + if (MACHINE_IS_VM) { dasd_diag_cleanup (); - DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd", - "DIAG discipline %s", "success"); - printk (KERN_INFO PRINTK_HEADER - "De-Registered DIAG discipline successfully\n"); -#endif /* CONFIG_DASD_ECKD_BUILTIN */ + + MESSAGE (KERN_INFO, "%s", + "De-Registered DIAG discipline successfully"); } +#endif /* CONFIG_DASD_DIAG */ + #ifdef CONFIG_DASD_FBA dasd_fba_cleanup (); - DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd", - "FBA discipline %s", "success"); - printk (KERN_INFO PRINTK_HEADER - "De-Registered FBA discipline successfully\n"); -#endif /* CONFIG_DASD_ECKD_BUILTIN */ + + MESSAGE (KERN_INFO, "%s", + "De-Registered FBA discipline successfully"); +#endif /* CONFIG_DASD_FBA */ + #ifdef CONFIG_DASD_ECKD dasd_eckd_cleanup (); - DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd", - "ECKD discipline %s", "success"); - printk (KERN_INFO PRINTK_HEADER - "De-Registered ECKD discipline successfully\n"); -#endif /* CONFIG_DASD_ECKD_BUILTIN */ - genhd_dasd_name = NULL; - genhd_dasd_ioctl = NULL; + MESSAGE (KERN_INFO, "%s", + "De-Registered ECKD discipline successfully"); +#endif /* CONFIG_DASD_ECKD */ + + genhd_dasd_name = NULL; + genhd_dasd_ioctl = NULL; + +#ifdef CONFIG_PROC_FS dasd_proc_cleanup (); +#endif /* CONFIG_PROC_FS */ - list_for_each_safe (l, n, &dasd_major_info[0].list) { + list_for_each_safe (l, n, &dasd_major_info) { major_info = list_entry (l, major_info_t, list); for (i = 0; i < DASD_PER_MAJOR; i++) { kfree (major_info->dasd_device[i]); } if ((major_info->flags & DASD_MAJOR_INFO_REGISTERED) && (rc = dasd_unregister_major (major_info)) == 0) { - DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd", - "major %d: success", - major_info->gendisk.major); - printk (KERN_INFO PRINTK_HEADER - "Unregistered successfully from major no %u\n", + + MESSAGE (KERN_INFO, + "Unregistered successfully from major no %u", major_info->gendisk.major); } else { - DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd", - "major %d: failed", - major_info->gendisk.major); - printk (KERN_WARNING PRINTK_HEADER - "Couldn't unregister successfully from major no %d rc = %d\n", - major_info->gendisk.major, rc); + + MESSAGE (KERN_WARNING, + "Couldn't unregister successfully from major " + "no %d rc = %d", + major_info->gendisk.major, + rc); } } list_for_each_safe (l, n, &dasd_range_head.list) { @@ -4360,10 +5572,14 @@ #endif /* MODULE */ if (dasd_devfs_handle) devfs_unregister(dasd_devfs_handle); - if (dasd_debug_area != NULL ) + + if (dasd_debug_area != NULL ) { debug_unregister(dasd_debug_area); - printk (KERN_INFO PRINTK_HEADER "shutdown completed\n"); - DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","LEAVE"); + dasd_debug_area = NULL; + } + + MESSAGE (KERN_INFO, "%s", + "shutdown completed"); } #ifdef MODULE diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_diag.c linux.21rc1-ac2/drivers/s390/block/dasd_diag.c --- linux.21rc1/drivers/s390/block/dasd_diag.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_diag.c 2003-04-25 13:49:34.000000000 +0100 @@ -5,13 +5,16 @@ * ...............: by Hartmunt Penner * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - + * + * $Revision: 1.47 $ + * * History of changes * 07/13/00 Added fixup sections for diagnoses ans saved some registers * 07/14/00 fixed constraints in newly generated inline asm * 10/05/00 adapted to 'new' DASD driver * fixed return codes of dia250() * fixed partition handling and HDIO_GETGEO + * 10/01/02 fixed Bugzilla 1341 */ #include @@ -20,7 +23,7 @@ #include #include -#include /* HDIO_GETGEO */ +#include #include #include #include @@ -29,6 +32,7 @@ #include #include #include +#include #include "dasd_int.h" #include "dasd_diag.h" @@ -38,62 +42,50 @@ #endif /* PRINTK_HEADER */ #define PRINTK_HEADER DASD_NAME"(diag):" +#ifdef MODULE +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12)) +MODULE_LICENSE("GPL"); +#endif +#endif + dasd_discipline_t dasd_diag_discipline; typedef struct dasd_diag_private_t { - dasd_diag_characteristics_t rdc_data; + diag210_t rdc_data; diag_rw_io_t iob; diag_init_io_t iib; unsigned long *label; } dasd_diag_private_t; static __inline__ int -dia210 (void *devchar) -{ - int rc; - - __asm__ __volatile__ (" diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - ".section .fixup,\"ax\"\n" - "2: lhi %0,3\n" - " bras 1,3f\n" - " .long 1b\n" - "3: l 1,0(1)\n" - " br 1\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,2b\n" ".previous\n":"=d" (rc) - :"d" ((void *) __pa (devchar)) - :"1"); - return rc; -} - -static __inline__ int dia250 (void *iob, int cmd) { - __asm__ __volatile__ (" lr 0,%1\n" - " diag 0,%0,0x250\n" + unsigned long addr; + int rc; + + addr = __pa(iob); + __asm__ __volatile__ (" lhi %0,11\n" + " lr 0,%2\n" + " diag 0,%1,0x250\n" "0: ipm %0\n" " srl %0,28\n" " or %0,1\n" "1:\n" - ".section .fixup,\"ax\"\n" - "2: lhi %0,3\n" - " bras 1,3f\n" - " .long 1b\n" - "3: l 1,0(1)\n" - " br 1\n" - ".previous\n" +#ifndef CONFIG_ARCH_S390X ".section __ex_table,\"a\"\n" " .align 4\n" - " .long 0b,2b\n" ".previous\n":"+d" (cmd) - :"d" ((void *) __pa (iob)) - :"0", "1", "cc"); - return cmd; + " .long 0b,1b\n" + ".previous\n" +#else + ".section __ex_table,\"a\"\n" + " .align 8\n" + " .quad 0b,1b\n" + ".previous\n" +#endif + : "+&d" (rc) + : "d" (cmd), "d" (addr) : "0", "1", "cc"); + return rc; } static __inline__ int @@ -144,13 +136,18 @@ iob->key = 0; iob->flags = 2; iob->block_count = cqr->cplength >> 1; - iob->interrupt_params = (u32) cqr; + iob->interrupt_params = (u32)(addr_t) cqr; iob->bio_list = __pa (cqr->cpaddr); - asm volatile ("STCK %0":"=m" (cqr->startclk)); + cqr->startclk = get_clock (); + rc = dia250 (iob, RW_BIO); if (rc > 8) { - PRINT_WARN ("dia250 returned CC %d\n", rc); + + MESSAGE (KERN_WARNING, + "dia250 returned CC %d", + rc); + check_then_set (&cqr->status, CQR_STATUS_QUEUED, CQR_STATUS_ERROR); } else if (rc == 0) { @@ -176,41 +173,56 @@ int done_fast_io = 0; int devno; unsigned long flags; + int subcode; + /* + * Get the external interruption subcode. VM stores + * this in the 'cpu address' field associated with + * the external interrupt. For diag 250 the subcode + * needs to be 3. + */ + subcode = S390_lowcore.cpu_addr; + if ((subcode & 0xff00) != 0x0300) + return; irq_enter(cpu, -1); if (!ip) { /* no intparm: unsolicited interrupt */ - printk (KERN_WARNING PRINTK_HEADER - "caught unsolicited interrupt\n"); + + MESSAGE (KERN_DEBUG, "%s", + "caught unsolicited interrupt"); + irq_exit(cpu, -1); return; } if (ip & 0x80000001) { - printk (KERN_WARNING PRINTK_HEADER - "caught spurious interrupt with parm %08x\n", ip); + + MESSAGE (KERN_DEBUG, + "caught spurious interrupt with parm %08x", + ip); + irq_exit(cpu, -1); return; } - cqr = (ccw_req_t *) ip; + cqr = (ccw_req_t *)(addr_t) ip; device = (dasd_device_t *) cqr->device; devno = device->devinfo.devno; + if (device == NULL) { - printk (KERN_WARNING PRINTK_HEADER - " INT on devno 0x%04X = /dev/%s (%d:%d)" - " belongs to NULL device\n", - devno, device->name, MAJOR (device->kdev), - MINOR (device->kdev)); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + " belongs to NULL device"); } + if (strncmp (device->discipline->ebcname, (char *) &cqr->magic, 4)) { - printk (KERN_WARNING PRINTK_HEADER - "0x%04X : /dev/%s (%d:%d)" - " magic number of ccw_req_t 0x%08X doesn't match" - " discipline 0x%08X\n", - devno, device->name, - MAJOR (device->kdev), MINOR (device->kdev), - cqr->magic, *(int *) (&device->discipline->name)); + + DEV_MESSAGE (KERN_WARNING, device, + " magic number of ccw_req_t 0x%08X doesn't match" + " discipline 0x%08X", + cqr->magic, + *(int *) (&device->discipline->name)); + irq_exit(cpu, -1); return; } @@ -219,7 +231,7 @@ s390irq_spin_lock_irqsave (device->devinfo.irq, flags); - asm volatile ("STCK %0":"=m" (cqr->stopclk)); + cqr->stopclk = get_clock (); switch (status) { case 0x00: @@ -254,52 +266,60 @@ { int rc = 0; int bsize; - int label_block; dasd_diag_private_t *private; - dasd_diag_characteristics_t *rdc_data; + diag210_t *rdc_data; ccw_req_t *cqr; long *label; int sb; if (device == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "Null device pointer passed to characteristics checker\n"); + + MESSAGE (KERN_WARNING, "%s", + "Null device pointer passed to characteristics " + "checker"); + return -ENODEV; } device->private = kmalloc (sizeof (dasd_diag_private_t), GFP_KERNEL); + if (device->private == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "memory allocation failed for private data\n"); + + MESSAGE (KERN_WARNING, "%s", + "memory allocation failed for private data"); + rc = -ENOMEM; goto fail; } private = (dasd_diag_private_t *) device->private; rdc_data = (void *) &(private->rdc_data); - rdc_data->dev_nr = device->devinfo.devno; - rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t); + rdc_data->vrdcdvno = device->devinfo.devno; + rdc_data->vrdclen = sizeof (diag210_t); - rc = dia210 (rdc_data); + rc = diag210 (rdc_data); if ( rc != 0) { goto fail; } - if (rdc_data->vdev_class != DEV_CLASS_FBA && - rdc_data->vdev_class != DEV_CLASS_ECKD && - rdc_data->vdev_class != DEV_CLASS_CKD) { + if (rdc_data->vrdcvcla != DEV_CLASS_FBA && + rdc_data->vrdcvcla != DEV_CLASS_ECKD && + rdc_data->vrdcvcla != DEV_CLASS_CKD) { rc = -ENOTSUPP; goto fail; } -#if 0 - printk (KERN_INFO PRINTK_HEADER - "%04X: %04X on real %04X/%02X\n", - rdc_data->dev_nr, - rdc_data->vdev_type, rdc_data->rdev_type, rdc_data->rdev_model); -#endif + + DBF_EVENT (DBF_INFO, + "%04X: %04X on real %04X/%02X", + rdc_data->vrdcdvno, + rdc_data->vrdcvtyp, + rdc_data->vrdccrty, + rdc_data->vrdccrmd); + + /* Figure out position of label block */ - if (private->rdc_data.vdev_class == DEV_CLASS_FBA) { + if (private->rdc_data.vrdcvcla == DEV_CLASS_FBA) { device->sizes.pt_block = 1; - } else if (private->rdc_data.vdev_class == DEV_CLASS_ECKD || - private->rdc_data.vdev_class == DEV_CLASS_CKD) { + } else if (private->rdc_data.vrdcvcla == DEV_CLASS_ECKD || + private->rdc_data.vrdcvcla == DEV_CLASS_CKD) { device->sizes.pt_block = 2; } else { BUG(); @@ -319,9 +339,12 @@ cqr = dasd_alloc_request (dasd_diag_discipline.name, sizeof (diag_bio_t) / sizeof (ccw1_t), 0, device); + if (cqr == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + free_page ((long) private->label); rc = -ENOMEM; goto fail; @@ -334,11 +357,12 @@ cqr->device = device; cqr->status = CQR_STATUS_FILLED; memset (iob, 0, sizeof (diag_rw_io_t)); - iob->dev_nr = rdc_data->dev_nr; + iob->dev_nr = rdc_data->vrdcdvno; iob->block_count = 1; - iob->interrupt_params = (u32) cqr; + iob->interrupt_params = (u32)(addr_t) cqr; iob->bio_list = __pa (bio); rc = dia250 (iob, RW_BIO); + dasd_free_request(cqr, device); if (rc == 0) { if (label[3] != bsize || label[0] != 0xc3d4e2f1 || /* != CMS1 */ @@ -357,13 +381,16 @@ device->sizes.blocks = label[7]; device->sizes.bp_block = bsize; device->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */ + for (sb = 512; sb < bsize; sb = sb << 1) + device->sizes.s2b_shift++; - printk (KERN_INFO PRINTK_HEADER - "/dev/%s (%04X): capacity (%dkB blks): %ldkB\n", - device->name, device->devinfo.devno, - (device->sizes.bp_block >> 10), - (device->sizes.blocks << device->sizes.s2b_shift) >> 1); + + DEV_MESSAGE (KERN_INFO, device, + "capacity (%dkB blks): %ldkB", + (device->sizes.bp_block >> 10), + (device->sizes.blocks << device->sizes.s2b_shift) >> 1); + free_page ((long) private->label); rc = 0; goto out; @@ -380,12 +407,9 @@ dasd_diag_fill_geometry (struct dasd_device_t *device, struct hd_geometry *geo) { int rc = 0; - dasd_diag_private_t *private = (dasd_diag_private_t *) device->private; unsigned long sectors = device->sizes.blocks << device->sizes.s2b_shift; unsigned long tracks = sectors >> 6; - unsigned long trk_rem = sectors & ((1 << 6) - 1); /* 64k per track */ unsigned long cyls = tracks >> 4; - unsigned long cyls_rem = tracks & ((1 << 4) - 1); /* 16 head per cyl */ switch (device->sizes.bp_block) { case 512: @@ -420,14 +444,19 @@ diag_bio_t *bio; int bhct; long size; + unsigned long reloc_sector = req->sector + + device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect; if (!noblk) { - PRINT_ERR ("No blocks to read/write...returning\n"); - return NULL; + + MESSAGE (KERN_ERR, "%s", + "No blocks to read/write...returning"); + + return ERR_PTR(-EINVAL); } if (req->cmd == READ) { rw_cmd = MDSK_READ_REQ; - } else if (req->cmd == WRITE) { + } else { rw_cmd = MDSK_WRITE_REQ; } bhct = 0; @@ -439,11 +468,11 @@ /* Build the request */ rw_cp = dasd_alloc_request (dasd_diag_discipline.name, bhct << 1, 0, device); if (!rw_cp) { - return NULL; + return ERR_PTR(-ENOMEM); } bio = (diag_bio_t *) (rw_cp->cpaddr); - block = req->sector >> device->sizes.s2b_shift; + block = reloc_sector >> device->sizes.s2b_shift; for (bh = req->bh; bh; bh = bh->b_reqnext) { memset (bio, 0, sizeof (diag_bio_t)); for (size = 0; size < bh->b_size; size += byt_per_blk) { @@ -454,7 +483,9 @@ block++; } } - asm volatile ("STCK %0":"=m" (rw_cp->buildclk)); + + rw_cp->buildclk = get_clock (); + rw_cp->device = device; rw_cp->expires = 50 * TOD_SEC; /* 50 seconds */ rw_cp->req = req; @@ -463,14 +494,15 @@ } static int -dasd_diag_fill_info (dasd_device_t * device, dasd_information_t * info) +dasd_diag_fill_info (dasd_device_t * device, dasd_information2_t * info) { int rc = 0; info->FBA_layout = 1; - info->characteristics_size = sizeof (dasd_diag_characteristics_t); + info->format = DASD_FORMAT_LDL; + info->characteristics_size = sizeof (diag210_t); memcpy (info->characteristics, &((dasd_diag_private_t *) device->private)->rdc_data, - sizeof (dasd_diag_characteristics_t)); + sizeof (diag210_t)); info->confdata_size = 0; return rc; } @@ -501,26 +533,32 @@ examine_error:dasd_diag_examine_error, build_cp_from_req:dasd_diag_build_cp_from_req, dump_sense:dasd_diag_dump_sense, - int_handler:dasd_ext_handler, + int_handler:(void *) dasd_ext_handler, fill_info:dasd_diag_fill_info, + list:LIST_HEAD_INIT(dasd_diag_discipline.list), }; int dasd_diag_init (void) { int rc = 0; + if (MACHINE_IS_VM) { - printk (KERN_INFO PRINTK_HEADER - "%s discipline initializing\n", - dasd_diag_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline initializing", + dasd_diag_discipline.name); + ASCEBC (dasd_diag_discipline.ebcname, 4); ctl_set_bit (0, 9); register_external_interrupt (0x2603, dasd_ext_handler); dasd_discipline_add (&dasd_diag_discipline); } else { - printk (KERN_INFO PRINTK_HEADER - "Machine is not VM: %s discipline not initializing\n", - dasd_diag_discipline.name); + + MESSAGE (KERN_INFO, + "Machine is not VM: %s discipline not initializing", + dasd_diag_discipline.name); + rc = -EINVAL; } return rc; @@ -530,16 +568,19 @@ dasd_diag_cleanup (void) { if (MACHINE_IS_VM) { - printk (KERN_INFO PRINTK_HEADER - "%s discipline cleaning up\n", - dasd_diag_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline cleaning up", + dasd_diag_discipline.name); + dasd_discipline_del (&dasd_diag_discipline); unregister_external_interrupt (0x2603, dasd_ext_handler); ctl_clear_bit (0, 9); } else { - printk (KERN_INFO PRINTK_HEADER - "Machine is not VM: %s discipline not initializing\n", - dasd_diag_discipline.name); + + MESSAGE (KERN_INFO, + "Machine is not VM: %s discipline not initializing", + dasd_diag_discipline.name); } } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_diag.h linux.21rc1-ac2/drivers/s390/block/dasd_diag.h --- linux.21rc1/drivers/s390/block/dasd_diag.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_diag.h 2003-04-25 13:49:34.000000000 +0100 @@ -5,6 +5,11 @@ * ...............: by Hartmunt Penner * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 + * + * $Revision: 1.7 $ + * + * History of changes + * */ #define MDSK_WRITE_REQ 0x01 @@ -18,21 +23,6 @@ #define DEV_CLASS_ECKD 0x04 #define DEV_CLASS_CKD 0x04 -typedef struct dasd_diag_characteristics_t { - u16 dev_nr; - u16 rdc_len; - u8 vdev_class; - u8 vdev_type; - u8 vdev_status; - u8 vdev_flags; - u8 rdev_class; - u8 rdev_type; - u8 rdev_model; - u8 rdev_features; -} __attribute__ ((packed, aligned (4))) - - dasd_diag_characteristics_t; - typedef struct diag_bio_t { u8 type; u8 status; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_eckd.c linux.21rc1-ac2/drivers/s390/block/dasd_eckd.c --- linux.21rc1/drivers/s390/block/dasd_eckd.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_eckd.c 2003-04-25 13:49:34.000000000 +0100 @@ -1,10 +1,13 @@ /* * File...........: linux/drivers/s390/block/dasd_eckd.c * Author(s)......: Holger Smolinski - Carsten Otte + * Horst Hummel + * Carsten Otte * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - + * + * $Revision $ + * * History of changes (starts July 2000) * 07/11/00 Enabled rotational position sensing * 07/14/00 Reorganized the format process for better ERP @@ -42,7 +45,6 @@ #undef PRINTK_HEADER #endif /* PRINTK_HEADER */ #define PRINTK_HEADER DASD_NAME"(eckd):" -#undef CDL_PRINTK #define ECKD_C0(i) (i->home_bytes) #define ECKD_F(i) (i->formula) @@ -55,21 +57,27 @@ #define ECKD_F7(i) (i->factor7) #define ECKD_F8(i) (i->factor8) +#ifdef MODULE +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12)) +MODULE_LICENSE("GPL"); +#endif +#endif + dasd_discipline_t dasd_eckd_discipline; -typedef struct -dasd_eckd_private_t { +typedef struct dasd_eckd_private_t { dasd_eckd_characteristics_t rdc_data; - dasd_eckd_confdata_t conf_data; - eckd_count_t count_area[5]; - int uses_cdl; + dasd_eckd_confdata_t conf_data; + eckd_count_t count_area[5]; + int uses_cdl; + attrib_data_t attrib; /* e.g. cache operations */ } dasd_eckd_private_t; #ifdef CONFIG_DASD_DYNAMIC static devreg_t dasd_eckd_known_devices[] = { { - ci: { hc: {ctype:0x3880, dtype: 3390}}, + ci: { hc: {ctype:0x3880, dtype: 0x3390}}, flag:(DEVREG_MATCH_CU_TYPE | DEVREG_MATCH_DEV_TYPE | DEVREG_TYPE_DEVCHARS), oper_func:dasd_oper_handler @@ -143,7 +151,10 @@ break; } default: - INTERNAL_ERROR ("unknown formula%d\n", rdc->formula); + + MESSAGE (KERN_ERR, + "unknown formula%d", + rdc->formula); } return bpr; } @@ -190,10 +201,35 @@ return rpt; } +static inline void +check_XRC (ccw1_t *de_ccw, + DE_eckd_data_t *data, + dasd_device_t *device) +{ + + dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private; + + /* switch on System Time Stamp - needed for XRC Support */ + if (private->rdc_data.facilities.XRC_supported) { + + data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ + data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ + + data->ep_sys_time = get_clock (); + + de_ccw->count = sizeof (DE_eckd_data_t); + de_ccw->flags |= CCW_FLAG_SLI; + } + + return; + +} /* end check_XRC */ + static inline int define_extent (ccw1_t * de_ccw, DE_eckd_data_t * data, - int trk, int totrk, int cmd, dasd_device_t * device, ccw_req_t* cqr) + int trk, int totrk, + int cmd, dasd_device_t * device, ccw_req_t* cqr) { int rc=0; ch_t geo, beg, end; @@ -207,8 +243,10 @@ end.head = totrk % geo.head; memset (de_ccw, 0, sizeof (ccw1_t)); + de_ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; de_ccw->count = 16; + if ((rc=dasd_set_normalized_cda (de_ccw, __pa (data), cqr, device))) return rc; @@ -224,40 +262,86 @@ case DASD_ECKD_CCW_READ_KD_MT: case DASD_ECKD_CCW_READ_COUNT: data->mask.perm = 0x1; - data->attributes.operation = 0x3; /* enable seq. caching */ + data->attributes.operation = private->attrib.operation; break; case DASD_ECKD_CCW_WRITE: case DASD_ECKD_CCW_WRITE_MT: case DASD_ECKD_CCW_WRITE_KD: case DASD_ECKD_CCW_WRITE_KD_MT: data->mask.perm = 0x02; - data->attributes.operation = 0x3; /* enable seq. caching */ + data->attributes.operation = private->attrib.operation; + + check_XRC (de_ccw, + data, + device); break; case DASD_ECKD_CCW_WRITE_CKD: case DASD_ECKD_CCW_WRITE_CKD_MT: - data->attributes.operation = 0x1; /* format through cache */ + data->attributes.operation = DASD_BYPASS_CACHE; + + check_XRC (de_ccw, + data, + device); break; case DASD_ECKD_CCW_ERASE: case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: case DASD_ECKD_CCW_WRITE_RECORD_ZERO: data->mask.perm = 0x3; data->mask.auth = 0x1; - data->attributes.operation = 0x1; /* format through cache */ + data->attributes.operation = DASD_BYPASS_CACHE; + + check_XRC (de_ccw, + data, + device); break; default: - INTERNAL_ERROR ("unknown opcode 0x%x\n", cmd); + + MESSAGE (KERN_ERR, + "unknown opcode 0x%x", + cmd); + break; } - data->attributes.mode = 0x3; + + data->attributes.mode = 0x3; /* ECKD */ + if (private->rdc_data.cu_type == 0x2105 - && !(private->uses_cdl && trk < 2) - ) { - data->reserved |= 0x40; + && !(private->uses_cdl && trk < 2) ) { + + data->ga_extended |= 0x40; } - data->beg_ext.cyl = beg.cyl; + + /* check for sequential prestage - enhance cylinder range */ + if (data->attributes.operation == DASD_SEQ_PRESTAGE || + data->attributes.operation == DASD_SEQ_ACCESS ) { + + if (end.cyl + private->attrib.nr_cyl < geo.cyl) { + + end.cyl += private->attrib.nr_cyl; + + DBF_DEV_EVENT (DBF_NOTICE, device, + "Enhanced DE Cylinder from %x to %x", + (totrk / geo.head), + end.cyl); + + + } else { + end.cyl = (geo.cyl -1); + + DBF_DEV_EVENT (DBF_NOTICE, device, + "Enhanced DE Cylinder from %x to " + "End of device %x", + (totrk / geo.head), + end.cyl); + + } + } + + data->beg_ext.cyl = beg.cyl; data->beg_ext.head = beg.head; - data->end_ext.cyl = end.cyl; + data->end_ext.cyl = end.cyl; data->end_ext.head = end.head; + return rc; } @@ -266,7 +350,11 @@ LO_eckd_data_t * data, int trk, int rec_on_trk, - int no_rec, int cmd, dasd_device_t * device, int reclen, ccw_req_t* cqr) + int no_rec, + int cmd, + dasd_device_t * device, + int reclen, + ccw_req_t* cqr) { int rc=0; dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private; @@ -276,10 +364,14 @@ ch_t seek = { trk / (geo.head), trk % (geo.head) }; int sector = 0; -#ifdef CDL_PRINTK - printk ("Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d\n", trk, - rec_on_trk, no_rec, cmd, reclen); -#endif + DBF_EVENT (DBF_INFO, + "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d", + trk, + rec_on_trk, + no_rec, + cmd, + reclen); + memset (lo_ccw, 0, sizeof (ccw1_t)); lo_ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; lo_ccw->count = 16; @@ -365,7 +457,10 @@ data->operation.operation = 0x0b; break; default: - INTERNAL_ERROR ("unknown opcode 0x%x\n", cmd); + + MESSAGE (KERN_ERR, + "unknown opcode 0x%x", + cmd); } memcpy (&(data->seek_addr), &seek, sizeof (ch_t)); memcpy (&(data->search_arg), &seek, sizeof (ch_t)); @@ -391,79 +486,113 @@ static int dasd_eckd_check_characteristics (struct dasd_device_t *device) { - int rc = 0; + int rc = 0; void *conf_data; - void *rdc_data; - int conf_len; + void *rdc_data; + int conf_len; dasd_eckd_private_t *private; if (device == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "Null device pointer passed to characteristics checker\n"); + + MESSAGE (KERN_WARNING, "%s", + "Null device pointer passed to characteristics " + "checker"); + return -ENODEV; } - device->private = kmalloc (sizeof (dasd_eckd_private_t), GFP_KERNEL); + device->private = kmalloc (sizeof (dasd_eckd_private_t), + GFP_KERNEL); + if (device->private == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "memory allocation failed for private data\n"); + + MESSAGE (KERN_WARNING, "%s", + "memory allocation failed for private data"); + rc = -ENOMEM; goto fail; } - private = (dasd_eckd_private_t *) device->private; + + private = (dasd_eckd_private_t *) device->private; rdc_data = (void *) &(private->rdc_data); - rc = read_dev_chars (device->devinfo.irq, &rdc_data, 64); + + /* Read Device Characteristics */ + rc = read_dev_chars (device->devinfo.irq, + &rdc_data, + 64); if (rc) { - printk (KERN_WARNING PRINTK_HEADER - "Read device characteristics returned error %d\n", rc); + + MESSAGE (KERN_WARNING, + "Read device characteristics returned error %d", + rc); + goto fail; } - printk (KERN_INFO PRINTK_HEADER - "%04X on sch %d: %04X/%02X(CU:%04X/%02X) " - "Cyl:%d Head:%d Sec:%d\n", - device->devinfo.devno, device->devinfo.irq, - private->rdc_data.dev_type, private->rdc_data.dev_model, - private->rdc_data.cu_type, private->rdc_data.cu_model.model, - private->rdc_data.no_cyl, private->rdc_data.trk_per_cyl, - private->rdc_data.sec_per_trk); - rc = read_conf_data (device->devinfo.irq, &conf_data, &conf_len, + + DEV_MESSAGE (KERN_INFO, device, + "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", + private->rdc_data.dev_type, + private->rdc_data.dev_model, + private->rdc_data.cu_type, + private->rdc_data.cu_model.model, + private->rdc_data.no_cyl, + private->rdc_data.trk_per_cyl, + private->rdc_data.sec_per_trk); + + /* set default cache operations */ + private->attrib.operation = DASD_SEQ_ACCESS; + private->attrib.nr_cyl = 0x02; + + /* Read Configuration Data */ + rc = read_conf_data (device->devinfo.irq, + &conf_data, + &conf_len, LPM_ANYPATH); if (rc == -EOPNOTSUPP) { rc = 0; /* this one is ok */ } if (rc) { - printk (KERN_WARNING PRINTK_HEADER - "Read configuration data returned error %d\n", rc); + + MESSAGE (KERN_WARNING, + "Read configuration data returned error %d", + rc); + goto fail; } if (conf_data == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No configuration data retrieved\n"); + + MESSAGE (KERN_WARNING, "%s", + "No configuration data retrieved"); + goto out; /* no errror */ } if (conf_len != sizeof (dasd_eckd_confdata_t)) { - printk (KERN_WARNING PRINTK_HEADER - "sizes of configuration data mismatch" - "%d (read) vs %ld (expected)\n", - conf_len, sizeof (dasd_eckd_confdata_t)); + + MESSAGE (KERN_WARNING, + "sizes of configuration data mismatch" + "%d (read) vs %ld (expected)", + conf_len, + sizeof (dasd_eckd_confdata_t)); + goto out; /* no errror */ } memcpy (&private->conf_data, conf_data, sizeof (dasd_eckd_confdata_t)); - printk (KERN_INFO PRINTK_HEADER - "%04X on sch %d: %04X/%02X(CU:%04X/%02X): " - "Configuration data read\n", - device->devinfo.devno, device->devinfo.irq, + + DEV_MESSAGE (KERN_INFO, device, + "%04X/%02X(CU:%04X/%02X): Configuration data read", private->rdc_data.dev_type, private->rdc_data.dev_model, private->rdc_data.cu_type, private->rdc_data.cu_model.model); goto out; + fail: - if ( rc ) { + if (device->private) { kfree (device->private); device->private = NULL; } + out: return rc; } @@ -498,8 +627,9 @@ 2 * sizeof (LO_eckd_data_t), device); if (cqr == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); goto out; } DE_data = cqr->data; @@ -557,13 +687,17 @@ } cqr->device = device; cqr->retries = 0; + cqr->buildclk = get_clock (); cqr->status = CQR_STATUS_FILLED; dasd_chanq_enq (&device->queue, cqr); goto out; + clear_cqr: dasd_free_request (cqr,device); - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + cqr=NULL; out: return cqr; @@ -578,14 +712,17 @@ char *cdl_msg; int status; int i; + private->uses_cdl = 1; status = device->init_cqr->status; dasd_chanq_deq (&device->queue, device->init_cqr); dasd_free_request (device->init_cqr, device); /* Free the cqr and cleanup device->sizes */ if ( status != CQR_STATUS_DONE ) { - DASD_MESSAGE (KERN_WARNING,device,"%s", - "volume analysis returned unformatted disk"); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "volume analysis returned unformatted disk"); + return -EMEDIUMTYPE; } /* Check Track 0 for Compatible Disk Layout */ @@ -614,8 +751,9 @@ } } else { if (private->count_area[3].record == 1) { - DASD_MESSAGE (KERN_WARNING, device, "%s", - "Trk 0: no records after VTOC!"); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "Trk 0: no records after VTOC!"); } } if (count_area != NULL && /* we found notthing violating our disk layout */ @@ -631,8 +769,10 @@ } } if (device->sizes.bp_block == 0) { - DASD_MESSAGE (KERN_WARNING, device, "%s", - "Volume has incompatible disk layout"); + + DEV_MESSAGE (KERN_WARNING, device, "%s", + "Volume has incompatible disk layout"); + return -EMEDIUMTYPE; } device->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */ @@ -647,18 +787,21 @@ device->sizes.bp_block)); cdl_msg = private-> - uses_cdl ? "compatible disk layout" : "classic disk layout"; + uses_cdl ? "compatible disk layout" : "linux disk layout"; + + DEV_MESSAGE (KERN_INFO, device, + "(%dkB blks): %dkB at %dkB/trk %s", + (device->sizes.bp_block >> 10), + ((private->rdc_data.no_cyl * + private->rdc_data.trk_per_cyl * + recs_per_track (&private->rdc_data, 0, + device->sizes.bp_block) * + (device->sizes.bp_block >> 9)) >> 1), + ((recs_per_track (&private->rdc_data, 0, + device->sizes.bp_block) * + device->sizes.bp_block) >> 10), + cdl_msg); - DASD_MESSAGE (KERN_INFO, device, "(%dkB blks): %dkB at %dkB/trk %s", - (device->sizes.bp_block >> 10), - (private->rdc_data.no_cyl * - private->rdc_data.trk_per_cyl * - recs_per_track (&private->rdc_data, 0, - device->sizes.bp_block) * - (device->sizes.bp_block >> 9)) >> 1, - (recs_per_track (&private->rdc_data, 0, - device->sizes.bp_block) * - device->sizes.bp_block) >> 10, cdl_msg); return 0; } @@ -703,13 +846,21 @@ int wrccws = rpt; int datasize = sizeof (DE_eckd_data_t) + sizeof (LO_eckd_data_t); - if (fdata->start_unit >= (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)){ - DASD_MESSAGE (KERN_INFO, device, "Track no %d too big!", fdata->start_unit); + if (fdata->start_unit >= + (private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl)){ + + DEV_MESSAGE (KERN_INFO, device, + "Track no %d too big!", + fdata->start_unit); + return NULL; } if ( fdata->start_unit > fdata->stop_unit) { - DASD_MESSAGE (KERN_INFO, device, "Track %d reached! ending.", - fdata->start_unit); + + DEV_MESSAGE (KERN_INFO, device, + "Track %d reached! ending.", + fdata->start_unit); + return NULL; } switch (fdata->blksize) { @@ -719,8 +870,11 @@ case 4096: break; default: - printk (KERN_WARNING PRINTK_HEADER - "Invalid blocksize %d...terminating!\n", fdata->blksize); + + MESSAGE (KERN_WARNING, + "Invalid blocksize %d...terminating!", + fdata->blksize); + return NULL; } switch (fdata->intensity) { @@ -734,8 +888,11 @@ case 0x0c: break; default: - printk (KERN_WARNING PRINTK_HEADER - "Invalid flags 0x%x...terminating!\n", fdata->intensity); + + MESSAGE (KERN_WARNING, + "Invalid flags 0x%x...terminating!", + fdata->intensity); + return NULL; } @@ -745,9 +902,12 @@ (fdata->start_unit % private->rdc_data.no_cyl == 0 && (fdata->start_unit / private->rdc_data.no_cyl) % (private->rdc_data.no_cyl / 20))) { - DASD_MESSAGE (KERN_INFO, device, - "Format Cylinder: %d Flags: %d", - fdata->start_unit / private->rdc_data.trk_per_cyl, fdata->intensity); + + DBF_DEV_EVENT (DBF_NOTICE, device, + "Format Cylinder: %d Flags: %d", + fdata->start_unit / private->rdc_data.trk_per_cyl, + fdata->intensity); + } if ((fdata->intensity & ~0x8) & 0x04) { wrccws = 1; @@ -789,14 +949,14 @@ switch (fdata->intensity & ~0x08) { case 0x03: if (define_extent (last_ccw, DE_data, fdata->start_unit, fdata->start_unit, - DASD_ECKD_CCW_WRITE_HOME_ADDRESS, + DASD_ECKD_CCW_WRITE_HOME_ADDRESS, device, fcp)) { goto clear_fcp; } last_ccw->flags |= CCW_FLAG_CC; last_ccw++; if (locate_record (last_ccw, LO_data, fdata->start_unit, 0, wrccws, - DASD_ECKD_CCW_WRITE_HOME_ADDRESS, device, + DASD_ECKD_CCW_WRITE_HOME_ADDRESS, device, device->sizes.bp_block, fcp)) { goto clear_fcp; } @@ -811,7 +971,7 @@ last_ccw->flags |= CCW_FLAG_CC; last_ccw++; if (locate_record (last_ccw, LO_data, fdata->start_unit, 0, wrccws, - DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, + DASD_ECKD_CCW_WRITE_RECORD_ZERO, device, device->sizes.bp_block, fcp)) { goto clear_fcp; } @@ -837,11 +997,19 @@ last_ccw++; break; default: - PRINT_WARN ("Unknown format flags...%d\n", fdata->intensity); + + MESSAGE (KERN_WARNING, + "Unknown format flags...%d", + fdata->intensity); + return NULL; } if (fdata->intensity & 0x02) { - PRINT_WARN ("Unsupported format flag...%d\n", fdata->intensity); + + MESSAGE (KERN_WARNING, + "Unsupported format flag...%d", + fdata->intensity); + return NULL; } if (fdata->intensity & 0x01) { /* write record zero */ @@ -913,6 +1081,7 @@ } (last_ccw - 1)->flags &= ~(CCW_FLAG_CC | CCW_FLAG_DC); fcp->device = device; + fcp->buildclk = get_clock (); fcp->status = CQR_STATUS_FILLED; } goto out; @@ -939,8 +1108,10 @@ case 0x9343: return dasd_9343_erp_examine (cqr, stat); default: - printk (KERN_WARNING PRINTK_HEADER - "default (unknown CU type) - RECOVERABLE return \n"); + + MESSAGE (KERN_WARNING, "%s", + "default (unknown CU type) - RECOVERABLE return"); + return dasd_era_recover; } } @@ -1015,26 +1186,36 @@ int byt_per_blk = device->sizes.bp_block; int shift = device->sizes.s2b_shift; int blk_per_trk = recs_per_track (&(private->rdc_data), 0, byt_per_blk); - int btrk = (req->sector >> shift) / blk_per_trk; - int etrk = ((req->sector + req->nr_sectors - 1) >> shift) / blk_per_trk; - int recid = req->sector >> shift; + unsigned long reloc_sector = req->sector + + device->major_info->gendisk.part[MINOR(req->rq_dev)].start_sect; + int btrk = (reloc_sector >> shift) / blk_per_trk; + int etrk = ((reloc_sector + req->nr_sectors - 1) >> shift) / blk_per_trk; + int recid = reloc_sector >> shift; int locate4k_set = 0; int nlocs = 0; + int errcode; if (req->cmd == READ) { rw_cmd = DASD_ECKD_CCW_READ_MT; } else if (req->cmd == WRITE) { rw_cmd = DASD_ECKD_CCW_WRITE_MT; } else { - PRINT_ERR ("Unknown command %d\n", req->cmd); - return NULL; + + MESSAGE (KERN_ERR, + "Unknown command %d", + req->cmd); + + return ERR_PTR(-EINVAL); } /* Build the request */ /* count bhs to prevent errors, when bh smaller than block */ bhct = 0; for (bh = req->bh; bh; bh = bh->b_reqnext) { - if (bh->b_size < byt_per_blk) - BUG(); + if (bh->b_size < byt_per_blk) { + MESSAGE(KERN_ERR, "ignoring bogus sized request: %d<%d", + bh->b_size, byt_per_blk); + return ERR_PTR(-EINVAL); + } bhct+= bh->b_size >> (device->sizes.s2b_shift+9); } if (btrk < 2 && private->uses_cdl) { @@ -1050,12 +1231,12 @@ sizeof (LO_eckd_data_t), device); if (!rw_cp) { - return NULL; + return ERR_PTR(-ENOMEM); } DE_data = rw_cp->data; LO_data = rw_cp->data + sizeof (DE_eckd_data_t); ccw = rw_cp->cpaddr; - if (define_extent (ccw, DE_data, btrk, etrk, rw_cmd, device, rw_cp)) { + if (errcode=define_extent (ccw, DE_data, btrk, etrk, rw_cmd, device, rw_cp)) { goto clear_rw_cp; } ccw->flags |= CCW_FLAG_CC; @@ -1068,7 +1249,7 @@ && private->uses_cdl) { /* Do a locate record for our special blocks */ int cmd = dasd_eckd_cdl_cmd (device,recid, req->cmd); - if (locate_record (ccw, + if (errcode=locate_record (ccw, LO_data++, recid / blk_per_trk, recid % blk_per_trk + 1, @@ -1078,11 +1259,11 @@ } } else { // Do a locate record for standard blocks */ - if (locate_record (ccw, + if (errcode=locate_record (ccw, LO_data++, recid /blk_per_trk, recid %blk_per_trk + 1, - (((req->sector + + (((reloc_sector + req->nr_sectors) >> shift) - recid), rw_cmd, device, @@ -1105,25 +1286,31 @@ 0xE5, byt_per_blk - ccw->count); } } - if (dasd_set_normalized_cda (ccw, __pa (bh->b_data+size), rw_cp, device)) { + if (errcode=dasd_set_normalized_cda (ccw, __pa (bh->b_data+size), rw_cp, device)) { goto clear_rw_cp; } recid++; } bh = bh->b_reqnext; } - ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC); - rw_cp->device = device; + ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC); + rw_cp->device = device; rw_cp->expires = 5 * TOD_MIN; /* 5 minutes */ - rw_cp->req = req; - rw_cp->lpm = LPM_ANYPATH; - rw_cp->retries = 2; - asm volatile ("STCK %0":"=m" (rw_cp->buildclk)); - check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED); + rw_cp->req = req; + rw_cp->lpm = LPM_ANYPATH; + rw_cp->retries = 256; + + rw_cp->buildclk = get_clock (); + + check_then_set (&rw_cp->status, + CQR_STATUS_EMPTY, + CQR_STATUS_FILLED); + goto out; clear_rw_cp: - dasd_free_request (rw_cp, device); - rw_cp=NULL; + dasd_free_request (rw_cp, + device); + rw_cp=ERR_PTR(errcode); out: return rw_cp; } @@ -1178,16 +1365,19 @@ { ccw_req_t *cqr = dasd_alloc_request (dasd_eckd_discipline.name, 1 + 1, 0, device); + if (cqr == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + return NULL; } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RESERVE; cqr->device = device; cqr->retries = 0; cqr->expires = 10 * TOD_SEC; - cqr->options = (DOIO_WAIT_FOR_INTERRUPT | DOIO_TIMEOUT); /* timeout reqest */ + cqr->buildclk = get_clock (); cqr->status = CQR_STATUS_FILLED; return cqr; } @@ -1204,16 +1394,19 @@ { ccw_req_t *cqr = dasd_alloc_request (dasd_eckd_discipline.name, 1 + 1, 0, device); + if (cqr == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + return NULL; } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RELEASE; cqr->device = device; cqr->retries = 0; cqr->expires = 10 * TOD_SEC; - cqr->options = (DOIO_WAIT_FOR_INTERRUPT | DOIO_TIMEOUT); /* timeout reqest */ + cqr->buildclk = get_clock (); cqr->status = CQR_STATUS_FILLED; return cqr; @@ -1231,16 +1424,19 @@ { ccw_req_t *cqr = dasd_alloc_request (dasd_eckd_discipline.name, 1 + 1, 0, device); + if (cqr == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "No memory to allocate initialization request\n"); + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + return NULL; } cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SLCK; cqr->device = device; cqr->retries = 0; cqr->expires = 10 * TOD_SEC; - cqr->options = (DOIO_WAIT_FOR_INTERRUPT | DOIO_TIMEOUT); /* timeout reqest */ + cqr->buildclk = get_clock (); cqr->status = CQR_STATUS_FILLED; return cqr; } @@ -1274,14 +1470,17 @@ } static int -dasd_eckd_fill_info (dasd_device_t * device, dasd_information_t * info) +dasd_eckd_fill_info (dasd_device_t * device, dasd_information2_t * info) { int rc = 0; info->label_block = 2; - if (((dasd_eckd_private_t *) device->private)->uses_cdl) + if (((dasd_eckd_private_t *) device->private)->uses_cdl) { info->FBA_layout = 0; - else - info->FBA_layout = 1; + info->format = DASD_FORMAT_CDL; + } else { + info->FBA_layout = 1; + info->format = DASD_FORMAT_LDL; + } info->characteristics_size = sizeof (dasd_eckd_characteristics_t); memcpy (info->characteristics, &((dasd_eckd_private_t *) device->private)->rdc_data, @@ -1293,6 +1492,143 @@ return rc; } +/* + * DASD_ECKD_READ_STATS + * + * DESCRIPTION + * build the channel program to read the performance statistics + * of the attached subsystem + */ +ccw_req_t * +dasd_eckd_read_stats (struct dasd_device_t * device) +{ + + int rc; + ccw1_t *ccw; + ccw_req_t *cqr; + dasd_psf_prssd_data_t *prssdp; + dasd_rssd_perf_stats_t *statsp; + + cqr = dasd_alloc_request (dasd_eckd_discipline.name, + 1 /* PSF */ + 1 /* RSSD */, + (sizeof (dasd_psf_prssd_data_t) + + sizeof (dasd_rssd_perf_stats_t) ), + device); + + if (cqr == NULL) { + + MESSAGE (KERN_WARNING, "%s", + "No memory to allocate initialization request"); + + return NULL; + } + + cqr->device = device; + cqr->retries = 0; + cqr->expires = 10 * TOD_SEC; + + /* Prepare for Read Subsystem Data */ + prssdp = (dasd_psf_prssd_data_t *) cqr->data; + + memset (prssdp, 0, sizeof (dasd_psf_prssd_data_t)); + + prssdp->order = PSF_ORDER_PRSSD; + prssdp->suborder = 0x01; /* Perfomance Statistics */ + prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */ + + ccw = cqr->cpaddr; + + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->count = sizeof (dasd_psf_prssd_data_t); + ccw->flags |= CCW_FLAG_CC; + + if ((rc = dasd_set_normalized_cda (ccw,__pa (prssdp), cqr, device))) { + + dasd_free_request (cqr, + device); + return NULL; + } + + ccw++; + + /* Read Subsystem Data - Performance Statistics */ + statsp = (dasd_rssd_perf_stats_t *) (prssdp + 1); + memset (statsp, 0, sizeof (dasd_rssd_perf_stats_t)); + + ccw->cmd_code = DASD_ECKD_CCW_RSSD; + ccw->count = sizeof (dasd_rssd_perf_stats_t); + + if ((rc = dasd_set_normalized_cda (ccw,__pa (statsp), cqr, device))) { + + dasd_free_request (cqr, + device); + return NULL; + } + cqr->buildclk = get_clock (); + cqr->status = CQR_STATUS_FILLED; + + return cqr; +} /* end dasd_eckd_rstat */ + +/* + * DASD_ECKD_RET_STATS + * + * DESCRIPTION + * returns pointer to Performance Statistics Data. + */ +dasd_rssd_perf_stats_t * +dasd_eckd_ret_stats (ccw_req_t *cqr) +{ + + dasd_psf_prssd_data_t *prssdp; + dasd_rssd_perf_stats_t *statsp; + + if (cqr == NULL) { + + return NULL; + } + + /* Prepare for Read Subsystem Data */ + prssdp = (dasd_psf_prssd_data_t *) cqr->data; + statsp = (dasd_rssd_perf_stats_t *) (prssdp + 1); + + return statsp; + +} /* end dasd_eckd_rstat */ + + +/* + * DASD_ECKD_SET_ATTRUB + * + * DESCRIPTION + * stores the attributes for cache operation to be used in Define Extend (DE). + */ +int +dasd_eckd_set_attrib (dasd_device_t *device, + attrib_data_t *attrib) +{ + int rc = 0; + dasd_eckd_private_t *private = (dasd_eckd_private_t *) device->private; + + DBF_DEV_EVENT (DBF_ERR, device, + "cache operation mode got " + "%x (%i cylinder prestage)", + attrib->operation, + attrib->nr_cyl); + + private->attrib = *attrib; + + DBF_DEV_EVENT (DBF_ERR, device, + "cache operation mode set to " + "%x (%i cylinder prestage)", + private->attrib.operation, + private->attrib.nr_cyl); + + + return rc; + +} /* end dasd_eckd_set_attrib */ + static char* dasd_eckd_dump_sense (struct dasd_device_t *device, ccw_req_t *req) @@ -1304,8 +1640,10 @@ int len, sl, sct; if (page == NULL) { - printk (KERN_ERR PRINTK_HEADER - "No memory to dump sense data\n"); + + MESSAGE (KERN_ERR, "%s", + "No memory to dump sense data"); + return NULL; } @@ -1318,17 +1656,27 @@ len += sprintf (page + len, KERN_ERR PRINTK_HEADER "Failing CCW: %p\n", (void *) (long) stat->cpa); { + ccw1_t *act = req->cpaddr; int i = req->cplength; + do { -#ifdef ERP_DEBUG - printk (KERN_ERR "CCW %p: %08X %08X\n", - act, ((int *) act)[0], ((int *) act)[1]); - printk (KERN_ERR "DAT: %08X %08X %08X %08X\n", - ((int *) act->cda)[0], ((int *) act->cda)[1], - ((int *) act->cda)[2], ((int *) act->cda)[3]); -#endif /* ERP_DEBUG */ + + DBF_EVENT (DBF_INFO, + "CCW %p: %08X %08X", + act, + ((int *) act)[0], + ((int *) act)[1]); + + DBF_EVENT (DBF_INFO, + "DAT: %08X %08X %08X %08X", + ((int *) (addr_t) act->cda)[0], + ((int *) (addr_t) act->cda)[1], + ((int *) (addr_t) act->cda)[2], + ((int *) (addr_t) act->cda)[3]); + act++; + } while (--i); } if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL) { @@ -1353,13 +1701,15 @@ } else { /* 32 Byte Sense Data */ len += sprintf (page + len, KERN_ERR PRINTK_HEADER - "32 Byte: Format: %x Exception class %x\n", + "32 Byte: Format: %x " + "Exception class %x\n", sense[6] & 0x0f, sense[22] >> 4); } } - printk ("Sense data:\n%s", - page); + MESSAGE (KERN_ERR, + "Sense data:\n%s", + page); free_page ((unsigned long) page); @@ -1371,7 +1721,7 @@ owner: THIS_MODULE, name:"ECKD", ebcname:"ECKD", - max_blocks:255, + max_blocks:240, id_check:dasd_eckd_id_check, check_characteristics:dasd_eckd_check_characteristics, init_analysis:dasd_eckd_init_analysis, @@ -1391,14 +1741,21 @@ steal_lock:dasd_eckd_steal_lock, merge_cp:dasd_eckd_merge_cp, fill_info:dasd_eckd_fill_info, + read_stats:dasd_eckd_read_stats, + ret_stats:dasd_eckd_ret_stats, + set_attrib:dasd_eckd_set_attrib, + list:LIST_HEAD_INIT(dasd_eckd_discipline.list), }; int dasd_eckd_init (void) { int rc = 0; - printk (KERN_INFO PRINTK_HEADER - "%s discipline initializing\n", dasd_eckd_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline initializing", + dasd_eckd_discipline.name); + ASCEBC (dasd_eckd_discipline.ebcname, 4); dasd_discipline_add (&dasd_eckd_discipline); #ifdef CONFIG_DASD_DYNAMIC @@ -1407,10 +1764,12 @@ for (i = 0; i < sizeof (dasd_eckd_known_devices) / sizeof (devreg_t); i++) { - printk (KERN_INFO PRINTK_HEADER - "We are interested in: CU %04X/%02x\n", - dasd_eckd_known_devices[i].ci.hc.ctype, - dasd_eckd_known_devices[i].ci.hc.cmode); + + MESSAGE (KERN_INFO, + "We are interested in: CU %04X/%02x", + dasd_eckd_known_devices[i].ci.hc.ctype, + dasd_eckd_known_devices[i].ci.hc.cmode); + s390_device_register (&dasd_eckd_known_devices[i]); } } @@ -1421,18 +1780,23 @@ void dasd_eckd_cleanup (void) { - printk (KERN_INFO PRINTK_HEADER - "%s discipline cleaning up\n", dasd_eckd_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline cleaning up", + dasd_eckd_discipline.name); + #ifdef CONFIG_DASD_DYNAMIC { int i; for (i = 0; i < sizeof (dasd_eckd_known_devices) / sizeof (devreg_t); i++) { - printk (KERN_INFO PRINTK_HEADER - "We were interested in: CU %04X/%02x\n", - dasd_eckd_known_devices[i].ci.hc.ctype, - dasd_eckd_known_devices[i].ci.hc.cmode); + + MESSAGE (KERN_INFO, + "We were interested in: CU %04X/%02x", + dasd_eckd_known_devices[i].ci.hc.ctype, + dasd_eckd_known_devices[i].ci.hc.cmode); + s390_device_unregister (&dasd_eckd_known_devices[i]); } } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_eckd.h linux.21rc1-ac2/drivers/s390/block/dasd_eckd.h --- linux.21rc1/drivers/s390/block/dasd_eckd.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_eckd.h 2003-04-25 13:49:34.000000000 +0100 @@ -1,78 +1,96 @@ +/* + * File...........: linux/drivers/s390/block/dasd_eckd.h + * Author(s)......: Holger Smolinski + * Horst Hummel + * Bugreports.to..: + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 + * + * $Revision: 1.14 $ + * + * History of changes + * + */ + #ifndef DASD_ECKD_H #define DASD_ECKD_H #include "dasd_3990_erp.h" #include "dasd_9343_erp.h" -#define DASD_ECKD_CCW_WRITE 0x05 -#define DASD_ECKD_CCW_READ 0x06 +/******************************************************************************* + * SECTION: CCW Definitions + ******************************************************************************/ +#define DASD_ECKD_CCW_WRITE 0x05 +#define DASD_ECKD_CCW_READ 0x06 #define DASD_ECKD_CCW_WRITE_HOME_ADDRESS 0x09 -#define DASD_ECKD_CCW_READ_HOME_ADDRESS 0x0a -#define DASD_ECKD_CCW_WRITE_KD 0x0d -#define DASD_ECKD_CCW_READ_KD 0x0e -#define DASD_ECKD_CCW_ERASE 0x11 -#define DASD_ECKD_CCW_READ_COUNT 0x12 -#define DASD_ECKD_CCW_WRITE_RECORD_ZERO 0x15 -#define DASD_ECKD_CCW_READ_RECORD_ZERO 0x16 -#define DASD_ECKD_CCW_WRITE_CKD 0x1d -#define DASD_ECKD_CCW_READ_CKD 0x1e -#define DASD_ECKD_CCW_LOCATE_RECORD 0x47 -#define DASD_ECKD_CCW_DEFINE_EXTENT 0x63 -#define DASD_ECKD_CCW_WRITE_MT 0x85 -#define DASD_ECKD_CCW_READ_MT 0x86 -#define DASD_ECKD_CCW_WRITE_KD_MT 0x8d -#define DASD_ECKD_CCW_READ_KD_MT 0x8e -#define DASD_ECKD_CCW_RELEASE 0x94 -#define DASD_ECKD_CCW_READ_CKD_MT 0x9e -#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d -#define DASD_ECKD_CCW_RESERVE 0xB4 -#define DASD_ECKD_CCW_SLCK 0x14 /* steal lock - unconditional reserve */ +#define DASD_ECKD_CCW_READ_HOME_ADDRESS 0x0a +#define DASD_ECKD_CCW_WRITE_KD 0x0d +#define DASD_ECKD_CCW_READ_KD 0x0e +#define DASD_ECKD_CCW_ERASE 0x11 +#define DASD_ECKD_CCW_READ_COUNT 0x12 +#define DASD_ECKD_CCW_SLCK 0x14 +#define DASD_ECKD_CCW_WRITE_RECORD_ZERO 0x15 +#define DASD_ECKD_CCW_READ_RECORD_ZERO 0x16 +#define DASD_ECKD_CCW_WRITE_CKD 0x1d +#define DASD_ECKD_CCW_READ_CKD 0x1e +#define DASD_ECKD_CCW_PSF 0x27 +#define DASD_ECKD_CCW_RSSD 0x3e +#define DASD_ECKD_CCW_LOCATE_RECORD 0x47 +#define DASD_ECKD_CCW_DEFINE_EXTENT 0x63 +#define DASD_ECKD_CCW_WRITE_MT 0x85 +#define DASD_ECKD_CCW_READ_MT 0x86 +#define DASD_ECKD_CCW_WRITE_KD_MT 0x8d +#define DASD_ECKD_CCW_READ_KD_MT 0x8e +#define DASD_ECKD_CCW_RELEASE 0x94 +#define DASD_ECKD_CCW_READ_CKD_MT 0x9e +#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d +#define DASD_ECKD_CCW_RESERVE 0xB4 + +/* + *Perform Subsystem Function / Sub-Orders + */ +#define PSF_ORDER_PRSSD 0x18 + + +/******************************************************************************* + * SECTION: Type Definitions + ******************************************************************************/ -typedef - struct eckd_count_t { +typedef struct eckd_count_t { __u16 cyl; __u16 head; __u8 record; __u8 kl; __u16 dl; -} __attribute__ ((packed)) +} __attribute__ ((packed)) eckd_count_t; - eckd_count_t; -typedef - struct ch_t { +typedef struct ch_t { __u16 cyl; __u16 head; -} __attribute__ ((packed)) +} __attribute__ ((packed)) ch_t; - ch_t; -typedef - struct chs_t { +typedef struct chs_t { __u16 cyl; __u16 head; __u32 sector; -} __attribute__ ((packed)) +} __attribute__ ((packed)) chs_t; - chs_t; -typedef - struct chr_t { +typedef struct chr_t { __u16 cyl; __u16 head; __u8 record; -} __attribute__ ((packed)) +} __attribute__ ((packed)) chr_t; - chr_t; -typedef - struct geom_t { +typedef struct geom_t { __u16 cyl; __u16 head; __u32 sector; -} __attribute__ ((packed)) +} __attribute__ ((packed)) geom_t; - geom_t; typedef struct eckd_home_t { __u8 skip_control[14]; @@ -83,12 +101,10 @@ __u8 reserved; __u8 key_length; __u8 reserved2[2]; -} __attribute__ ((packed)) +} __attribute__ ((packed)) eckd_home_t; - eckd_home_t; -typedef - struct DE_eckd_data_t { +typedef struct DE_eckd_data_t { struct { unsigned char perm:2; /* Permissions on this extent */ unsigned char reserved:1; @@ -103,18 +119,20 @@ unsigned char cfw:1; /* Cache fast write */ unsigned char dfw:1; /* DASD fast write */ } __attribute__ ((packed)) attributes; - __u16 short blk_size; /* Blocksize */ + __u16 blk_size; /* Blocksize */ __u16 fast_write_id; - __u8 unused; - __u8 reserved; + __u8 ga_additional; /* Global Attributes Additional */ + __u8 ga_extended; /* Global Attributes Extended */ ch_t beg_ext; ch_t end_ext; -} __attribute__ ((packed)) + unsigned long long ep_sys_time; /* Extended Parameter - System Time Stamp */ + __u8 ep_format; /* Extended Parameter format byte */ + __u8 ep_prio; /* Extended Parameter priority I/O byte */ + __u8 ep_reserved[6]; /* Extended Parameter Reserved */ +} __attribute__ ((packed)) DE_eckd_data_t; - DE_eckd_data_t; -typedef - struct LO_eckd_data_t { +typedef struct LO_eckd_data_t { struct { unsigned char orientation:2; unsigned char operation:6; @@ -130,12 +148,10 @@ chr_t search_arg; __u8 sector; __u16 length; -} __attribute__ ((packed)) +} __attribute__ ((packed)) LO_eckd_data_t; - LO_eckd_data_t; -typedef - struct dasd_eckd_characteristics_t { +typedef struct dasd_eckd_characteristics_t { __u16 cu_type; struct { unsigned char support:2; @@ -154,7 +170,8 @@ unsigned char reserved2:4; unsigned char reserved3:8; unsigned char defect_wr:1; - unsigned char reserved4:2; + unsigned char XRC_supported:1; + unsigned char reserved4:1; unsigned char striping:1; unsigned char reserved5:4; unsigned char cfw:1; @@ -205,9 +222,7 @@ __u8 factor8; __u8 reserved2[3]; __u8 reserved3[10]; -} __attribute__ ((packed)) - - dasd_eckd_characteristics_t; +} __attribute__ ((packed)) dasd_eckd_characteristics_t; typedef struct dasd_eckd_confdata_t { struct { @@ -324,9 +339,20 @@ __u8 log_dev_address; unsigned char reserved2[12]; } __attribute__ ((packed)) neq; -} __attribute__ ((packed)) +} __attribute__ ((packed)) dasd_eckd_confdata_t; + +/* + * Perform Subsystem Function - Prepare for Read Subsystem Data + */ +typedef struct dasd_psf_prssd_data_t { + unsigned char order; + unsigned char flags; + unsigned char reserved[4]; + unsigned char suborder; + unsigned char varies[9]; +} __attribute__((packed)) dasd_psf_prssd_data_t; + - dasd_eckd_confdata_t; int dasd_eckd_init (void); void dasd_eckd_cleanup (void); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_fba.c linux.21rc1-ac2/drivers/s390/block/dasd_fba.c --- linux.21rc1/drivers/s390/block/dasd_fba.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_fba.c 2003-04-25 13:49:34.000000000 +0100 @@ -1,9 +1,12 @@ - /* * File...........: linux/drivers/s390/block/dasd_fba.c * Author(s)......: Holger Smolinski * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 + * + * $Revision: 1.49 $ + * + * History of changes * fixed partition handling and HDIO_GETGEO */ @@ -38,6 +41,12 @@ #define DASD_FBA_CCW_LOCATE 0x43 #define DASD_FBA_CCW_DEFINE_EXTENT 0x63 +#ifdef MODULE +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12)) +MODULE_LICENSE("GPL"); +#endif +#endif + dasd_discipline_t dasd_fba_discipline; typedef struct @@ -123,35 +132,46 @@ dasd_fba_private_t *private; if (device == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "Null device pointer passed to characteristics checker\n"); + + MESSAGE (KERN_WARNING, "%s", + "Null device pointer passed to characteristics " + "checker"); + return -ENODEV; } device->private = kmalloc (sizeof (dasd_fba_private_t), GFP_KERNEL); + if (device->private == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "memory allocation failed for private data\n"); + + MESSAGE (KERN_WARNING, "%s", + "memory allocation failed for private data"); + rc = -ENOMEM; goto fail; } private = (dasd_fba_private_t *) device->private; rdc_data = (void *) &(private->rdc_data); rc = read_dev_chars (device->devinfo.irq, &rdc_data, 32); + if (rc) { - printk (KERN_WARNING PRINTK_HEADER - "Read device characteristics returned error %d\n", rc); + + MESSAGE (KERN_WARNING, + "Read device characteristics returned error %d", + rc); + goto fail; } - printk (KERN_INFO PRINTK_HEADER - "%04X on sch %d: %04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)\n", - device->devinfo.devno, device->devinfo.irq, - device->devinfo.sid_data.dev_type, - device->devinfo.sid_data.dev_model, - device->devinfo.sid_data.cu_type, - device->devinfo.sid_data.cu_model, - (private->rdc_data.blk_bdsa * - (private->rdc_data.blk_size >> 9)) >> 11, - private->rdc_data.blk_size); + + DEV_MESSAGE (KERN_INFO, device, + "%04X/%02X(CU:%04X/%02X) %dMB at(%d B/blk)", + device->devinfo.sid_data.dev_type, + device->devinfo.sid_data.dev_model, + device->devinfo.sid_data.cu_type, + device->devinfo.sid_data.cu_model, + ((private->rdc_data.blk_bdsa * + (private->rdc_data.blk_size >> 9)) >> 11), + private->rdc_data.blk_size); + goto out; fail: if ( rc ) { @@ -180,9 +200,11 @@ device->sizes.bp_block = bs; break; default: - printk (KERN_INFO PRINTK_HEADER - "/dev/%s (%04X): unknown blocksize %d\n", - device->name, device->devinfo.devno, bs); + + DEV_MESSAGE (KERN_INFO, device, + "unknown blocksize %d", + bs); + return -EMEDIUMTYPE; } device->sizes.s2b_shift = 0; /* bits to shift 512 to get a block */ @@ -247,8 +269,11 @@ { if (cqr->function == dasd_default_erp_action) return dasd_default_erp_postaction; - printk (KERN_WARNING PRINTK_HEADER - "unknown ERP action %p\n", cqr->function); + + MESSAGE (KERN_WARNING, + "unknown ERP action %p", + cqr->function); + return NULL; } @@ -265,14 +290,20 @@ struct buffer_head *bh; dasd_fba_private_t *private = (dasd_fba_private_t *) device->private; int byt_per_blk = device->sizes.bp_block; + unsigned long reloc_sector = req->sector + + device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect; if (req->cmd == READ) { rw_cmd = DASD_FBA_CCW_READ; } else if (req->cmd == WRITE) { rw_cmd = DASD_FBA_CCW_WRITE; } else { - PRINT_ERR ("Unknown command %d\n", req->cmd); - return NULL; + + MESSAGE (KERN_ERR, + "Unknown command %d\n", + req->cmd); + + return ERR_PTR(-EINVAL); } /* Build the request */ /* count hs to prevent errors, when bh smaller than block */ @@ -300,14 +331,14 @@ device); } if (!rw_cp) { - return NULL; + return ERR_PTR(-ENOMEM); } DE_data = rw_cp->data; LO_data = rw_cp->data + sizeof (DE_fba_data_t); ccw = rw_cp->cpaddr; if (define_extent (ccw, DE_data, req->cmd, byt_per_blk, - req->sector, req->nr_sectors, rw_cp, device)) { + reloc_sector, req->nr_sectors, rw_cp, device)) { goto clear_rw_cp; } ccw->flags |= CCW_FLAG_CC; @@ -363,11 +394,12 @@ } static int -dasd_fba_fill_info (dasd_device_t * device, dasd_information_t * info) +dasd_fba_fill_info (dasd_device_t * device, dasd_information2_t * info) { int rc = 0; info->label_block = 1; info->FBA_layout = 1; + info->format = DASD_FORMAT_LDL; info->characteristics_size = sizeof (dasd_fba_characteristics_t); memcpy (info->characteristics, &((dasd_fba_private_t *) device->private)->rdc_data, @@ -410,14 +442,18 @@ dump_sense:dasd_fba_dump_sense, int_handler:dasd_int_handler, fill_info:dasd_fba_fill_info, + list:LIST_HEAD_INIT(dasd_fba_discipline.list), }; int dasd_fba_init (void) { int rc = 0; - printk (KERN_INFO PRINTK_HEADER - "%s discipline initializing\n", dasd_fba_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline initializing", + dasd_fba_discipline.name); + ASCEBC (dasd_fba_discipline.ebcname, 4); dasd_discipline_add (&dasd_fba_discipline); #ifdef CONFIG_DASD_DYNAMIC @@ -426,12 +462,15 @@ for (i = 0; i < sizeof (dasd_fba_known_devices) / sizeof (devreg_t); i++) { - printk (KERN_INFO PRINTK_HEADER - "We are interested in: Dev %04X/%02X @ CU %04X/%02x\n", - dasd_fba_known_devices[i].ci.hc.dtype, - dasd_fba_known_devices[i].ci.hc.dmode, - dasd_fba_known_devices[i].ci.hc.ctype, - dasd_fba_known_devices[i].ci.hc.cmode); + + MESSAGE (KERN_INFO, + "We are interested in: " + "Dev %04X/%02X @ CU %04X/%02x", + dasd_fba_known_devices[i].ci.hc.dtype, + dasd_fba_known_devices[i].ci.hc.dmode, + dasd_fba_known_devices[i].ci.hc.ctype, + dasd_fba_known_devices[i].ci.hc.cmode); + s390_device_register (&dasd_fba_known_devices[i]); } } @@ -441,8 +480,11 @@ void dasd_fba_cleanup( void ) { - printk ( KERN_INFO PRINTK_HEADER - "%s discipline cleaning up\n", dasd_fba_discipline.name); + + MESSAGE (KERN_INFO, + "%s discipline cleaning up", + dasd_fba_discipline.name); + #ifdef CONFIG_DASD_DYNAMIC { int i; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_fba.h linux.21rc1-ac2/drivers/s390/block/dasd_fba.h --- linux.21rc1/drivers/s390/block/dasd_fba.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_fba.h 2003-04-25 13:49:34.000000000 +0100 @@ -1,3 +1,15 @@ +/* + * File...........: linux/drivers/s390/block/dasd_fba.h + * Author(s)......: Holger Smolinski + * Horst Hummel + * Bugreports.to..: + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 + * + * $Revision: 1.6 $ + * + * History of changes + * + */ #ifndef DASD_FBA_H #define DASD_FBA_H diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/block/dasd_int.h linux.21rc1-ac2/drivers/s390/block/dasd_int.h --- linux.21rc1/drivers/s390/block/dasd_int.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/block/dasd_int.h 2003-04-25 13:49:34.000000000 +0100 @@ -1,9 +1,12 @@ /* - * File...........: linux/drivers/s390/block/dasd.c + * File...........: linux/drivers/s390/block/dasd_int.h * Author(s)......: Holger Smolinski + * Horst Hummel * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * + * $Revision: 1.26 $ + * * History of changes (starts July 2000) * 02/01/01 added dynamic registration of ioctls */ @@ -11,8 +14,6 @@ #ifndef DASD_INT_H #define DASD_INT_H -#define DASD_API_VERSION 0 - #include #define CONFIG_DASD_DYNAMIC @@ -28,14 +29,19 @@ #define DASD_FORMAT_INTENS_WRITE_RECZERO 0x01 #define DASD_FORMAT_INTENS_WRITE_HOMEADR 0x02 -#define DASD_STATE_DEL -1 -#define DASD_STATE_NEW 0 -#define DASD_STATE_KNOWN 1 -#define DASD_STATE_ACCEPT 2 -#define DASD_STATE_INIT 3 -#define DASD_STATE_READY 4 -#define DASD_STATE_ONLINE 5 - +#define DASD_STATE_DEL -1 /* "unknown" */ +#define DASD_STATE_NEW 0 /* memory for dasd_device_t and lowmem ccw/idals allocated */ +#define DASD_STATE_BOXED 1 /* boxed dasd could not be analysed "plugged" */ +#define DASD_STATE_KNOWN 2 /* major_info/devinfo/discipline/devfs-'device'/gendisk - "detected" */ +#define DASD_STATE_ACCEPT 3 /* irq requested - "accepted" */ +#define DASD_STATE_INIT 4 /* init_cqr started - "busy" */ +#define DASD_STATE_READY 5 /* init finished - "fenced(plugged)" */ +#define DASD_STATE_ONLINE 6 /* unplugged "active" */ + +#define DASD_HOTPLUG_EVENT_ADD 0 +#define DASD_HOTPLUG_EVENT_REMOVE 1 +#define DASD_HOTPLUG_EVENT_PARTCHK 2 +#define DASD_HOTPLUG_EVENT_PARTREMOVE 3 #define DASD_FORMAT_INTENS_WRITE_RECZERO 0x01 #define DASD_FORMAT_INTENS_WRITE_HOMEADR 0x02 @@ -61,7 +67,9 @@ #include #include -/* Kernel Version Compatibility section */ +/******************************************************************************** + * SECTION: Kernel Version Compatibility section + ********************************************************************************/ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) typedef struct request *request_queue_t; #define block_device_operations file_operations @@ -102,6 +110,7 @@ *q = req->next; req->next = NULL; } + #else #define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \ do { \ @@ -124,9 +133,12 @@ { blkdev_dequeue_request (req); } -#endif +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) */ + +/******************************************************************************** + * SECTION: Type definitions + ********************************************************************************/ -/* dasd_range_t are used for dynamic device att-/detachment */ typedef struct dasd_devreg_t { devreg_t devreg; /* the devreg itself */ /* build a linked list of devregs, needed for cleanup */ @@ -153,27 +165,6 @@ #define DASD_SENSE_BIT_2 0x20 #define DASD_SENSE_BIT_3 0x10 -#define check_then_set(where,from,to) \ -do { \ - if ((*(where)) != (from) ) { \ - printk (KERN_ERR PRINTK_HEADER "was %d\n", *(where)); \ - BUG(); \ - } \ - (*(where)) = (to); \ -} while (0) - -#define DASD_MESSAGE(d_loglevel,d_device,d_string,d_args...)\ -do { \ - int d_devno = d_device->devinfo.devno; \ - int d_irq = d_device->devinfo.irq; \ - char *d_name = d_device->name; \ - int d_major = MAJOR(d_device->kdev); \ - int d_minor = MINOR(d_device->kdev); \ - printk(d_loglevel PRINTK_HEADER \ - "/dev/%s(%d:%d),%04x@0x%x:" \ - d_string "\n",d_name,d_major,d_minor,d_devno,d_irq,d_args ); \ -} while(0) - /* * struct dasd_sizes_t * represents all data needed to access dasd with properly set up sectors @@ -196,68 +187,164 @@ ccw_req_t *tail; } dasd_chanq_t; -#define DASD_DEVICE_FORMAT_STRING "Device: %p" -#define DASD_DEVICE_DEBUG_EVENT(d_level, d_device, d_str, d_data...)\ -do {\ - if ( d_device->debug_area != NULL )\ - debug_sprintf_event(d_device->debug_area,d_level,\ - DASD_DEVICE_FORMAT_STRING d_str "\n",\ - d_device, d_data);\ +/* + * struct dasd_lowmem_t + * represents a queue of pages for lowmem request + */ +typedef struct { + struct list_head list; +} dasd_lowmem_t; + +#define DASD_LOWMEM_PAGES 2 /* # of lowmem pages per device (min 2) */ + +/******************************************************************************** + * SECTION: MACROS + ********************************************************************************/ + +/* + * CHECK_THEN_SET + * + * Change 'where' value from 'from' to 'to'. + ' BUG if the 'from' value doesn't match. + */ +#define check_then_set(where,from,to) \ +do { \ + if ((*(where)) != (from) ) { \ + printk (KERN_ERR PRINTK_HEADER "was %d\n", *(where)); \ + BUG(); \ + } \ + (*(where)) = (to); \ } while(0) -#define DASD_DEVICE_DEBUG_EXCEPTION(d_level, d_device, d_str, d_data...)\ -do {\ - if ( d_device->debug_area != NULL )\ - debug_sprintf_exception(d_device->debug_area,d_level,\ - DASD_DEVICE_FORMAT_STRING d_str "\n",\ - d_device, d_data);\ + + +/******************************************************************************** + * SECION: MACROs for klogd and s390 debug feature (dbf) + ********************************************************************************/ + +#define DBF_DEV_EVENT(d_level, d_device, d_str, d_data...) \ +do { \ + if (d_device->debug_area != NULL) \ + debug_sprintf_event(d_device->debug_area, \ + d_level, \ + d_str "\n", \ + d_data); \ +} while(0) + +#define DBF_DEV_EXC(d_level, d_device, d_str, d_data...) \ +do { \ + if (d_device->debug_area != NULL) \ + debug_sprintf_exception(d_device->debug_area, \ + d_level, \ + d_str "\n", \ + d_data); \ +} while(0) + +#define DBF_EVENT(d_level, d_str, d_data...)\ +do { \ + if (dasd_debug_area != NULL) \ + debug_sprintf_event(dasd_debug_area, \ + d_level,\ + d_str "\n", \ + d_data); \ +} while(0) + +#define DBF_EXC(d_level, d_str, d_data...)\ +do { \ + if (dasd_debug_area != NULL) \ + debug_sprintf_exception(dasd_debug_area, \ + d_level,\ + d_str "\n", \ + d_data); \ } while(0) -#define DASD_DRIVER_FORMAT_STRING "Driver: <[%p]>" -#define DASD_DRIVER_DEBUG_EVENT(d_level, d_fn, d_str, d_data...)\ -do {\ - if ( dasd_debug_area != NULL )\ - debug_sprintf_event(dasd_debug_area, d_level,\ - DASD_DRIVER_FORMAT_STRING #d_fn ":" d_str "\n",\ - d_fn, d_data);\ +/* definition of dbf debug levels */ +#define DBF_EMERG 0 /* system is unusable */ +#define DBF_ALERT 1 /* action must be taken immediately */ +#define DBF_CRIT 2 /* critical conditions */ +#define DBF_ERR 3 /* error conditions */ +#define DBF_WARNING 4 /* warning conditions */ +#define DBF_NOTICE 5 /* normal but significant condition */ +#define DBF_INFO 6 /* informational */ +#define DBF_DEBUG 6 /* debug-level messages */ + +/* messages to be written via klogd and dbf */ +#define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\ +do { \ + int d_devno = d_device->devinfo.devno; \ + int d_irq = d_device->devinfo.irq; \ + char *d_name = d_device->name; \ + int d_major = MAJOR(d_device->kdev); \ + int d_minor = MINOR(d_device->kdev); \ +\ + printk(d_loglevel PRINTK_HEADER \ + " /dev/%-7s(%3d:%3d),%04x@%02x: " \ + d_string "\n", \ + d_name, \ + d_major, \ + d_minor, \ + d_devno, \ + d_irq, \ + d_args); \ +\ + DBF_DEV_EVENT(DBF_ALERT, \ + d_device, \ + d_string, \ + d_args); \ } while(0) -#define DASD_DRIVER_DEBUG_EXCEPTION(d_level, d_fn, d_str, d_data...)\ -do {\ - if ( dasd_debug_area != NULL )\ - debug_sprintf_exception(dasd_debug_area, d_level,\ - DASD_DRIVER_FORMAT_STRING #d_fn ":" d_str "\n",\ - d_fn, d_data);\ + +#define MESSAGE(d_loglevel,d_string,d_args...)\ +do { \ + printk(d_loglevel PRINTK_HEADER \ + " " d_string "\n", \ + d_args); \ +\ + DBF_EVENT(DBF_ALERT, \ + d_string, \ + d_args); \ } while(0) struct dasd_device_t; struct request; -/* - * signatures for the functions of dasd_discipline_t +/******************************************************************************** + * SECTION: signatures for the functions of dasd_discipline_t * make typecasts much easier - */ -typedef ccw_req_t *(*dasd_erp_action_fn_t) (ccw_req_t * cqr); + ********************************************************************************/ + +typedef int (*dasd_ck_id_fn_t) (s390_dev_info_t *); +typedef int (*dasd_ck_characteristics_fn_t) (struct dasd_device_t *); +typedef int (*dasd_fill_geometry_fn_t) (struct dasd_device_t *, + struct hd_geometry *); +typedef int (*dasd_do_analysis_fn_t) (struct dasd_device_t *); +typedef int (*dasd_io_starter_fn_t) (ccw_req_t *); +typedef int (*dasd_io_stopper_fn_t) (ccw_req_t *); +typedef int (*dasd_info_fn_t) (struct dasd_device_t *, + dasd_information2_t *); +typedef int (*dasd_use_count_fn_t) (int); +typedef int (*dasd_set_attrib_fn_t) (struct dasd_device_t *, + struct attrib_data_t *); +typedef void (*dasd_int_handler_fn_t) (int irq, void *, + struct pt_regs *); +typedef char * (*dasd_dump_sense_fn_t) (struct dasd_device_t *, + ccw_req_t *); +typedef ccw_req_t *(*dasd_format_fn_t) (struct dasd_device_t *, + struct format_data_t *); +typedef ccw_req_t *(*dasd_init_analysis_fn_t ) (struct dasd_device_t *); +typedef ccw_req_t *(*dasd_cp_builder_fn_t) (struct dasd_device_t *, + struct request *); +typedef ccw_req_t *(*dasd_reserve_fn_t) (struct dasd_device_t *); +typedef ccw_req_t *(*dasd_release_fn_t) (struct dasd_device_t *); +typedef ccw_req_t *(*dasd_steal_lock_fn_t) (struct dasd_device_t *); +typedef ccw_req_t *(*dasd_merge_cp_fn_t) (struct dasd_device_t *); +typedef ccw_req_t *(*dasd_erp_action_fn_t) (ccw_req_t * cqr); typedef ccw_req_t *(*dasd_erp_postaction_fn_t) (ccw_req_t * cqr); +typedef ccw_req_t *(*dasd_read_stats_fn_t) (struct dasd_device_t *); -typedef int (*dasd_ck_id_fn_t) (s390_dev_info_t *); -typedef int (*dasd_ck_characteristics_fn_t) (struct dasd_device_t *); -typedef int (*dasd_fill_geometry_fn_t) (struct dasd_device_t *, struct hd_geometry *); -typedef ccw_req_t *(*dasd_format_fn_t) (struct dasd_device_t *, struct format_data_t *); -typedef ccw_req_t *(*dasd_init_analysis_fn_t) (struct dasd_device_t *); -typedef int (*dasd_do_analysis_fn_t) (struct dasd_device_t *); -typedef int (*dasd_io_starter_fn_t) (ccw_req_t *); -typedef int (*dasd_io_stopper_fn_t) (ccw_req_t *); -typedef void (*dasd_int_handler_fn_t)(int irq, void *, struct pt_regs *); -typedef dasd_era_t (*dasd_error_examine_fn_t) (ccw_req_t *, devstat_t * stat); -typedef dasd_erp_action_fn_t (*dasd_error_analyse_fn_t) (ccw_req_t *); -typedef dasd_erp_postaction_fn_t (*dasd_erp_analyse_fn_t) (ccw_req_t *); -typedef ccw_req_t *(*dasd_cp_builder_fn_t)(struct dasd_device_t *,struct request *); -typedef char *(*dasd_dump_sense_fn_t)(struct dasd_device_t *,ccw_req_t *); -typedef ccw_req_t *(*dasd_reserve_fn_t)(struct dasd_device_t *); -typedef ccw_req_t *(*dasd_release_fn_t)(struct dasd_device_t *); -typedef ccw_req_t *(*dasd_steal_lock_fn_t)(struct dasd_device_t *); -typedef ccw_req_t *(*dasd_merge_cp_fn_t)(struct dasd_device_t *); -typedef int (*dasd_info_fn_t) (struct dasd_device_t *, dasd_information_t *); -typedef int (*dasd_use_count_fn_t) (int); +typedef dasd_rssd_perf_stats_t * (*dasd_ret_stats_fn_t) (ccw_req_t *); +typedef dasd_era_t (*dasd_error_examine_fn_t) (ccw_req_t *, + devstat_t * stat); +typedef dasd_erp_action_fn_t (*dasd_error_analyse_fn_t) (ccw_req_t *); +typedef dasd_erp_postaction_fn_t (*dasd_erp_analyse_fn_t) (ccw_req_t *); /* * the dasd_discipline_t is @@ -270,31 +357,31 @@ char ebcname[8]; /* a name used for tagging and printks */ char name[8]; /* a name used for tagging and printks */ int max_blocks; /* maximum number of blocks to be chained */ - dasd_ck_id_fn_t id_check; /* to check sense data */ - dasd_ck_characteristics_fn_t check_characteristics; /* to check the characteristics */ - dasd_init_analysis_fn_t init_analysis; /* to start the analysis of the volume */ - dasd_do_analysis_fn_t do_analysis; /* to complete the analysis of the volume */ - dasd_fill_geometry_fn_t fill_geometry; /* to set up hd_geometry */ - dasd_io_starter_fn_t start_IO; - dasd_io_stopper_fn_t term_IO; - dasd_format_fn_t format_device; /* to format the device */ - dasd_error_examine_fn_t examine_error; - dasd_error_analyse_fn_t erp_action; - dasd_erp_analyse_fn_t erp_postaction; - dasd_cp_builder_fn_t build_cp_from_req; - dasd_dump_sense_fn_t dump_sense; - dasd_int_handler_fn_t int_handler; - dasd_reserve_fn_t reserve; - dasd_release_fn_t release; - dasd_steal_lock_fn_t steal_lock; - dasd_merge_cp_fn_t merge_cp; - dasd_info_fn_t fill_info; + dasd_ck_id_fn_t id_check; /* check sense data */ + dasd_ck_characteristics_fn_t check_characteristics; /* check the characteristics */ + dasd_init_analysis_fn_t init_analysis; /* start the analysis of the volume */ + dasd_do_analysis_fn_t do_analysis; /* complete the analysis of the volume */ + dasd_fill_geometry_fn_t fill_geometry; /* set up hd_geometry */ + dasd_io_starter_fn_t start_IO; + dasd_io_stopper_fn_t term_IO; + dasd_format_fn_t format_device; /* format the device */ + dasd_error_examine_fn_t examine_error; + dasd_error_analyse_fn_t erp_action; + dasd_erp_analyse_fn_t erp_postaction; + dasd_cp_builder_fn_t build_cp_from_req; + dasd_dump_sense_fn_t dump_sense; + dasd_int_handler_fn_t int_handler; + dasd_reserve_fn_t reserve; + dasd_release_fn_t release; + dasd_steal_lock_fn_t steal_lock; + dasd_merge_cp_fn_t merge_cp; + dasd_info_fn_t fill_info; + dasd_read_stats_fn_t read_stats; + dasd_ret_stats_fn_t ret_stats; /* return performance statistics */ + dasd_set_attrib_fn_t set_attrib; /* set attributes (cache operations */ struct list_head list; /* used for list of disciplines */ } dasd_discipline_t; -#define DASD_DEFAULT_FEATURES 0 -#define DASD_FEATURE_READONLY 1 - /* dasd_range_t are used for ordering the DASD devices */ typedef struct dasd_range_t { unsigned int from; /* first DASD in range */ @@ -326,7 +413,9 @@ struct dasd_chanq_t queue; wait_queue_head_t wait_q; request_queue_t *request_queue; - struct timer_list timer; + struct timer_list timer; /* used for start_IO */ + struct timer_list late_timer; /* to get late devices online */ + struct timer_list blocking_timer; /* used for ERP */ devstat_t dev_status; /* needed ONLY!! for request_irq */ dasd_sizes_t sizes; char name[16]; /* The name of the device in /dev */ @@ -340,35 +429,37 @@ dasd_profile_info_t profile; ccw_req_t *init_cqr; atomic_t plugged; - void* lowmem_cqr; - void* lowmem_ccws; - void* lowmem_idals; - void* lowmem_idal_ptr; + int accessible; /* set to !=0 if doing IO is permitted */ + struct list_head lowmem_pool; } dasd_device_t; -int dasd_init (void); -void dasd_discipline_add(dasd_discipline_t *); -void dasd_discipline_del(dasd_discipline_t *); -int dasd_start_IO (ccw_req_t *); -int dasd_term_IO (ccw_req_t *); -void dasd_int_handler (int , void *, struct pt_regs *); -ccw_req_t *dasd_default_erp_action (ccw_req_t *); -ccw_req_t *dasd_default_erp_postaction (ccw_req_t *); -inline void dasd_chanq_deq (dasd_chanq_t *, ccw_req_t *); -inline void dasd_chanq_enq (dasd_chanq_t *, ccw_req_t *); -inline void dasd_chanq_enq_head (dasd_chanq_t *, ccw_req_t *); -ccw_req_t *dasd_alloc_request (char *, int, int, dasd_device_t *); -void dasd_free_request (ccw_req_t *, dasd_device_t *); -int dasd_oper_handler (int irq, devreg_t * devreg); -void dasd_schedule_bh (dasd_device_t *); -int dasd_sleep_on_req(ccw_req_t*); -int dasd_set_normalized_cda ( ccw1_t * cp, unsigned long address, ccw_req_t* request, dasd_device_t* device ); -dasd_device_t * dasd_device_from_kdev (kdev_t kdev); + +int dasd_init (void); +void dasd_discipline_add (dasd_discipline_t *); +void dasd_discipline_del (dasd_discipline_t *); +int dasd_start_IO (ccw_req_t *); +int dasd_term_IO (ccw_req_t *); +void dasd_int_handler (int , void *, struct pt_regs *); +void dasd_free_request (ccw_req_t *, dasd_device_t *); +int dasd_oper_handler (int irq, devreg_t * devreg); +void dasd_schedule_bh (dasd_device_t *); +void dasd_schedule_bh_timed (unsigned long); +int dasd_sleep_on_req (ccw_req_t*); +int dasd_set_normalized_cda (ccw1_t * cp, unsigned long address, + ccw_req_t* request, + dasd_device_t* device ); +ccw_req_t * dasd_default_erp_action (ccw_req_t *); +ccw_req_t * dasd_default_erp_postaction (ccw_req_t *); +inline void dasd_chanq_deq (dasd_chanq_t *, ccw_req_t *); +inline void dasd_chanq_enq (dasd_chanq_t *, ccw_req_t *); +inline void dasd_chanq_enq_head (dasd_chanq_t *, ccw_req_t *); +ccw_req_t * dasd_alloc_request (char *, int, int, dasd_device_t *); +dasd_device_t * dasd_device_from_kdev (kdev_t kdev); extern debug_info_t *dasd_debug_area; extern int (*genhd_dasd_name) (char *, int, int, struct gendisk *); extern int (*genhd_dasd_ioctl) (struct inode *inp, struct file *filp, - unsigned int no, unsigned long data); + unsigned int no, unsigned long data); #endif /* __KERNEL__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/con3215.c linux.21rc1-ac2/drivers/s390/char/con3215.c --- linux.21rc1/drivers/s390/char/con3215.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/con3215.c 2003-04-25 13:50:50.000000000 +0100 @@ -462,30 +462,35 @@ if ((raw = req->info) == NULL) return; /* That shouldn't happen ... */ if (req->type == RAW3215_READ && raw->tty != NULL) { - char *cchar; + unsigned int cchar; tty = raw->tty; - count = 160 - req->residual; - if (MACHINE_IS_P390) { - slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE); - if (count > slen) - count = slen; - } else + count = 160 - req->residual; + if (MACHINE_IS_P390) { + slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE); + if (count > slen) + count = slen; + } else if (count >= TTY_FLIPBUF_SIZE - tty->flip.count) count = TTY_FLIPBUF_SIZE - tty->flip.count - 1; EBCASC(raw->inbuf, count); - if ((cchar = ctrlchar_handle(raw->inbuf, count, tty))) { - if (cchar == (char *)-1) - goto in_out; + cchar = ctrlchar_handle(raw->inbuf, count, tty); + switch (cchar & CTRLCHAR_MASK) { + case CTRLCHAR_SYSRQ: + break; + + case CTRLCHAR_CTRL: tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = *cchar; + *tty->flip.char_buf_ptr++ = cchar; tty_flip_buffer_push(raw->tty); - } else { + break; + + case CTRLCHAR_NONE: memcpy(tty->flip.char_buf_ptr, raw->inbuf, count); if (count < 2 || - (strncmp(raw->inbuf+count-2, "^n", 2) || + (strncmp(raw->inbuf+count-2, "^n", 2) && strncmp(raw->inbuf+count-2, "\252n", 2)) ) { /* don't add the auto \n */ tty->flip.char_buf_ptr[count] = '\n'; @@ -498,12 +503,12 @@ tty->flip.flag_buf_ptr += count; tty->flip.count += count; tty_flip_buffer_push(raw->tty); + break; } } else if (req->type == RAW3215_WRITE) { raw->count -= req->len; raw->written -= req->len; } -in_out: raw->flags &= ~RAW3215_WORKING; raw3215_free_req(req); /* check for empty wait */ @@ -823,7 +828,8 @@ * The console structure for the 3215 console */ static struct console con3215 = { - name: "tty3215", + name: "ttyS", + index: 0, write: con3215_write, device: con3215_device, unblank: con3215_unblank, @@ -897,7 +903,7 @@ raw3215_info *raw; raw = (raw3215_info *) tty->driver_data; - if (raw == NULL || tty->count > 1) + if (raw == NULL || atomic_read(&tty->count) > 1) return; tty->closing = 1; /* Shutdown the terminal */ @@ -1120,6 +1126,9 @@ */ void __init tty3215_init(void) { + /* Don't bother registering the tty if we already skipped the console */ + if (!CONSOLE_IS_3215) + return; /* * Initialize the tty_driver structure * Entries in tty3215_driver that are NOT initialized: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/ctrlchar.c linux.21rc1-ac2/drivers/s390/char/ctrlchar.c --- linux.21rc1/drivers/s390/char/ctrlchar.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/ctrlchar.c 2003-04-25 13:50:50.000000000 +0100 @@ -8,32 +8,30 @@ */ #include -#include -#include -#include - +#include #include +#include +#include -#include -#include -#include -#include -#include +#include "ctrlchar.h" #ifdef CONFIG_MAGIC_SYSRQ static int ctrlchar_sysrq_key; static struct tq_struct ctrlchar_tq; static void -ctrlchar_handle_sysrq(struct tty_struct *tty) { - handle_sysrq(ctrlchar_sysrq_key, NULL, NULL, tty); +ctrlchar_handle_sysrq(void *tty) +{ + handle_sysrq(ctrlchar_sysrq_key, NULL, NULL, (struct tty_struct*) tty); } #endif -void ctrlchar_init(void) { +void +ctrlchar_init(void) +{ #ifdef CONFIG_MAGIC_SYSRQ static int init_done = 0; - + if (init_done++) return; INIT_LIST_HEAD(&ctrlchar_tq.list); @@ -48,49 +46,43 @@ * @param buf Console input buffer. * @param len Length of valid data in buffer. * @param tty The tty struct for this console. - * @return NULL, if nothing matched, (char *)-1, if buffer contents - * should be ignored, otherwise pointer to char to be inserted. + * @return CTRLCHAR_NONE, if nothing matched, + * CTRLCHAR_SYSRQ, if sysrq was encountered + * otherwise char to be inserted logically or'ed + * with CTRLCHAR_CTRL */ -char *ctrlchar_handle(const char *buf, int len, struct tty_struct *tty) { - - static char ret; - +unsigned int +ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty) +{ if ((len < 2) || (len > 3)) - return NULL; + return CTRLCHAR_NONE; + /* hat is 0xb1 in codepage 037 (US etc.) and thus */ /* converted to 0x5e in ascii ('^') */ if ((buf[0] != '^') && (buf[0] != '\252')) - return NULL; - switch (buf[1]) { + return CTRLCHAR_NONE; + #ifdef CONFIG_MAGIC_SYSRQ - case '-': - if (len == 3) { - ctrlchar_sysrq_key = buf[2]; - ctrlchar_tq.data = tty; - queue_task(&ctrlchar_tq, &tq_immediate); - mark_bh(IMMEDIATE_BH); - return (char *)-1; - } - break; + /* racy */ + if (len == 3 && buf[1] == '-') { + ctrlchar_sysrq_key = buf[2]; + ctrlchar_tq.data = tty; + queue_task(&ctrlchar_tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + return CTRLCHAR_SYSRQ; + } #endif - case 'c': - if (len == 2) { - ret = INTR_CHAR(tty); - return &ret; - } - break; - case 'd': - if (len == 2) { - ret = EOF_CHAR(tty); - return &ret; - } - break; - case 'z': - if (len == 2) { - ret = SUSP_CHAR(tty); - return &ret; - } - break; + + if (len != 2) + return CTRLCHAR_NONE; + + switch (tolower(buf[1])) { + case 'c': + return INTR_CHAR(tty) | CTRLCHAR_CTRL; + case 'd': + return EOF_CHAR(tty) | CTRLCHAR_CTRL; + case 'z': + return SUSP_CHAR(tty) | CTRLCHAR_CTRL; } - return NULL; + return CTRLCHAR_NONE; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/ctrlchar.h linux.21rc1-ac2/drivers/s390/char/ctrlchar.h --- linux.21rc1/drivers/s390/char/ctrlchar.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/ctrlchar.h 2003-04-25 13:50:50.000000000 +0100 @@ -7,9 +7,15 @@ * */ -#include -#include #include +extern unsigned int +ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty); extern void ctrlchar_init(void); -extern char *ctrlchar_handle(const char *buf, int len, struct tty_struct *tty); + + +#define CTRLCHAR_NONE (1 << 8) +#define CTRLCHAR_CTRL (2 << 8) +#define CTRLCHAR_SYSRQ (3 << 8) + +#define CTRLCHAR_MASK (~0xffu) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc_con.c linux.21rc1-ac2/drivers/s390/char/hwc_con.c --- linux.21rc1/drivers/s390/char/hwc_con.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc_con.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,89 +0,0 @@ -/* - * drivers/s390/char/hwc_con.c - * HWC line mode console driver - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hwc_rw.h" - -#ifdef CONFIG_HWC_CONSOLE - -#define hwc_console_major 4 -#define hwc_console_minor 64 -#define hwc_console_name "console" - -void hwc_console_write (struct console *, const char *, unsigned int); -kdev_t hwc_console_device (struct console *); -void hwc_console_unblank (void); - -#define HWC_CON_PRINT_HEADER "hwc console driver: " - -struct console hwc_console = { - name: hwc_console_name, - write: hwc_console_write, - device: hwc_console_device, - unblank:hwc_console_unblank, - flags: CON_PRINTBUFFER, -}; - -void -hwc_console_write ( - struct console *console, - const char *message, - unsigned int count) -{ - - if (console->device (console) != hwc_console.device (&hwc_console)) { - - hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER - "hwc_console_write() called with wrong " - "device number"); - return; - } - hwc_write (0, message, count); -} - -kdev_t -hwc_console_device (struct console * c) -{ - return MKDEV (hwc_console_major, hwc_console_minor); -} - -void -hwc_console_unblank (void) -{ - hwc_unblank (); -} - -#endif - -void __init -hwc_console_init (void) -{ - if (!MACHINE_HAS_HWC) - return; - - if (hwc_init () == 0) { -#ifdef CONFIG_HWC_CONSOLE - - if (CONSOLE_IS_HWC) - register_console (&hwc_console); -#endif - } else - panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !"); - - return; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc_cpi.c linux.21rc1-ac2/drivers/s390/char/hwc_cpi.c --- linux.21rc1/drivers/s390/char/hwc_cpi.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc_cpi.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,211 +0,0 @@ - -/* - * Author: Martin Peschke - * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hwc_rw.h" -#include "hwc.h" - -#define CPI_RETRIES 3 -#define CPI_SLEEP_TICKS 50 - -#define CPI_LENGTH_SYSTEM_TYPE 8 -#define CPI_LENGTH_SYSTEM_NAME 8 -#define CPI_LENGTH_SYSPLEX_NAME 8 - -typedef struct { - _EBUF_HEADER - u8 id_format; - u8 reserved0; - u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; - u64 reserved1; - u8 system_name[CPI_LENGTH_SYSTEM_NAME]; - u64 reserved2; - u64 system_level; - u64 reserved3; - u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; - u8 reserved4[16]; -} __attribute__ ((packed)) - -cpi_evbuf_t; - -typedef struct _cpi_hwcb_t { - _HWCB_HEADER - cpi_evbuf_t cpi_evbuf; -} __attribute__ ((packed)) - -cpi_hwcb_t; - -cpi_hwcb_t *cpi_hwcb; - -static int __init cpi_module_init (void); -static void __exit cpi_module_exit (void); - -module_init (cpi_module_init); -module_exit (cpi_module_exit); - -MODULE_AUTHOR ( - "Martin Peschke, IBM Deutschland Entwicklung GmbH " - ""); - -MODULE_DESCRIPTION ( - "identify this operating system instance to the S/390 or zSeries hardware"); - -static char *system_name = NULL; -MODULE_PARM (system_name, "s"); -MODULE_PARM_DESC (system_name, "e.g. hostname - max. 8 characters"); - -static char *sysplex_name = NULL; -#ifdef ALLOW_SYSPLEX_NAME -MODULE_PARM (sysplex_name, "s"); -MODULE_PARM_DESC (sysplex_name, "if applicable - max. 8 characters"); -#endif - -static char *system_type = "LINUX"; - -hwc_request_t cpi_request = -{}; - -hwc_callback_t cpi_callback; - -static DECLARE_MUTEX_LOCKED (sem); - -static int __init -cpi_module_init (void) -{ - int retval; - int system_type_length; - int system_name_length; - int sysplex_name_length = 0; - int retries; - - if (!MACHINE_HAS_HWC) { - printk ("cpi: bug: hardware console not present\n"); - retval = -EINVAL; - goto out; - } - if (!system_type) { - printk ("cpi: bug: no system type specified\n"); - retval = -EINVAL; - goto out; - } - system_type_length = strlen (system_type); - if (system_type_length > CPI_LENGTH_SYSTEM_NAME) { - printk ("cpi: bug: system type has length of %i characters - " - "only %i characters supported\n", - system_type_length, - CPI_LENGTH_SYSTEM_TYPE); - retval = -EINVAL; - goto out; - } - if (!system_name) { - printk ("cpi: no system name specified\n"); - retval = -EINVAL; - goto out; - } - system_name_length = strlen (system_name); - if (system_name_length > CPI_LENGTH_SYSTEM_NAME) { - printk ("cpi: system name has length of %i characters - " - "only %i characters supported\n", - system_name_length, - CPI_LENGTH_SYSTEM_NAME); - retval = -EINVAL; - goto out; - } - if (sysplex_name) { - sysplex_name_length = strlen (sysplex_name); - if (sysplex_name_length > CPI_LENGTH_SYSPLEX_NAME) { - printk ("cpi: sysplex name has length of %i characters - " - "only %i characters supported\n", - sysplex_name_length, - CPI_LENGTH_SYSPLEX_NAME); - retval = -EINVAL; - goto out; - } - } - cpi_hwcb = kmalloc (sizeof (cpi_hwcb_t), GFP_KERNEL); - if (!cpi_hwcb) { - printk ("cpi: no storage to fulfill request\n"); - retval = -ENOMEM; - goto out; - } - memset (cpi_hwcb, 0, sizeof (cpi_hwcb_t)); - - cpi_hwcb->length = sizeof (cpi_hwcb_t); - cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t); - cpi_hwcb->cpi_evbuf.type = 0x0B; - - memset (cpi_hwcb->cpi_evbuf.system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); - memcpy (cpi_hwcb->cpi_evbuf.system_type, system_type, system_type_length); - HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE); - EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE); - - memset (cpi_hwcb->cpi_evbuf.system_name, ' ', CPI_LENGTH_SYSTEM_NAME); - memcpy (cpi_hwcb->cpi_evbuf.system_name, system_name, system_name_length); - HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME); - EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME); - - cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE; - - if (sysplex_name) { - memset (cpi_hwcb->cpi_evbuf.sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); - memcpy (cpi_hwcb->cpi_evbuf.sysplex_name, sysplex_name, sysplex_name_length); - HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - EBC_TOUPPER (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME); - } - cpi_request.block = cpi_hwcb; - cpi_request.word = HWC_CMDW_WRITEDATA; - cpi_request.callback = cpi_callback; - - for (retries = CPI_RETRIES; retries; retries--) { - retval = hwc_send (&cpi_request); - if (retval) { - - set_current_state (TASK_INTERRUPTIBLE); - schedule_timeout (CPI_SLEEP_TICKS); - } else { - - down (&sem); - - switch (cpi_hwcb->response_code) { - case 0x0020: - printk ("cpi: succeeded\n"); - break; - default: - printk ("cpi: failed with response code 0x%x\n", - cpi_hwcb->response_code); - } - goto free; - } - } - - printk ("cpi: failed (%i)\n", retval); - - free: - kfree (cpi_hwcb); - - out: - return retval; -} - -static void __exit -cpi_module_exit (void) -{ - printk ("cpi: exit\n"); -} - -void -cpi_callback (hwc_request_t * req) -{ - up (&sem); -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc.h linux.21rc1-ac2/drivers/s390/char/hwc.h --- linux.21rc1/drivers/s390/char/hwc.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,275 +0,0 @@ -/* - * drivers/s390/char/hwc.h - * - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke - * - * - * - */ - -#ifndef __HWC_H__ -#define __HWC_H__ - -#define HWC_EXT_INT_PARAM_ADDR 0xFFFFFFF8 -#define HWC_EXT_INT_PARAM_PEND 0x00000001 - -#define ET_OpCmd 0x01 -#define ET_Msg 0x02 -#define ET_StateChange 0x08 -#define ET_PMsgCmd 0x09 -#define ET_CntlProgOpCmd 0x20 -#define ET_CntlProgIdent 0x0B -#define ET_SigQuiesce 0x1D - -#define ET_OpCmd_Mask 0x80000000 -#define ET_Msg_Mask 0x40000000 -#define ET_StateChange_Mask 0x01000000 -#define ET_PMsgCmd_Mask 0x00800000 -#define ET_CtlProgOpCmd_Mask 0x00000001 -#define ET_CtlProgIdent_Mask 0x00200000 -#define ET_SigQuiesce_Mask 0x00000008 - -#define GMF_DOM 0x8000 -#define GMF_SndAlrm 0x4000 -#define GMF_HoldMsg 0x2000 - -#define LTF_CntlText 0x8000 -#define LTF_LabelText 0x4000 -#define LTF_DataText 0x2000 -#define LTF_EndText 0x1000 -#define LTF_PromptText 0x0800 - -#define HWC_COMMAND_INITIATED 0 -#define HWC_BUSY 2 -#define HWC_NOT_OPERATIONAL 3 - -#define hwc_cmdw_t u32; - -#define HWC_CMDW_READDATA 0x00770005 - -#define HWC_CMDW_WRITEDATA 0x00760005 - -#define HWC_CMDW_WRITEMASK 0x00780005 - -#define GDS_ID_MDSMU 0x1310 - -#define GDS_ID_MDSRouteInfo 0x1311 - -#define GDS_ID_AgUnWrkCorr 0x1549 - -#define GDS_ID_SNACondReport 0x1532 - -#define GDS_ID_CPMSU 0x1212 - -#define GDS_ID_RoutTargInstr 0x154D - -#define GDS_ID_OpReq 0x8070 - -#define GDS_ID_TextCmd 0x1320 - -#define GDS_KEY_SelfDefTextMsg 0x31 - -#define _HWCB_HEADER u16 length; \ - u8 function_code; \ - u8 control_mask[3]; \ - u16 response_code; - -#define _EBUF_HEADER u16 length; \ - u8 type; \ - u8 flags; \ - u16 _reserved; - -typedef struct { - _EBUF_HEADER -} __attribute__ ((packed)) - -evbuf_t; - -#define _MDB_HEADER u16 length; \ - u16 type; \ - u32 tag; \ - u32 revision_code; - -#define _GO_HEADER u16 length; \ - u16 type; \ - u32 domid; \ - u8 hhmmss_time[8]; \ - u8 th_time[3]; \ - u8 _reserved_0; \ - u8 dddyyyy_date[7]; \ - u8 _reserved_1; \ - u16 general_msg_flags; \ - u8 _reserved_2[10]; \ - u8 originating_system_name[8]; \ - u8 job_guest_name[8]; - -#define _MTO_HEADER u16 length; \ - u16 type; \ - u16 line_type_flags; \ - u8 alarm_control; \ - u8 _reserved[3]; - -typedef struct { - _GO_HEADER -} __attribute__ ((packed)) - -go_t; - -typedef struct { - go_t go; -} __attribute__ ((packed)) - -mdb_body_t; - -typedef struct { - _MDB_HEADER - mdb_body_t mdb_body; -} __attribute__ ((packed)) - -mdb_t; - -typedef struct { - _EBUF_HEADER - mdb_t mdb; -} __attribute__ ((packed)) - -msgbuf_t; - -typedef struct { - _HWCB_HEADER - msgbuf_t msgbuf; -} __attribute__ ((packed)) - -write_hwcb_t; - -typedef struct { - _MTO_HEADER -} __attribute__ ((packed)) - -mto_t; - -static write_hwcb_t write_hwcb_template = -{ - sizeof (write_hwcb_t), - 0x00, - { - 0x00, - 0x00, - 0x00 - }, - 0x0000, - { - sizeof (msgbuf_t), - ET_Msg, - 0x00, - 0x0000, - { - sizeof (mdb_t), - 0x0001, - 0xD4C4C240, - 0x00000001, - { - { - sizeof (go_t), - 0x0001 - - } - } - } - } -}; - -static mto_t mto_template = -{ - sizeof (mto_t), - 0x0004, - LTF_EndText, - 0x00 -}; - -typedef u32 _hwcb_mask_t; - -typedef struct { - _HWCB_HEADER - u16 _reserved; - u16 mask_length; - _hwcb_mask_t cp_receive_mask; - _hwcb_mask_t cp_send_mask; - _hwcb_mask_t hwc_receive_mask; - _hwcb_mask_t hwc_send_mask; -} __attribute__ ((packed)) - -init_hwcb_t; - -static init_hwcb_t init_hwcb_template = -{ - sizeof (init_hwcb_t), - 0x00, - { - 0x00, - 0x00, - 0x00 - }, - 0x0000, - 0x0000, - sizeof (_hwcb_mask_t), - ET_OpCmd_Mask | ET_PMsgCmd_Mask | - ET_StateChange_Mask | ET_SigQuiesce_Mask, - ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask -}; - -typedef struct { - _EBUF_HEADER - u8 validity_hwc_active_facility_mask:1; - u8 validity_hwc_receive_mask:1; - u8 validity_hwc_send_mask:1; - u8 validity_read_data_function_mask:1; - u16 _zeros:12; - u16 mask_length; - u64 hwc_active_facility_mask; - _hwcb_mask_t hwc_receive_mask; - _hwcb_mask_t hwc_send_mask; - u32 read_data_function_mask; -} __attribute__ ((packed)) - -statechangebuf_t; - -#define _GDS_VECTOR_HEADER u16 length; \ - u16 gds_id; - -#define _GDS_SUBVECTOR_HEADER u8 length; \ - u8 key; - -typedef struct { - _GDS_VECTOR_HEADER -} __attribute__ ((packed)) - -gds_vector_t; - -typedef struct { - _GDS_SUBVECTOR_HEADER -} __attribute__ ((packed)) - -gds_subvector_t; - -typedef struct { - _HWCB_HEADER -} __attribute__ ((packed)) - -read_hwcb_t; - -static read_hwcb_t read_hwcb_template = -{ - PAGE_SIZE, - 0x00, - { - 0x00, - 0x00, - 0x80 - } -}; - -#endif /* __HWC_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc_rw.c linux.21rc1-ac2/drivers/s390/char/hwc_rw.c --- linux.21rc1/drivers/s390/char/hwc_rw.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc_rw.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,2458 +0,0 @@ -/* - * drivers/s390/char/hwc_rw.c - * driver: reading from and writing to system console on S/390 via HWC - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke - * - * - * - * - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef MIN -#define MIN(a,b) (((amto_char_sum) - -#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number) - -#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost) - -#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost) - -#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost) - -#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next) - -#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB) - -#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB) - -#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB) - -#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB) - -#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB) - -#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB) - -#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB) - -#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB) - -#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB) - -#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB) - -#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB) - -#include "hwc.h" - -#define __HWC_RW_C__ -#include "hwc_rw.h" -#undef __HWC_RW_C__ - -static unsigned char _obuf[MAX_HWCB_ROOM]; - -static unsigned char - _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE))); - -typedef unsigned long kmem_pages_t; - -#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3) - -#define HWC_WTIMER_RUNS 1 -#define HWC_FLUSH 2 -#define HWC_INIT 4 -#define HWC_BROKEN 8 -#define HWC_INTERRUPT 16 -#define HWC_PTIMER_RUNS 32 - -static struct { - - hwc_ioctls_t ioctls; - - hwc_ioctls_t init_ioctls; - - unsigned char *hwcb_list_head; - - unsigned char *hwcb_list_tail; - - unsigned short int mto_number; - - unsigned int mto_char_sum; - - unsigned char hwcb_count; - - unsigned long kmem_start; - - unsigned long kmem_end; - - kmem_pages_t kmem_pages; - - unsigned char *obuf; - - unsigned short int obuf_cursor; - - unsigned short int obuf_count; - - unsigned short int obuf_start; - - unsigned char *page; - - u32 current_servc; - - unsigned char *current_hwcb; - - unsigned char write_nonprio:1; - unsigned char write_prio:1; - unsigned char read_nonprio:1; - unsigned char read_prio:1; - unsigned char read_statechange:1; - unsigned char sig_quiesce:1; - - unsigned char flags; - - hwc_high_level_calls_t *calls; - - hwc_request_t *request; - - spinlock_t lock; - - struct timer_list write_timer; - - struct timer_list poll_timer; -} hwc_data = -{ - { - }, - { - 8, - 0, - 80, - 1, - MAX_KMEM_PAGES, - MAX_KMEM_PAGES, - - 0, - - 0x6c - - }, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - _obuf, - 0, - 0, - 0, - _page, - 0, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - NULL, - NULL - -}; - -static unsigned long cr0 __attribute__ ((aligned (8))); -static unsigned long cr0_save __attribute__ ((aligned (8))); -static unsigned char psw_mask __attribute__ ((aligned (8))); - -static ext_int_info_t ext_int_info_hwc; - -#define DELAYED_WRITE 0 -#define IMMEDIATE_WRITE 1 - -static signed int do_hwc_write (int from_user, unsigned char *, - unsigned int, - unsigned char); - -unsigned char hwc_ip_buf[512]; - -static asmlinkage int -internal_print (char write_time, char *fmt,...) -{ - va_list args; - int i; - - va_start (args, fmt); - i = vsprintf (hwc_ip_buf, fmt, args); - va_end (args); - return do_hwc_write (0, hwc_ip_buf, i, write_time); -} - -int -hwc_printk (const char *fmt,...) -{ - va_list args; - int i; - unsigned long flags; - int retval; - - spin_lock_irqsave (&hwc_data.lock, flags); - - i = vsprintf (hwc_ip_buf, fmt, args); - va_end (args); - retval = do_hwc_write (0, hwc_ip_buf, i, IMMEDIATE_WRITE); - - spin_unlock_irqrestore (&hwc_data.lock, flags); - - return retval; -} - -#ifdef DUMP_HWCB_INPUT - -static void -dump_storage_area (unsigned char *area, unsigned short int count) -{ - unsigned short int index; - ioctl_nl_t old_final_nl; - - if (!area || !count) - return; - - old_final_nl = hwc_data.ioctls.final_nl; - hwc_data.ioctls.final_nl = 1; - - internal_print (DELAYED_WRITE, "\n%8x ", area); - - for (index = 0; index < count; index++) { - - if (area[index] <= 0xF) - internal_print (DELAYED_WRITE, "0%x", area[index]); - else - internal_print (DELAYED_WRITE, "%x", area[index]); - - if ((index & 0xF) == 0xF) - internal_print (DELAYED_WRITE, "\n%8x ", - &area[index + 1]); - else if ((index & 3) == 3) - internal_print (DELAYED_WRITE, " "); - } - - internal_print (IMMEDIATE_WRITE, "\n"); - - hwc_data.ioctls.final_nl = old_final_nl; -} -#endif - -static inline u32 -service_call ( - u32 hwc_command_word, - unsigned char hwcb[]) -{ - unsigned int condition_code = 1; - - __asm__ __volatile__ ("L 1, 0(%0) \n\t" - "LRA 2, 0(%1) \n\t" - ".long 0xB2200012 \n\t" - : - :"a" (&hwc_command_word), "a" (hwcb) - :"1", "2", "memory"); - - __asm__ __volatile__ ("IPM %0 \n\t" - "SRL %0, 28 \n\t" - :"=r" (condition_code)); - - return condition_code; -} - -static inline unsigned long -hwc_ext_int_param (void) -{ - u32 param; - - __asm__ __volatile__ ("L %0,128\n\t" - :"=r" (param)); - - return (unsigned long) param; -} - -static int -prepare_write_hwcb (void) -{ - write_hwcb_t *hwcb; - - if (!BUF_HWCB) - return -ENOMEM; - - BUF_HWCB_MTO = 0; - BUF_HWCB_CHAR = 0; - - hwcb = (write_hwcb_t *) BUF_HWCB; - - memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t)); - - return 0; -} - -static int -sane_write_hwcb (void) -{ - unsigned short int lost_msg; - unsigned int lost_char; - unsigned char lost_hwcb; - unsigned char *bad_addr; - unsigned long page; - int page_nr; - - if (!OUT_HWCB) - return -ENOMEM; - - if ((unsigned long) OUT_HWCB & 0xFFF) { - - bad_addr = OUT_HWCB; - -#ifdef DUMP_HWC_WRITE_LIST_ERROR - __asm__ ("LHI 1,0xe30\n\t" - "LRA 2,0(%0) \n\t" - "J .+0 \n\t" - : - : "a" (bad_addr) - : "1", "2"); -#endif - - hwc_data.kmem_pages = 0; - if ((unsigned long) BUF_HWCB & 0xFFF) { - - lost_hwcb = hwc_data.hwcb_count; - lost_msg = ALL_HWCB_MTO; - lost_char = ALL_HWCB_CHAR; - - OUT_HWCB = NULL; - BUF_HWCB = NULL; - ALL_HWCB_MTO = 0; - ALL_HWCB_CHAR = 0; - hwc_data.hwcb_count = 0; - } else { - - lost_hwcb = hwc_data.hwcb_count - 1; - lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO; - lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR; - OUT_HWCB = BUF_HWCB; - ALL_HWCB_MTO = BUF_HWCB_MTO; - ALL_HWCB_CHAR = BUF_HWCB_CHAR; - hwc_data.hwcb_count = 1; - page = (unsigned long) BUF_HWCB; - - if (page >= hwc_data.kmem_start && - page <= hwc_data.kmem_end) { - - page_nr = (int) - ((page - hwc_data.kmem_start) >> 12); - set_bit (page_nr, &hwc_data.kmem_pages); - } - } - - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "found invalid HWCB at address 0x%lx. List corrupted. " - "Lost %i HWCBs with %i characters within up to %i " - "messages. Saved %i HWCB with last %i characters i" - "within up to %i messages.\n", - (unsigned long) bad_addr, - lost_hwcb, lost_char, lost_msg, - hwc_data.hwcb_count, - ALL_HWCB_CHAR, ALL_HWCB_MTO); - } - return 0; -} - -static int -reuse_write_hwcb (void) -{ - int retval; - - if (hwc_data.hwcb_count < 2) -#ifdef DUMP_HWC_WRITE_LIST_ERROR - __asm__ ("LHI 1,0xe31\n\t" - "LRA 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "J .+0 \n\t" - : - : "a" (BUF_HWCB), "a" (OUT_HWCB) - : "1", "2", "3"); -#else - return -EPERM; -#endif - - if (hwc_data.current_hwcb == OUT_HWCB) { - - if (hwc_data.hwcb_count > 2) { - - BUF_HWCB_NEXT = OUT_HWCB_NEXT; - - BUF_HWCB = OUT_HWCB_NEXT; - - OUT_HWCB_NEXT = BUF_HWCB_NEXT; - - BUF_HWCB_NEXT = NULL; - } - } else { - - BUF_HWCB_NEXT = OUT_HWCB; - - BUF_HWCB = OUT_HWCB; - - OUT_HWCB = OUT_HWCB_NEXT; - - BUF_HWCB_NEXT = NULL; - } - - BUF_HWCB_TIMES_LOST += 1; - BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR; - BUF_HWCB_MTO_LOST += BUF_HWCB_MTO; - ALL_HWCB_MTO -= BUF_HWCB_MTO; - ALL_HWCB_CHAR -= BUF_HWCB_CHAR; - - retval = prepare_write_hwcb (); - - if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "reached my own limit of " - "allowed buffer space for output (%i HWCBs = %li " - "bytes), skipped content of oldest HWCB %i time(s) " - "(%i lines = %i characters)\n", - hwc_data.ioctls.max_hwcb, - hwc_data.ioctls.max_hwcb * PAGE_SIZE, - BUF_HWCB_TIMES_LOST, - BUF_HWCB_MTO_LOST, - BUF_HWCB_CHAR_LOST); - else - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "page allocation failed, " - "could not expand buffer for output (currently in " - "use: %i HWCBs = %li bytes), skipped content of " - "oldest HWCB %i time(s) (%i lines = %i characters)\n", - hwc_data.hwcb_count, - hwc_data.hwcb_count * PAGE_SIZE, - BUF_HWCB_TIMES_LOST, - BUF_HWCB_MTO_LOST, - BUF_HWCB_CHAR_LOST); - - return retval; -} - -static int -allocate_write_hwcb (void) -{ - unsigned char *page; - int page_nr; - - if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) - return -ENOMEM; - - page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES); - if (page_nr < hwc_data.ioctls.kmem_hwcb) { - - page = (unsigned char *) - (hwc_data.kmem_start + (page_nr << 12)); - set_bit (page_nr, &hwc_data.kmem_pages); - } else - page = (unsigned char *) __get_free_page (GFP_ATOMIC | GFP_DMA); - - if (!page) - return -ENOMEM; - - if (!OUT_HWCB) - OUT_HWCB = page; - else - BUF_HWCB_NEXT = page; - - BUF_HWCB = page; - - BUF_HWCB_NEXT = NULL; - - hwc_data.hwcb_count++; - - prepare_write_hwcb (); - - BUF_HWCB_TIMES_LOST = 0; - BUF_HWCB_MTO_LOST = 0; - BUF_HWCB_CHAR_LOST = 0; - -#ifdef BUFFER_STRESS_TEST - - internal_print ( - DELAYED_WRITE, - "*** " HWC_RW_PRINT_HEADER - "page #%i at 0x%x for buffering allocated. ***\n", - hwc_data.hwcb_count, page); - -#endif - - return 0; -} - -static int -release_write_hwcb (void) -{ - unsigned long page; - int page_nr; - - if (!hwc_data.hwcb_count) - return -ENODATA; - - if (hwc_data.hwcb_count == 1) { - - prepare_write_hwcb (); - - ALL_HWCB_CHAR = 0; - ALL_HWCB_MTO = 0; - BUF_HWCB_TIMES_LOST = 0; - BUF_HWCB_MTO_LOST = 0; - BUF_HWCB_CHAR_LOST = 0; - } else { - page = (unsigned long) OUT_HWCB; - - ALL_HWCB_MTO -= OUT_HWCB_MTO; - ALL_HWCB_CHAR -= OUT_HWCB_CHAR; - hwc_data.hwcb_count--; - - OUT_HWCB = OUT_HWCB_NEXT; - - if (page >= hwc_data.kmem_start && - page <= hwc_data.kmem_end) { - /*memset((void *) page, 0, PAGE_SIZE); */ - - page_nr = (int) ((page - hwc_data.kmem_start) >> 12); - clear_bit (page_nr, &hwc_data.kmem_pages); - } else - free_page (page); -#ifdef BUFFER_STRESS_TEST - - internal_print ( - DELAYED_WRITE, - "*** " HWC_RW_PRINT_HEADER - "page at 0x%x released, %i pages still in use ***\n", - page, hwc_data.hwcb_count); - -#endif - } - return 0; -} - -static int -add_mto ( - unsigned char *message, - unsigned short int count) -{ - unsigned short int mto_size; - write_hwcb_t *hwcb; - mto_t *mto; - void *dest; - - if (!BUF_HWCB) - return -ENOMEM; - - if (BUF_HWCB == hwc_data.current_hwcb) - return -ENOMEM; - - mto_size = sizeof (mto_t) + count; - - hwcb = (write_hwcb_t *) BUF_HWCB; - - if ((MAX_HWCB_ROOM - hwcb->length) < mto_size) - return -ENOMEM; - - mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length); - - memcpy (mto, &mto_template, sizeof (mto_t)); - - dest = (void *) (((unsigned long) mto) + sizeof (mto_t)); - - memcpy (dest, message, count); - - mto->length += count; - - hwcb->length += mto_size; - hwcb->msgbuf.length += mto_size; - hwcb->msgbuf.mdb.length += mto_size; - - BUF_HWCB_MTO++; - ALL_HWCB_MTO++; - BUF_HWCB_CHAR += count; - ALL_HWCB_CHAR += count; - - return count; -} - -static int write_event_data_1 (void); - -static void -do_poll_hwc (unsigned long data) -{ - unsigned long flags; - - spin_lock_irqsave (&hwc_data.lock, flags); - - write_event_data_1 (); - - spin_unlock_irqrestore (&hwc_data.lock, flags); -} - -void -start_poll_hwc (void) -{ - init_timer (&hwc_data.poll_timer); - hwc_data.poll_timer.function = do_poll_hwc; - hwc_data.poll_timer.data = (unsigned long) NULL; - hwc_data.poll_timer.expires = jiffies + 2 * HZ; - add_timer (&hwc_data.poll_timer); - hwc_data.flags |= HWC_PTIMER_RUNS; -} - -static int -write_event_data_1 (void) -{ - unsigned short int condition_code; - int retval; - write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB; - - if ((!hwc_data.write_prio) && - (!hwc_data.write_nonprio) && - hwc_data.read_statechange) - return -EOPNOTSUPP; - - if (hwc_data.current_servc) - return -EBUSY; - - retval = sane_write_hwcb (); - if (retval < 0) - return -EIO; - - if (!OUT_HWCB_MTO) - return -ENODATA; - - if (!hwc_data.write_nonprio && hwc_data.write_prio) - hwcb->msgbuf.type = ET_PMsgCmd; - else - hwcb->msgbuf.type = ET_Msg; - - condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB); - -#ifdef DUMP_HWC_WRITE_ERROR - if (condition_code != HWC_COMMAND_INITIATED) - __asm__ ("LHI 1,0xe20\n\t" - "L 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "J .+0 \n\t" - : - : "a" (&condition_code), "a" (OUT_HWCB) - : "1", "2", "3"); -#endif - - switch (condition_code) { - case HWC_COMMAND_INITIATED: - hwc_data.current_servc = HWC_CMDW_WRITEDATA; - hwc_data.current_hwcb = OUT_HWCB; - retval = condition_code; - break; - case HWC_BUSY: - retval = -EBUSY; - break; - case HWC_NOT_OPERATIONAL: - start_poll_hwc (); - default: - retval = -EIO; - } - - return retval; -} - -static void -flush_hwcbs (void) -{ - while (hwc_data.hwcb_count > 1) - release_write_hwcb (); - - release_write_hwcb (); - - hwc_data.flags &= ~HWC_FLUSH; -} - -static int -write_event_data_2 (u32 ext_int_param) -{ - write_hwcb_t *hwcb; - int retval = 0; - -#ifdef DUMP_HWC_WRITE_ERROR - if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR) - != (unsigned long) hwc_data.current_hwcb) { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "write_event_data_2 : " - "HWCB address does not fit " - "(expected: 0x%lx, got: 0x%lx).\n", - (unsigned long) hwc_data.current_hwcb, - ext_int_param); - return -EINVAL; - } -#endif - - hwcb = (write_hwcb_t *) OUT_HWCB; - -#ifdef DUMP_HWC_WRITE_LIST_ERROR - if (((unsigned char *) hwcb) != hwc_data.current_hwcb) { - __asm__ ("LHI 1,0xe22\n\t" - "LRA 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "LRA 4,0(%2)\n\t" - "LRA 5,0(%3)\n\t" - "J .+0 \n\t" - : - : "a" (OUT_HWCB), - "a" (hwc_data.current_hwcb), - "a" (BUF_HWCB), - "a" (hwcb) - : "1", "2", "3", "4", "5"); - } -#endif - -#ifdef DUMP_HWC_WRITE_ERROR - if (hwcb->response_code != 0x0020) { - __asm__ ("LHI 1,0xe21\n\t" - "LRA 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "LRA 4,0(%2)\n\t" - "LH 5,0(%3)\n\t" - "SRL 5,8\n\t" - "J .+0 \n\t" - : - : "a" (OUT_HWCB), "a" (hwc_data.current_hwcb), - "a" (BUF_HWCB), - "a" (&(hwc_data.hwcb_count)) - : "1", "2", "3", "4", "5"); - } -#endif - - switch (hwcb->response_code) { - case 0x0020: - - retval = OUT_HWCB_CHAR; - release_write_hwcb (); - break; - case 0x0040: - case 0x0340: - case 0x40F0: - if (!hwc_data.read_statechange) { - hwcb->response_code = 0; - start_poll_hwc (); - } - retval = -EIO; - break; - default: - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "write_event_data_2 : " - "failed operation " - "(response code: 0x%x " - "HWCB address: 0x%x).\n", - hwcb->response_code, - hwcb); - retval = -EIO; - } - - if (retval == -EIO) { - - hwcb->control_mask[0] = 0; - hwcb->control_mask[1] = 0; - hwcb->control_mask[2] = 0; - hwcb->response_code = 0; - } - hwc_data.current_servc = 0; - hwc_data.current_hwcb = NULL; - - if (hwc_data.flags & HWC_FLUSH) - flush_hwcbs (); - - return retval; -} - -static void -do_put_line ( - unsigned char *message, - unsigned short count) -{ - - if (add_mto (message, count) != count) { - - if (allocate_write_hwcb () < 0) - reuse_write_hwcb (); - -#ifdef DUMP_HWC_WRITE_LIST_ERROR - if (add_mto (message, count) != count) - __asm__ ("LHI 1,0xe32\n\t" - "LRA 2,0(%0)\n\t" - "L 3,0(%1)\n\t" - "LRA 4,0(%2)\n\t" - "LRA 5,0(%3)\n\t" - "J .+0 \n\t" - : - : "a" (message), "a" (&hwc_data.kmem_pages), - "a" (BUF_HWCB), "a" (OUT_HWCB) - : "1", "2", "3", "4", "5"); -#else - add_mto (message, count); -#endif - } -} - -static void -put_line ( - unsigned char *message, - unsigned short count) -{ - - if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_WTIMER_RUNS)) { - del_timer (&hwc_data.write_timer); - hwc_data.flags &= ~HWC_WTIMER_RUNS; - } - hwc_data.obuf_start += count; - - do_put_line (message, count); - - hwc_data.obuf_start -= count; -} - -static void -set_alarm (void) -{ - write_hwcb_t *hwcb; - - if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb)) - allocate_write_hwcb (); - - hwcb = (write_hwcb_t *) BUF_HWCB; - hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm; -} - -static void -hwc_write_timeout (unsigned long data) -{ - unsigned long flags; - - spin_lock_irqsave (&hwc_data.lock, flags); - - hwc_data.obuf_start = hwc_data.obuf_count; - if (hwc_data.obuf_count) - put_line (hwc_data.obuf, hwc_data.obuf_count); - hwc_data.obuf_start = 0; - - hwc_data.obuf_cursor = 0; - hwc_data.obuf_count = 0; - - write_event_data_1 (); - - spin_unlock_irqrestore (&hwc_data.lock, flags); -} - -static int -do_hwc_write ( - int from_user, - unsigned char *msg, - unsigned int count, - unsigned char write_time) -{ - unsigned int i_msg = 0; - unsigned short int spaces = 0; - unsigned int processed_characters = 0; - unsigned char ch; - unsigned short int obuf_count; - unsigned short int obuf_cursor; - unsigned short int obuf_columns; - - if (hwc_data.obuf_start) { - obuf_cursor = 0; - obuf_count = 0; - obuf_columns = MIN (hwc_data.ioctls.columns, - MAX_MESSAGE_SIZE - hwc_data.obuf_start); - } else { - obuf_cursor = hwc_data.obuf_cursor; - obuf_count = hwc_data.obuf_count; - obuf_columns = hwc_data.ioctls.columns; - } - - for (i_msg = 0; i_msg < count; i_msg++) { - if (from_user) - get_user (ch, msg + i_msg); - else - ch = msg[i_msg]; - - processed_characters++; - - if ((obuf_cursor == obuf_columns) && - - (ch != '\n') && - - (ch != '\t')) { - put_line (&hwc_data.obuf[hwc_data.obuf_start], - obuf_columns); - obuf_cursor = 0; - obuf_count = 0; - } - switch (ch) { - - case '\n': - - put_line (&hwc_data.obuf[hwc_data.obuf_start], - obuf_count); - obuf_cursor = 0; - obuf_count = 0; - break; - - case '\a': - - hwc_data.obuf_start += obuf_count; - set_alarm (); - hwc_data.obuf_start -= obuf_count; - - break; - - case '\t': - - do { - if (obuf_cursor < obuf_columns) { - hwc_data.obuf[hwc_data.obuf_start + - obuf_cursor] - = HWC_ASCEBC (' '); - obuf_cursor++; - } else - break; - } while (obuf_cursor % hwc_data.ioctls.width_htab); - - break; - - case '\f': - case '\v': - - spaces = obuf_cursor; - put_line (&hwc_data.obuf[hwc_data.obuf_start], - obuf_count); - obuf_count = obuf_cursor; - while (spaces) { - hwc_data.obuf[hwc_data.obuf_start + - obuf_cursor - spaces] - = HWC_ASCEBC (' '); - spaces--; - } - - break; - - case '\b': - - if (obuf_cursor) - obuf_cursor--; - break; - - case '\r': - - obuf_cursor = 0; - break; - - case 0x00: - - put_line (&hwc_data.obuf[hwc_data.obuf_start], - obuf_count); - obuf_cursor = 0; - obuf_count = 0; - goto out; - - default: - - if (isprint (ch)) - hwc_data.obuf[hwc_data.obuf_start + - obuf_cursor++] - = HWC_ASCEBC (ch); - } - if (obuf_cursor > obuf_count) - obuf_count = obuf_cursor; - } - - if (obuf_cursor) { - - if (hwc_data.obuf_start || - (hwc_data.ioctls.final_nl == 0)) { - - put_line (&hwc_data.obuf[hwc_data.obuf_start], - obuf_count); - obuf_cursor = 0; - obuf_count = 0; - } else { - - if (hwc_data.ioctls.final_nl > 0) { - - if (hwc_data.flags & HWC_WTIMER_RUNS) { - - mod_timer (&hwc_data.write_timer, - jiffies + hwc_data.ioctls.final_nl * HZ / 10); - } else { - - init_timer (&hwc_data.write_timer); - hwc_data.write_timer.function = - hwc_write_timeout; - hwc_data.write_timer.data = - (unsigned long) NULL; - hwc_data.write_timer.expires = - jiffies + - hwc_data.ioctls.final_nl * HZ / 10; - add_timer (&hwc_data.write_timer); - hwc_data.flags |= HWC_WTIMER_RUNS; - } - } else; - - } - } else; - - out: - - if (!hwc_data.obuf_start) { - hwc_data.obuf_cursor = obuf_cursor; - hwc_data.obuf_count = obuf_count; - } - if (write_time == IMMEDIATE_WRITE) - write_event_data_1 (); - - return processed_characters; -} - -signed int -hwc_write (int from_user, const unsigned char *msg, unsigned int count) -{ - unsigned long flags; - int retval; - - spin_lock_irqsave (&hwc_data.lock, flags); - - retval = do_hwc_write (from_user, (unsigned char *) msg, - count, IMMEDIATE_WRITE); - - spin_unlock_irqrestore (&hwc_data.lock, flags); - - return retval; -} - -unsigned int -hwc_chars_in_buffer (unsigned char flag) -{ - unsigned short int number = 0; - unsigned long flags; - - spin_lock_irqsave (&hwc_data.lock, flags); - - if (flag & IN_HWCB) - number += ALL_HWCB_CHAR; - - if (flag & IN_WRITE_BUF) - number += hwc_data.obuf_cursor; - - spin_unlock_irqrestore (&hwc_data.lock, flags); - - return number; -} - -static inline int -nr_setbits (kmem_pages_t arg) -{ - int i; - int nr = 0; - - for (i = 0; i < (sizeof (arg) << 3); i++) { - if (arg & 1) - nr++; - arg >>= 1; - } - - return nr; -} - -unsigned int -hwc_write_room (unsigned char flag) -{ - unsigned int number = 0; - unsigned long flags; - write_hwcb_t *hwcb; - - spin_lock_irqsave (&hwc_data.lock, flags); - - if (flag & IN_HWCB) { - - if (BUF_HWCB) { - hwcb = (write_hwcb_t *) BUF_HWCB; - number += MAX_HWCB_ROOM - hwcb->length; - } - number += (hwc_data.ioctls.kmem_hwcb - - nr_setbits (hwc_data.kmem_pages)) * - (MAX_HWCB_ROOM - - (sizeof (write_hwcb_t) + sizeof (mto_t))); - } - if (flag & IN_WRITE_BUF) - number += MAX_HWCB_ROOM - hwc_data.obuf_cursor; - - spin_unlock_irqrestore (&hwc_data.lock, flags); - - return number; -} - -void -hwc_flush_buffer (unsigned char flag) -{ - unsigned long flags; - - spin_lock_irqsave (&hwc_data.lock, flags); - - if (flag & IN_HWCB) { - if (hwc_data.current_servc != HWC_CMDW_WRITEDATA) - flush_hwcbs (); - else - hwc_data.flags |= HWC_FLUSH; - } - if (flag & IN_WRITE_BUF) { - hwc_data.obuf_cursor = 0; - hwc_data.obuf_count = 0; - } - spin_unlock_irqrestore (&hwc_data.lock, flags); -} - -unsigned short int -seperate_cases (unsigned char *buf, unsigned short int count) -{ - - unsigned short int i_in; - - unsigned short int i_out = 0; - - unsigned char _case = 0; - - for (i_in = 0; i_in < count; i_in++) { - - if (buf[i_in] == hwc_data.ioctls.delim) { - - if ((i_in + 1 < count) && - (buf[i_in + 1] == hwc_data.ioctls.delim)) { - - buf[i_out] = hwc_data.ioctls.delim; - - i_out++; - - i_in++; - - } else - _case = ~_case; - - } else { - - if (_case) { - - if (hwc_data.ioctls.tolower) - buf[i_out] = _ebc_toupper[buf[i_in]]; - - else - buf[i_out] = _ebc_tolower[buf[i_in]]; - - } else - buf[i_out] = buf[i_in]; - - i_out++; - } - } - - return i_out; -} - -#ifdef DUMP_HWCB_INPUT - -static int -gds_vector_name (u16 id, unsigned char name[]) -{ - int retval = 0; - - switch (id) { - case GDS_ID_MDSMU: - name = "Multiple Domain Support Message Unit"; - break; - case GDS_ID_MDSRouteInfo: - name = "MDS Routing Information"; - break; - case GDS_ID_AgUnWrkCorr: - name = "Agent Unit of Work Correlator"; - break; - case GDS_ID_SNACondReport: - name = "SNA Condition Report"; - break; - case GDS_ID_CPMSU: - name = "CP Management Services Unit"; - break; - case GDS_ID_RoutTargInstr: - name = "Routing and Targeting Instructions"; - break; - case GDS_ID_OpReq: - name = "Operate Request"; - break; - case GDS_ID_TextCmd: - name = "Text Command"; - break; - - default: - name = "unknown GDS variable"; - retval = -EINVAL; - } - - return retval; -} -#endif - -inline static gds_vector_t * -find_gds_vector ( - gds_vector_t * start, void *end, u16 id) -{ - gds_vector_t *vec; - gds_vector_t *retval = NULL; - - vec = start; - - while (((void *) vec) < end) { - if (vec->gds_id == id) { - -#ifdef DUMP_HWCB_INPUT - int retval_name; - unsigned char name[64]; - - retval_name = gds_vector_name (id, name); - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "%s at 0x%x up to 0x%x, length: %d", - name, - (unsigned long) vec, - ((unsigned long) vec) + vec->length - 1, - vec->length); - if (retval_name < 0) - internal_print ( - IMMEDIATE_WRITE, - ", id: 0x%x\n", - vec->gds_id); - else - internal_print ( - IMMEDIATE_WRITE, - "\n"); -#endif - - retval = vec; - break; - } - vec = (gds_vector_t *) (((unsigned long) vec) + vec->length); - } - - return retval; -} - -inline static gds_subvector_t * -find_gds_subvector ( - gds_subvector_t * start, void *end, u8 key) -{ - gds_subvector_t *subvec; - gds_subvector_t *retval = NULL; - - subvec = start; - - while (((void *) subvec) < end) { - if (subvec->key == key) { - retval = subvec; - break; - } - subvec = (gds_subvector_t *) - (((unsigned long) subvec) + subvec->length); - } - - return retval; -} - -inline static int -get_input (void *start, void *end) -{ - int count; - - count = ((unsigned long) end) - ((unsigned long) start); - - if (hwc_data.ioctls.tolower) - EBC_TOLOWER (start, count); - - if (hwc_data.ioctls.delim) - count = seperate_cases (start, count); - - HWC_EBCASC_STR (start, count); - - if (hwc_data.ioctls.echo) - do_hwc_write (0, start, count, IMMEDIATE_WRITE); - - if (hwc_data.calls != NULL) - if (hwc_data.calls->move_input != NULL) - (hwc_data.calls->move_input) (start, count); - - return count; -} - -inline static int -eval_selfdeftextmsg (gds_subvector_t * start, void *end) -{ - gds_subvector_t *subvec; - void *subvec_data; - void *subvec_end; - int retval = 0; - - subvec = start; - - while (((void *) subvec) < end) { - subvec = find_gds_subvector (subvec, end, 0x30); - if (!subvec) - break; - subvec_data = (void *) - (((unsigned long) subvec) + - sizeof (gds_subvector_t)); - subvec_end = (void *) - (((unsigned long) subvec) + subvec->length); - retval += get_input (subvec_data, subvec_end); - subvec = (gds_subvector_t *) subvec_end; - } - - return retval; -} - -inline static int -eval_textcmd (gds_subvector_t * start, void *end) -{ - gds_subvector_t *subvec; - gds_subvector_t *subvec_data; - void *subvec_end; - int retval = 0; - - subvec = start; - - while (((void *) subvec) < end) { - subvec = find_gds_subvector ( - subvec, end, GDS_KEY_SelfDefTextMsg); - if (!subvec) - break; - subvec_data = (gds_subvector_t *) - (((unsigned long) subvec) + - sizeof (gds_subvector_t)); - subvec_end = (void *) - (((unsigned long) subvec) + subvec->length); - retval += eval_selfdeftextmsg (subvec_data, subvec_end); - subvec = (gds_subvector_t *) subvec_end; - } - - return retval; -} - -inline static int -eval_cpmsu (gds_vector_t * start, void *end) -{ - gds_vector_t *vec; - gds_subvector_t *vec_data; - void *vec_end; - int retval = 0; - - vec = start; - - while (((void *) vec) < end) { - vec = find_gds_vector (vec, end, GDS_ID_TextCmd); - if (!vec) - break; - vec_data = (gds_subvector_t *) - (((unsigned long) vec) + sizeof (gds_vector_t)); - vec_end = (void *) (((unsigned long) vec) + vec->length); - retval += eval_textcmd (vec_data, vec_end); - vec = (gds_vector_t *) vec_end; - } - - return retval; -} - -inline static int -eval_mdsmu (gds_vector_t * start, void *end) -{ - gds_vector_t *vec; - gds_vector_t *vec_data; - void *vec_end; - int retval = 0; - - vec = find_gds_vector (start, end, GDS_ID_CPMSU); - if (vec) { - vec_data = (gds_vector_t *) - (((unsigned long) vec) + sizeof (gds_vector_t)); - vec_end = (void *) (((unsigned long) vec) + vec->length); - retval = eval_cpmsu (vec_data, vec_end); - } - return retval; -} - -static int -eval_evbuf (gds_vector_t * start, void *end) -{ - gds_vector_t *vec; - gds_vector_t *vec_data; - void *vec_end; - int retval = 0; - - vec = find_gds_vector (start, end, GDS_ID_MDSMU); - if (vec) { - vec_data = (gds_vector_t *) - (((unsigned long) vec) + sizeof (gds_vector_t)); - vec_end = (void *) (((unsigned long) vec) + vec->length); - retval = eval_mdsmu (vec_data, vec_end); - } - return retval; -} - -static inline int -eval_hwc_receive_mask (_hwcb_mask_t mask) -{ - - hwc_data.write_nonprio - = ((mask & ET_Msg_Mask) == ET_Msg_Mask); - - hwc_data.write_prio - = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask); - - if (hwc_data.write_prio || hwc_data.write_nonprio) { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can write messages\n"); - return 0; - } else { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can not write messages\n"); - return -1; - } -} - -static inline int -eval_hwc_send_mask (_hwcb_mask_t mask) -{ - - hwc_data.read_statechange - = ((mask & ET_StateChange_Mask) == ET_StateChange_Mask); - if (hwc_data.read_statechange) - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can read state change notifications\n"); - else - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can not read state change notifications\n"); - - hwc_data.sig_quiesce - = ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask); - if (hwc_data.sig_quiesce) - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can receive signal quiesce\n"); - else - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can not receive signal quiesce\n"); - - hwc_data.read_nonprio - = ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask); - if (hwc_data.read_nonprio) - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can read commands\n"); - - hwc_data.read_prio - = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask); - if (hwc_data.read_prio) - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can read priority commands\n"); - - if (hwc_data.read_prio || hwc_data.read_nonprio) { - return 0; - } else { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "can not read commands from operator\n"); - return -1; - } -} - -static int -eval_statechangebuf (statechangebuf_t * scbuf) -{ - int retval = 0; - - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "HWC state change detected\n"); - - if (scbuf->validity_hwc_active_facility_mask) { - - } - if (scbuf->validity_hwc_receive_mask) { - - if (scbuf->mask_length != 4) { -#ifdef DUMP_HWC_INIT_ERROR - __asm__ ("LHI 1,0xe50\n\t" - "LRA 2,0(%0)\n\t" - "J .+0 \n\t" - : - : "a" (scbuf) - : "1", "2"); -#endif - } else { - - retval += eval_hwc_receive_mask - (scbuf->hwc_receive_mask); - } - } - if (scbuf->validity_hwc_send_mask) { - - if (scbuf->mask_length != 4) { -#ifdef DUMP_HWC_INIT_ERROR - __asm__ ("LHI 1,0xe51\n\t" - "LRA 2,0(%0)\n\t" - "J .+0 \n\t" - : - : "a" (scbuf) - : "1", "2"); -#endif - } else { - - retval += eval_hwc_send_mask - (scbuf->hwc_send_mask); - } - } - if (scbuf->validity_read_data_function_mask) { - - } - return retval; -} - -#ifdef CONFIG_SMP -extern unsigned long cpu_online_map; -static volatile unsigned long cpu_quiesce_map; - -static void -do_load_quiesce_psw (void) -{ - psw_t quiesce_psw; - - clear_bit (smp_processor_id (), &cpu_quiesce_map); - if (smp_processor_id () == 0) { - - while (cpu_quiesce_map != 0) ; - - quiesce_psw.mask = _DW_PSW_MASK; - quiesce_psw.addr = 0xfff; - __load_psw (quiesce_psw); - } - signal_processor (smp_processor_id (), sigp_stop); -} - -static void -do_machine_quiesce (void) -{ - cpu_quiesce_map = cpu_online_map; - smp_call_function (do_load_quiesce_psw, NULL, 0, 0); - do_load_quiesce_psw (); -} - -#else -static void -do_machine_quiesce (void) -{ - psw_t quiesce_psw; - - quiesce_psw.mask = _DW_PSW_MASK; - queisce_psw.addr = 0xfff; - __load_psw (quiesce_psw); -} - -#endif - -static int -process_evbufs (void *start, void *end) -{ - int retval = 0; - evbuf_t *evbuf; - void *evbuf_end; - gds_vector_t *evbuf_data; - - evbuf = (evbuf_t *) start; - while (((void *) evbuf) < end) { - evbuf_data = (gds_vector_t *) - (((unsigned long) evbuf) + sizeof (evbuf_t)); - evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length); - switch (evbuf->type) { - case ET_OpCmd: - case ET_CntlProgOpCmd: - case ET_PMsgCmd: -#ifdef DUMP_HWCB_INPUT - - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "event buffer " - "at 0x%x up to 0x%x, length: %d\n", - (unsigned long) evbuf, - (unsigned long) (evbuf_end - 1), - evbuf->length); - dump_storage_area ((void *) evbuf, evbuf->length); -#endif - retval += eval_evbuf (evbuf_data, evbuf_end); - break; - case ET_StateChange: - retval += eval_statechangebuf - ((statechangebuf_t *) evbuf); - break; - case ET_SigQuiesce: - - _machine_restart = do_machine_quiesce; - _machine_halt = do_machine_quiesce; - _machine_power_off = do_machine_quiesce; - ctrl_alt_del (); - break; - default: - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: " - "unknown event buffer found, " - "type 0x%x", - evbuf->type); - retval = -ENOSYS; - } - evbuf = (evbuf_t *) evbuf_end; - } - return retval; -} - -static int -unconditional_read_1 (void) -{ - unsigned short int condition_code; - read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; - int retval; - -#if 0 - - if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio)) - return -EOPNOTSUPP; - - if (hwc_data.current_servc) - return -EBUSY; -#endif - - memset (hwcb, 0x00, PAGE_SIZE); - memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t)); - - condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page); - -#ifdef DUMP_HWC_READ_ERROR - if (condition_code == HWC_NOT_OPERATIONAL) - __asm__ ("LHI 1,0xe40\n\t" - "L 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "J .+0 \n\t" - : - : "a" (&condition_code), "a" (hwc_data.page) - : "1", "2", "3"); -#endif - - switch (condition_code) { - case HWC_COMMAND_INITIATED: - hwc_data.current_servc = HWC_CMDW_READDATA; - hwc_data.current_hwcb = hwc_data.page; - retval = condition_code; - break; - case HWC_BUSY: - retval = -EBUSY; - break; - default: - retval = -EIO; - } - - return retval; -} - -static int -unconditional_read_2 (u32 ext_int_param) -{ - read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page; - -#ifdef DUMP_HWC_READ_ERROR - if ((hwcb->response_code != 0x0020) && - (hwcb->response_code != 0x0220) && - (hwcb->response_code != 0x60F0) && - (hwcb->response_code != 0x62F0)) - __asm__ ("LHI 1,0xe41\n\t" - "LRA 2,0(%0)\n\t" - "L 3,0(%1)\n\t" - "J .+0\n\t" - : - : "a" (hwc_data.page), "a" (&(hwcb->response_code)) - : "1", "2", "3"); -#endif - - hwc_data.current_servc = 0; - hwc_data.current_hwcb = NULL; - - switch (hwcb->response_code) { - - case 0x0020: - case 0x0220: - return process_evbufs ( - (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)), - (void *) (((unsigned long) hwcb) + hwcb->length)); - - case 0x60F0: - case 0x62F0: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: " - "got interrupt and tried to read input, " - "but nothing found (response code=0x%x).\n", - hwcb->response_code); - return 0; - - case 0x0100: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: HWCB boundary violation - this " - "must not occur in a correct driver, please contact " - "author\n"); - return -EIO; - - case 0x0300: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: " - "insufficient HWCB length - this must not occur in a " - "correct driver, please contact author\n"); - return -EIO; - - case 0x01F0: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: " - "invalid command - this must not occur in a correct " - "driver, please contact author\n"); - return -EIO; - - case 0x40F0: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: invalid function code\n"); - return -EIO; - - case 0x70F0: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: invalid selection mask\n"); - return -EIO; - - case 0x0040: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: HWC equipment check\n"); - return -EIO; - - default: - internal_print ( - IMMEDIATE_WRITE, - HWC_RW_PRINT_HEADER - "unconditional read: invalid response code %x - this " - "must not occur in a correct driver, please contact " - "author\n", - hwcb->response_code); - return -EIO; - } -} - -static int -write_event_mask_1 (void) -{ - unsigned int condition_code; - int retval; - - condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page); - -#ifdef DUMP_HWC_INIT_ERROR - - if (condition_code == HWC_NOT_OPERATIONAL) - __asm__ ("LHI 1,0xe10\n\t" - "L 2,0(%0)\n\t" - "LRA 3,0(%1)\n\t" - "J .+0\n\t" - : - : "a" (&condition_code), "a" (hwc_data.page) - : "1", "2", "3"); -#endif - - switch (condition_code) { - case HWC_COMMAND_INITIATED: - hwc_data.current_servc = HWC_CMDW_WRITEMASK; - hwc_data.current_hwcb = hwc_data.page; - retval = condition_code; - break; - case HWC_BUSY: - retval = -EBUSY; - break; - default: - retval = -EIO; - } - - return retval; -} - -static int -write_event_mask_2 (u32 ext_int_param) -{ - init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page; - int retval = 0; - - if (hwcb->response_code != 0x0020) { -#ifdef DUMP_HWC_INIT_ERROR - __asm__ ("LHI 1,0xe11\n\t" - "LRA 2,0(%0)\n\t" - "L 3,0(%1)\n\t" - "J .+0\n\t" - : - : "a" (hwcb), "a" (&(hwcb->response_code)) - : "1", "2", "3"); -#else - retval = -1; -#endif - } else { - if (hwcb->mask_length != 4) { -#ifdef DUMP_HWC_INIT_ERROR - __asm__ ("LHI 1,0xe52\n\t" - "LRA 2,0(%0)\n\t" - "J .+0 \n\t" - : - : "a" (hwcb) - : "1", "2"); -#endif - } else { - retval += eval_hwc_receive_mask - (hwcb->hwc_receive_mask); - retval += eval_hwc_send_mask (hwcb->hwc_send_mask); - } - } - - hwc_data.current_servc = 0; - hwc_data.current_hwcb = NULL; - - return retval; -} - -static int -set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct) -{ - int retval = 0; - hwc_ioctls_t tmp; - - if (ioctls->width_htab > MAX_MESSAGE_SIZE) { - if (correct) - tmp.width_htab = MAX_MESSAGE_SIZE; - else - retval = -EINVAL; - } else - tmp.width_htab = ioctls->width_htab; - - tmp.echo = ioctls->echo; - - if (ioctls->columns > MAX_MESSAGE_SIZE) { - if (correct) - tmp.columns = MAX_MESSAGE_SIZE; - else - retval = -EINVAL; - } else - tmp.columns = ioctls->columns; - - tmp.final_nl = ioctls->final_nl; - - if (ioctls->max_hwcb < 2) { - if (correct) - tmp.max_hwcb = 2; - else - retval = -EINVAL; - } else - tmp.max_hwcb = ioctls->max_hwcb; - - tmp.tolower = ioctls->tolower; - - if (ioctls->kmem_hwcb > ioctls->max_hwcb) { - if (correct) - tmp.kmem_hwcb = ioctls->max_hwcb; - else - retval = -EINVAL; - } else - tmp.kmem_hwcb = ioctls->kmem_hwcb; - - if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) { - if (correct) - ioctls->kmem_hwcb = MAX_KMEM_PAGES; - else - retval = -EINVAL; - } - if (ioctls->kmem_hwcb < 2) { - if (correct) - ioctls->kmem_hwcb = 2; - else - retval = -EINVAL; - } - tmp.delim = ioctls->delim; - - if (!(retval < 0)) - hwc_data.ioctls = tmp; - - return retval; -} - -int -do_hwc_init (void) -{ - int retval; - - memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t)); - - do { - - retval = write_event_mask_1 (); - - if (retval == -EBUSY) { - - hwc_data.flags |= HWC_INIT; - - __ctl_store (cr0, 0, 0); - cr0_save = cr0; - cr0 |= 0x00000200; - cr0 &= 0xFFFFF3AC; - __ctl_load (cr0, 0, 0); - - asm volatile ("STOSM %0,0x01" - :"=m" (psw_mask)::"memory"); - - while (!(hwc_data.flags & HWC_INTERRUPT)) - barrier (); - - asm volatile ("STNSM %0,0xFE" - :"=m" (psw_mask)::"memory"); - - __ctl_load (cr0_save, 0, 0); - - hwc_data.flags &= ~HWC_INIT; - } - } while (retval == -EBUSY); - - if (retval == -EIO) { - hwc_data.flags |= HWC_BROKEN; - printk (HWC_RW_PRINT_HEADER "HWC not operational\n"); - } - return retval; -} - -void hwc_interrupt_handler (struct pt_regs *regs, __u16 code); - -int -hwc_init (void) -{ - int retval; - -#ifdef BUFFER_STRESS_TEST - - init_hwcb_t *hwcb; - int i; - -#endif - - if (register_early_external_interrupt (0x2401, hwc_interrupt_handler, - &ext_int_info_hwc) != 0) - panic ("Couldn't request external interrupts 0x2401"); - - spin_lock_init (&hwc_data.lock); - -#ifdef USE_VM_DETECTION - - if (MACHINE_IS_VM) { - - if (hwc_data.init_ioctls.columns > 76) - hwc_data.init_ioctls.columns = 76; - hwc_data.init_ioctls.tolower = 1; - if (!hwc_data.init_ioctls.delim) - hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER; - } else { - hwc_data.init_ioctls.tolower = 0; - hwc_data.init_ioctls.delim = 0; - } -#endif - retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1); - - hwc_data.kmem_start = (unsigned long) - alloc_bootmem_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE); - hwc_data.kmem_end = hwc_data.kmem_start + - hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1; - - retval = do_hwc_init (); - - ctl_set_bit (0, 9); - -#ifdef BUFFER_STRESS_TEST - - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "use %i bytes for buffering.\n", - hwc_data.ioctls.kmem_hwcb * PAGE_SIZE); - for (i = 0; i < 500; i++) { - hwcb = (init_hwcb_t *) BUF_HWCB; - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "This is stress test message #%i, free: %i bytes\n", - i, - MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t))); - } - -#endif - - return /*retval */ 0; -} - -signed int -hwc_register_calls (hwc_high_level_calls_t * calls) -{ - if (calls == NULL) - return -EINVAL; - - if (hwc_data.calls != NULL) - return -EBUSY; - - hwc_data.calls = calls; - return 0; -} - -signed int -hwc_unregister_calls (hwc_high_level_calls_t * calls) -{ - if (hwc_data.calls == NULL) - return -EINVAL; - - if (calls != hwc_data.calls) - return -EINVAL; - - hwc_data.calls = NULL; - return 0; -} - -int -hwc_send (hwc_request_t * req) -{ - unsigned long flags; - int retval; - int cc; - - spin_lock_irqsave (&hwc_data.lock, flags); - if (!req || !req->callback || !req->block) { - retval = -EINVAL; - goto unlock; - } - if (hwc_data.request) { - retval = -ENOTSUPP; - goto unlock; - } - cc = service_call (req->word, req->block); - switch (cc) { - case 0: - hwc_data.request = req; - hwc_data.current_servc = req->word; - hwc_data.current_hwcb = req->block; - retval = 0; - break; - case 2: - retval = -EBUSY; - break; - default: - retval = -ENOSYS; - - } - unlock: - spin_unlock_irqrestore (&hwc_data.lock, flags); - return retval; -} - -EXPORT_SYMBOL (hwc_send); - -void -do_hwc_callback (u32 ext_int_param) -{ - if (!hwc_data.request || !hwc_data.request->callback) - return; - if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR) - != (unsigned long) hwc_data.request->block) - return; - hwc_data.request->callback (hwc_data.request); - hwc_data.request = NULL; - hwc_data.current_hwcb = NULL; - hwc_data.current_servc = 0; -} - -void -hwc_do_interrupt (u32 ext_int_param) -{ - u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR; - u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND; - - if (hwc_data.flags & HWC_PTIMER_RUNS) { - del_timer (&hwc_data.poll_timer); - hwc_data.flags &= ~HWC_PTIMER_RUNS; - } - if (finished_hwcb) { - - if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "interrupt: mismatch: " - "ext. int param. (0x%x) vs. " - "current HWCB (0x%x)\n", - ext_int_param, - hwc_data.current_hwcb); - } else { - if (hwc_data.request) { - - do_hwc_callback (ext_int_param); - } else { - - switch (hwc_data.current_servc) { - - case HWC_CMDW_WRITEMASK: - - write_event_mask_2 (ext_int_param); - break; - - case HWC_CMDW_WRITEDATA: - - write_event_data_2 (ext_int_param); - break; - - case HWC_CMDW_READDATA: - - unconditional_read_2 (ext_int_param); - break; - default: - } - } - } - } else { - - if (hwc_data.current_hwcb) { - internal_print ( - DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "interrupt: mismatch: " - "ext. int. param. (0x%x) vs. " - "current HWCB (0x%x)\n", - ext_int_param, - hwc_data.current_hwcb); - } - } - - if (evbuf_pending) { - - unconditional_read_1 (); - } else { - - write_event_data_1 (); - } - - if (!hwc_data.calls || !hwc_data.calls->wake_up) - return; - (hwc_data.calls->wake_up) (); -} - -void -hwc_interrupt_handler (struct pt_regs *regs, __u16 code) -{ - int cpu = smp_processor_id (); - - u32 ext_int_param = hwc_ext_int_param (); - - irq_enter (cpu, 0x2401); - - if (hwc_data.flags & HWC_INIT) { - - hwc_data.flags |= HWC_INTERRUPT; - } else if (hwc_data.flags & HWC_BROKEN) { - - if (!do_hwc_init ()) { - hwc_data.flags &= ~HWC_BROKEN; - internal_print (DELAYED_WRITE, - HWC_RW_PRINT_HEADER - "delayed HWC setup after" - " temporary breakdown" - " (ext. int. parameter=0x%x)\n", - ext_int_param); - } - } else { - spin_lock (&hwc_data.lock); - hwc_do_interrupt (ext_int_param); - spin_unlock (&hwc_data.lock); - } - irq_exit (cpu, 0x2401); -} - -void -hwc_unblank (void) -{ - - spin_lock (&hwc_data.lock); - spin_unlock (&hwc_data.lock); - - __ctl_store (cr0, 0, 0); - cr0_save = cr0; - cr0 |= 0x00000200; - cr0 &= 0xFFFFF3AC; - __ctl_load (cr0, 0, 0); - - asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory"); - - while (ALL_HWCB_CHAR) - barrier (); - - asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory"); - - __ctl_load (cr0_save, 0, 0); -} - -int -hwc_ioctl (unsigned int cmd, unsigned long arg) -{ - hwc_ioctls_t tmp = hwc_data.ioctls; - int retval = 0; - unsigned long flags; - unsigned int obuf; - - spin_lock_irqsave (&hwc_data.lock, flags); - - switch (cmd) { - - case TIOCHWCSHTAB: - if (get_user (tmp.width_htab, (ioctl_htab_t *) arg)) - goto fault; - break; - - case TIOCHWCSECHO: - if (get_user (tmp.echo, (ioctl_echo_t *) arg)) - goto fault; - break; - - case TIOCHWCSCOLS: - if (get_user (tmp.columns, (ioctl_cols_t *) arg)) - goto fault; - break; - - case TIOCHWCSNL: - if (get_user (tmp.final_nl, (ioctl_nl_t *) arg)) - goto fault; - break; - - case TIOCHWCSOBUF: - if (get_user (obuf, (unsigned int *) arg)) - goto fault; - if (obuf & 0xFFF) - tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12); - else - tmp.max_hwcb = (obuf >> 12); - break; - - case TIOCHWCSCASE: - if (get_user (tmp.tolower, (ioctl_case_t *) arg)) - goto fault; - break; - - case TIOCHWCSDELIM: - if (get_user (tmp.delim, (ioctl_delim_t *) arg)) - goto fault; - break; - - case TIOCHWCSINIT: - retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1); - break; - - case TIOCHWCGHTAB: - if (put_user (tmp.width_htab, (ioctl_htab_t *) arg)) - goto fault; - break; - - case TIOCHWCGECHO: - if (put_user (tmp.echo, (ioctl_echo_t *) arg)) - goto fault; - break; - - case TIOCHWCGCOLS: - if (put_user (tmp.columns, (ioctl_cols_t *) arg)) - goto fault; - break; - - case TIOCHWCGNL: - if (put_user (tmp.final_nl, (ioctl_nl_t *) arg)) - goto fault; - break; - - case TIOCHWCGOBUF: - if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg)) - goto fault; - break; - - case TIOCHWCGKBUF: - if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg)) - goto fault; - break; - - case TIOCHWCGCASE: - if (put_user (tmp.tolower, (ioctl_case_t *) arg)) - goto fault; - break; - - case TIOCHWCGDELIM: - if (put_user (tmp.delim, (ioctl_delim_t *) arg)) - goto fault; - break; -#if 0 - - case TIOCHWCGINIT: - if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg)) - goto fault; - break; - - case TIOCHWCGCURR: - if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg)) - goto fault; - break; -#endif - - default: - goto noioctlcmd; - } - - if (_IOC_DIR (cmd) == _IOC_WRITE) - retval = set_hwc_ioctls (&tmp, 0); - - goto out; - - fault: - retval = -EFAULT; - goto out; - noioctlcmd: - retval = -ENOIOCTLCMD; - out: - spin_unlock_irqrestore (&hwc_data.lock, flags); - return retval; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc_rw.h linux.21rc1-ac2/drivers/s390/char/hwc_rw.h --- linux.21rc1/drivers/s390/char/hwc_rw.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc_rw.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,132 +0,0 @@ -/* - * drivers/s390/char/hwc_rw.h - * interface to the HWC-read/write driver - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke - */ - -#ifndef __HWC_RW_H__ -#define __HWC_RW_H__ - -#include - -typedef struct { - - void (*move_input) (unsigned char *, unsigned int); - - void (*wake_up) (void); -} hwc_high_level_calls_t; - -struct _hwc_request; - -typedef void hwc_callback_t (struct _hwc_request *); - -typedef struct _hwc_request { - void *block; - u32 word; - hwc_callback_t *callback; - void *data; -} __attribute__ ((packed)) - -hwc_request_t; - -#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x])) - -#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c))) - -#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c))) - -#define IN_HWCB 1 -#define IN_WRITE_BUF 2 -#define IN_BUFS_TOTAL (IN_HWCB | IN_WRITE_BUF) - -typedef unsigned short int ioctl_htab_t; -typedef unsigned char ioctl_echo_t; -typedef unsigned short int ioctl_cols_t; -typedef signed char ioctl_nl_t; -typedef unsigned short int ioctl_obuf_t; -typedef unsigned char ioctl_case_t; -typedef unsigned char ioctl_delim_t; - -typedef struct { - ioctl_htab_t width_htab; - ioctl_echo_t echo; - ioctl_cols_t columns; - ioctl_nl_t final_nl; - ioctl_obuf_t max_hwcb; - ioctl_obuf_t kmem_hwcb; - ioctl_case_t tolower; - ioctl_delim_t delim; -} hwc_ioctls_t; - -static hwc_ioctls_t _hwc_ioctls; - -#define HWC_IOCTL_LETTER 'B' - -#define TIOCHWCSHTAB _IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab) - -#define TIOCHWCSECHO _IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo) - -#define TIOCHWCSCOLS _IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns) - -#define TIOCHWCSNL _IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl) - -#define TIOCHWCSOBUF _IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb) - -#define TIOCHWCSINIT _IO(HWC_IOCTL_LETTER, 6) - -#define TIOCHWCSCASE _IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower) - -#define TIOCHWCSDELIM _IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim) - -#define TIOCHWCGHTAB _IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab) - -#define TIOCHWCGECHO _IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo) - -#define TIOCHWCGCOLS _IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns) - -#define TIOCHWCGNL _IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl) - -#define TIOCHWCGOBUF _IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb) - -#define TIOCHWCGINIT _IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls) - -#define TIOCHWCGCASE _IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower) - -#define TIOCHWCGDELIM _IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim) - -#define TIOCHWCGKBUF _IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb) - -#define TIOCHWCGCURR _IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls) - -#ifndef __HWC_RW_C__ - -extern int hwc_init (void); - -extern int hwc_write (int from_user, const unsigned char *, unsigned int); - -extern unsigned int hwc_chars_in_buffer (unsigned char); - -extern unsigned int hwc_write_room (unsigned char); - -extern void hwc_flush_buffer (unsigned char); - -extern void hwc_unblank (void); - -extern signed int hwc_ioctl (unsigned int, unsigned long); - -extern void do_hwc_interrupt (void); - -extern int hwc_printk (const char *,...); - -extern signed int hwc_register_calls (hwc_high_level_calls_t *); - -extern signed int hwc_unregister_calls (hwc_high_level_calls_t *); - -extern int hwc_send (hwc_request_t *); - -#endif - -#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/hwc_tty.c linux.21rc1-ac2/drivers/s390/char/hwc_tty.c --- linux.21rc1/drivers/s390/char/hwc_tty.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/hwc_tty.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,273 +0,0 @@ -/* - * drivers/s390/char/hwc_tty.c - * HWC line mode terminal driver. - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke - * - * Thanks to Martin Schwidefsky. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "hwc_rw.h" -#include "ctrlchar.h" - -#define HWC_TTY_PRINT_HEADER "hwc tty driver: " - -#define HWC_TTY_BUF_SIZE 512 - -typedef struct { - - struct tty_struct *tty; - - unsigned char buf[HWC_TTY_BUF_SIZE]; - - unsigned short int buf_count; - - spinlock_t lock; - - hwc_high_level_calls_t calls; -} hwc_tty_data_struct; - -static hwc_tty_data_struct hwc_tty_data = -{ /* NULL/0 */ }; -static struct tty_driver hwc_tty_driver; -static struct tty_struct *hwc_tty_table[1]; -static struct termios *hwc_tty_termios[1]; -static struct termios *hwc_tty_termios_locked[1]; -static int hwc_tty_refcount = 0; - -extern struct termios tty_std_termios; - -void hwc_tty_wake_up (void); -void hwc_tty_input (unsigned char *, unsigned int); - -static int -hwc_tty_open (struct tty_struct *tty, - struct file *filp) -{ - - if (MINOR (tty->device) - tty->driver.minor_start) - return -ENODEV; - - tty->driver_data = &hwc_tty_data; - hwc_tty_data.buf_count = 0; - hwc_tty_data.tty = tty; - tty->low_latency = 0; - - hwc_tty_data.calls.wake_up = hwc_tty_wake_up; - hwc_tty_data.calls.move_input = hwc_tty_input; - hwc_register_calls (&(hwc_tty_data.calls)); - - return 0; -} - -static void -hwc_tty_close (struct tty_struct *tty, - struct file *filp) -{ - if (MINOR (tty->device) != tty->driver.minor_start) { - printk (KERN_WARNING HWC_TTY_PRINT_HEADER - "do not close hwc tty because of wrong device number"); - return; - } - if (tty->count > 1) - return; - - hwc_tty_data.tty = NULL; - - hwc_unregister_calls (&(hwc_tty_data.calls)); -} - -static int -hwc_tty_write_room (struct tty_struct *tty) -{ - int retval; - - retval = hwc_write_room (IN_BUFS_TOTAL); - return retval; -} - -static int -hwc_tty_write (struct tty_struct *tty, - int from_user, - const unsigned char *buf, - int count) -{ - int retval; - - if (hwc_tty_data.buf_count > 0) { - hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count); - hwc_tty_data.buf_count = 0; - } - retval = hwc_write (from_user, buf, count); - return retval; -} - -static void -hwc_tty_put_char (struct tty_struct *tty, - unsigned char ch) -{ - unsigned long flags; - - spin_lock_irqsave (&hwc_tty_data.lock, flags); - if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) { - hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count); - hwc_tty_data.buf_count = 0; - } - hwc_tty_data.buf[hwc_tty_data.buf_count] = ch; - hwc_tty_data.buf_count++; - spin_unlock_irqrestore (&hwc_tty_data.lock, flags); -} - -static void -hwc_tty_flush_chars (struct tty_struct *tty) -{ - unsigned long flags; - - spin_lock_irqsave (&hwc_tty_data.lock, flags); - hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count); - hwc_tty_data.buf_count = 0; - spin_unlock_irqrestore (&hwc_tty_data.lock, flags); -} - -static int -hwc_tty_chars_in_buffer (struct tty_struct *tty) -{ - int retval; - - retval = hwc_chars_in_buffer (IN_BUFS_TOTAL); - return retval; -} - -static void -hwc_tty_flush_buffer (struct tty_struct *tty) -{ - hwc_tty_wake_up (); -} - -static int -hwc_tty_ioctl ( - struct tty_struct *tty, - struct file *file, - unsigned int cmd, - unsigned long arg) -{ - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - return hwc_ioctl (cmd, arg); -} - -void -hwc_tty_wake_up (void) -{ - if (hwc_tty_data.tty == NULL) - return; - if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - hwc_tty_data.tty->ldisc.write_wakeup) - (hwc_tty_data.tty->ldisc.write_wakeup) (hwc_tty_data.tty); - wake_up_interruptible (&hwc_tty_data.tty->write_wait); -} - -void -hwc_tty_input (unsigned char *buf, unsigned int count) -{ - struct tty_struct *tty = hwc_tty_data.tty; - - if (tty != NULL) { - char *cchar; - if ((cchar = ctrlchar_handle (buf, count, tty))) { - if (cchar == (char *) -1) - return; - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = *cchar; - } else { - - memcpy (tty->flip.char_buf_ptr, buf, count); - if (count < 2 || ( - strncmp (buf + count - 2, "^n", 2) || - strncmp (buf + count - 2, "\0252n", 2))) { - tty->flip.char_buf_ptr[count] = '\n'; - count++; - } else - count -= 2; - memset (tty->flip.flag_buf_ptr, TTY_NORMAL, count); - tty->flip.char_buf_ptr += count; - tty->flip.flag_buf_ptr += count; - tty->flip.count += count; - } - tty_flip_buffer_push (tty); - hwc_tty_wake_up (); - } -} - -void -hwc_tty_init (void) -{ - if (!CONSOLE_IS_HWC) - return; - - ctrlchar_init (); - - memset (&hwc_tty_driver, 0, sizeof (struct tty_driver)); - memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct)); - hwc_tty_driver.magic = TTY_DRIVER_MAGIC; - hwc_tty_driver.driver_name = "tty_hwc"; - hwc_tty_driver.name = "ttyS"; - hwc_tty_driver.name_base = 0; - hwc_tty_driver.major = TTY_MAJOR; - hwc_tty_driver.minor_start = 64; - hwc_tty_driver.num = 1; - hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM; - hwc_tty_driver.subtype = SYSTEM_TYPE_TTY; - hwc_tty_driver.init_termios = tty_std_termios; - hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR; - hwc_tty_driver.init_termios.c_oflag = ONLCR; - hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO; - hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW; - hwc_tty_driver.refcount = &hwc_tty_refcount; - - hwc_tty_driver.table = hwc_tty_table; - hwc_tty_driver.termios = hwc_tty_termios; - hwc_tty_driver.termios_locked = hwc_tty_termios_locked; - - hwc_tty_driver.open = hwc_tty_open; - hwc_tty_driver.close = hwc_tty_close; - hwc_tty_driver.write = hwc_tty_write; - hwc_tty_driver.put_char = hwc_tty_put_char; - hwc_tty_driver.flush_chars = hwc_tty_flush_chars; - hwc_tty_driver.write_room = hwc_tty_write_room; - hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer; - hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer; - hwc_tty_driver.ioctl = hwc_tty_ioctl; - - hwc_tty_driver.throttle = NULL; - hwc_tty_driver.unthrottle = NULL; - hwc_tty_driver.send_xchar = NULL; - hwc_tty_driver.set_termios = NULL; - hwc_tty_driver.set_ldisc = NULL; - hwc_tty_driver.stop = NULL; - hwc_tty_driver.start = NULL; - hwc_tty_driver.hangup = NULL; - hwc_tty_driver.break_ctl = NULL; - hwc_tty_driver.wait_until_sent = NULL; - hwc_tty_driver.read_proc = NULL; - hwc_tty_driver.write_proc = NULL; - - if (tty_register_driver (&hwc_tty_driver)) - panic ("Couldn't register hwc_tty driver\n"); -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/Makefile linux.21rc1-ac2/drivers/s390/char/Makefile --- linux.21rc1/drivers/s390/char/Makefile 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/Makefile 2003-04-25 13:53:58.000000000 +0100 @@ -4,31 +4,36 @@ O_TARGET := s390-char.o -list-multi := tub3270.o tape390.o -export-objs := hwc_rw.o +list-multi := tub3270.o \ + tape390.o + +export-objs := sclp.o \ + tape_core.o \ + tape_devmap.o \ + tape_std.o tub3270-objs := tuball.o tubfs.o tubtty.o \ tubttyaid.o tubttybld.o tubttyscl.o \ tubttyrcl.o tubttysiz.o -tape390-$(CONFIG_S390_TAPE_CHAR) += tapechar.o -tape390-$(CONFIG_S390_TAPE_BLOCK) += tapeblock.o -tape390-$(CONFIG_S390_TAPE_3480) += tape3480.o tape34xx.o -tape390-$(CONFIG_S390_TAPE_3490) += tape3490.o tape34xx.o -tape390-objs := tape.o $(sort $(tape390-y)) +tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o +tape-objs := tape_core.o tape_devmap.o tape_proc.o tape_std.o tape_char.o \ + $(sort $(tape-y)) +obj-$(CONFIG_S390_TAPE) += tape390.o +obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-y += ctrlchar.o obj-$(CONFIG_TN3215) += con3215.o -obj-$(CONFIG_HWC) += hwc_con.o hwc_rw.o hwc_tty.o -obj-$(CONFIG_HWC_CPI) += hwc_cpi.o +obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o +obj-$(CONFIG_SCLP_TTY) += sclp_tty.o +obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o +obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o obj-$(CONFIG_TN3270) += tub3270.o -obj-$(CONFIG_S390_TAPE) += tape390.o include $(TOPDIR)/Rules.make tub3270.o: $(tub3270-objs) $(LD) -r -o $@ $(tub3270-objs) -tape390.o: $(tape390-objs) - $(LD) -r -o $@ $(tape390-objs) - +tape390.o: $(tape-objs) + $(LD) -r -o $@ $(tape-objs) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp.c linux.21rc1-ac2/drivers/s390/char/sclp.c --- linux.21rc1/drivers/s390/char/sclp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp.c 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,778 @@ +/* + * drivers/s390/char/sclp.c + * core function to access sclp interface + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sclp.h" + +#define SCLP_CORE_PRINT_HEADER "sclp low level driver: " + +/* Structure for register_early_external_interrupt. */ +static ext_int_info_t ext_int_info_hwc; + +/* spinlock to protect global variables of sclp_core */ +static spinlock_t sclp_lock; + +/* Mask of valid sclp events */ +static sccb_mask_t sclp_receive_mask; +static sccb_mask_t sclp_send_mask; + +/* List of registered event types */ +static struct list_head sclp_reg_list; + +/* sccb queue */ +static struct list_head sclp_req_queue; + +/* sccb for unconditional read */ +static struct sclp_req sclp_read_req; +static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +/* sccb for write mask sccb */ +static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + +/* Timer for init mask retries. */ +static struct timer_list retry_timer; + +static unsigned long sclp_status = 0; +/* some status flags */ +#define SCLP_INIT 0 +#define SCLP_RUNNING 1 +#define SCLP_READING 2 + +#define SCLP_INIT_POLL_INTERVAL 1 + +#define SCLP_COMMAND_INITIATED 0 +#define SCLP_BUSY 2 +#define SCLP_NOT_OPERATIONAL 3 + +/* + * assembler instruction for Service Call + */ +static int +__service_call(sclp_cmdw_t command, void *sccb) +{ + int cc; + + /* + * Mnemonic: SERVC Rx, Ry [RRE] + * + * Rx: SCLP command word + * Ry: address of SCCB + */ + __asm__ __volatile__( + " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + " ipm %0\n" + " srl %0,28" + : "=&d" (cc) + : "d" (command), "a" (__pa(sccb)) + : "cc", "memory" ); + /* + * cc == 0: Service Call succesful initiated + * cc == 2: SCLP busy, new Service Call not initiated, + * new SCCB unchanged + * cc == 3: SCLP function not operational + */ + if (cc == SCLP_NOT_OPERATIONAL) + return -EIO; + /* + * We set the SCLP_RUNNING bit for cc 2 as well because if + * service_call returns cc 2 some old request is running + * that has to complete first + */ + set_bit(SCLP_RUNNING, &sclp_status); + if (cc == SCLP_BUSY) + return -EBUSY; + return 0; +} + +static int +sclp_start_request(void) +{ + struct sclp_req *req; + int rc; + unsigned long flags; + + /* quick exit if sclp is already in use */ + if (test_bit(SCLP_RUNNING, &sclp_status)) + return -EBUSY; + spin_lock_irqsave(&sclp_lock, flags); + /* Get first request on queue if available */ + req = NULL; + if (!list_empty(&sclp_req_queue)) + req = list_entry(sclp_req_queue.next, struct sclp_req, list); + if (req) { + rc = __service_call(req->command, req->sccb); + if (rc) { + req->status = SCLP_REQ_FAILED; + list_del(&req->list); + } else + req->status = SCLP_REQ_RUNNING; + } else + rc = -EINVAL; + spin_unlock_irqrestore(&sclp_lock, flags); + if (rc == -EIO && req->callback != NULL) + req->callback(req, req->callback_data); + return rc; +} + +static int +sclp_process_evbufs(struct sccb_header *sccb) +{ + int result; + unsigned long flags; + struct evbuf_header *evbuf; + struct list_head *l; + struct sclp_register *t; + + spin_lock_irqsave(&sclp_lock, flags); + evbuf = (struct evbuf_header *) (sccb + 1); + result = 0; + while ((addr_t) evbuf < (addr_t) sccb + sccb->length) { + /* check registered event */ + t = NULL; + list_for_each(l, &sclp_reg_list) { + t = list_entry(l, struct sclp_register, list); + if (t->receive_mask & (1 << (32 - evbuf->type))) { + if (t->receiver_fn != NULL) { + spin_unlock_irqrestore(&sclp_lock, + flags); + t->receiver_fn(evbuf); + spin_lock_irqsave(&sclp_lock, flags); + } + break; + } + else + t = NULL; + } + /* Check for unrequested event buffer */ + if (t == NULL) + result = -ENOSYS; + evbuf = (struct evbuf_header *) + ((addr_t) evbuf + evbuf->length); + } + spin_unlock_irqrestore(&sclp_lock, flags); + return result; +} + +char * +sclp_error_message(u16 rc) +{ + static struct { + u16 code; char *msg; + } sclp_errors[] = { + { 0x0000, "No response code stored (machine malfunction)" }, + { 0x0020, "Normal Completion" }, + { 0x0040, "SCLP equipment check" }, + { 0x0100, "SCCB boundary violation" }, + { 0x01f0, "Invalid command" }, + { 0x0220, "Normal Completion; suppressed buffers pending" }, + { 0x0300, "Insufficient SCCB length" }, + { 0x0340, "Contained SCLP equipment check" }, + { 0x05f0, "Target resource in improper state" }, + { 0x40f0, "Invalid function code/not installed" }, + { 0x60f0, "No buffers stored" }, + { 0x62f0, "No buffers stored; suppressed buffers pending" }, + { 0x70f0, "Invalid selection mask" }, + { 0x71f0, "Event buffer exceeds available space" }, + { 0x72f0, "Inconsistent lengths" }, + { 0x73f0, "Event buffer syntax error" } + }; + int i; + for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++) + if (rc == sclp_errors[i].code) + return sclp_errors[i].msg; + return "Invalid response code"; +} + +/* + * postprocessing of unconditional read service call + */ +static void +sclp_unconditional_read_cb(struct sclp_req *read_req, void *data) +{ + struct sccb_header *sccb; + + sccb = read_req->sccb; + if (sccb->response_code == 0x0020 || + sccb->response_code == 0x0220) { + if (sclp_process_evbufs(sccb) != 0) + printk(KERN_WARNING SCLP_CORE_PRINT_HEADER + "unconditional read: " + "unrequested event buffer received.\n"); + } + + if (sccb->response_code != 0x0020) + printk(KERN_WARNING SCLP_CORE_PRINT_HEADER + "unconditional read: %s (response code=0x%x).\n", + sclp_error_message(sccb->response_code), + sccb->response_code); + + clear_bit(SCLP_READING, &sclp_status); +} + +/* + * Function to queue Read Event Data/Unconditional Read + */ +static void +__sclp_unconditional_read(void) +{ + struct sccb_header *sccb; + struct sclp_req *read_req; + + /* + * Don't try to initiate Unconditional Read if we are not able to + * receive anything + */ + if (sclp_receive_mask == 0) + return; + /* Don't try reading if a read is already outstanding */ + if (test_and_set_bit(SCLP_READING, &sclp_status)) + return; + /* Initialize read sccb */ + sccb = (struct sccb_header *) sclp_read_sccb; + clear_page(sccb); + sccb->length = PAGE_SIZE; + sccb->function_code = 0; /* unconditional read */ + sccb->control_mask[2] = 0x80; /* variable length response */ + /* Initialize request structure */ + read_req = &sclp_read_req; + read_req->command = SCLP_CMDW_READDATA; + read_req->status = SCLP_REQ_QUEUED; + read_req->callback = sclp_unconditional_read_cb; + read_req->sccb = sccb; + /* Add read request to the head of queue */ + list_add(&read_req->list, &sclp_req_queue); +} + +/* Bit masks to interpret external interruption parameter contents. */ +#define EXT_INT_SCCB_MASK 0xfffffff8 +#define EXT_INT_STATECHANGE_PENDING 0x00000002 +#define EXT_INT_EVBUF_PENDING 0x00000001 + +/* + * Handler for service-signal external interruptions + */ +static void +sclp_interrupt_handler(struct pt_regs *regs, __u16 code) +{ + u32 ext_int_param, finished_sccb, evbuf_pending; + struct list_head *l; + struct sclp_req *req, *tmp; + int cpu; + + /* + * Only process interrupt if sclp is initialized. + * This avoids strange effects for a pending request + * from before the last re-ipl. + */ + if (!test_bit(SCLP_INIT, &sclp_status)) { + clear_bit(SCLP_RUNNING, &sclp_status); + return; + } + ext_int_param = S390_lowcore.ext_params; + finished_sccb = ext_int_param & EXT_INT_SCCB_MASK; + evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING | + EXT_INT_STATECHANGE_PENDING); + cpu = smp_processor_id(); + irq_enter(cpu, 0x2401); + req = NULL; + spin_lock(&sclp_lock); + if (finished_sccb != 0U) { + list_for_each(l, &sclp_req_queue) { + tmp = list_entry(l, struct sclp_req, list); + if (finished_sccb == (u32)(addr_t) tmp->sccb) { + list_del(&tmp->list); + req = tmp; + break; + } + } + } + spin_unlock(&sclp_lock); + /* Perform callback */ + if (req != NULL) { + req->status = SCLP_REQ_DONE; + if (req->callback != NULL) + req->callback(req, req->callback_data); + } + spin_lock(&sclp_lock); + /* Head queue a read sccb if an event buffer is pending */ + if (evbuf_pending) + __sclp_unconditional_read(); + spin_unlock(&sclp_lock); + /* Now clear the running bit */ + clear_bit(SCLP_RUNNING, &sclp_status); + /* and start next request on the queue */ + sclp_start_request(); + irq_exit(cpu, 0x2401); +} + +/* + * Wait synchronously for external interrupt of sclp. We may not receive + * any other external interrupt, so we disable all other external interrupts + * in control register 0. + */ +void +sclp_sync_wait(void) +{ + unsigned long psw_mask; + unsigned long cr0, cr0_sync; + + /* + * save cr0 + * enable service signal external interruption (cr0.22) + * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31 + * don't touch any other bit in cr0 + */ + __ctl_store(cr0, 0, 0); + cr0_sync = cr0; + cr0_sync |= 0x00000200; + cr0_sync &= 0xFFFFF3AC; + __ctl_load(cr0_sync, 0, 0); + + /* enable external interruptions (PSW-mask.7) */ + asm volatile ("STOSM 0(%1),0x01" + : "=m" (psw_mask) : "a" (&psw_mask) : "memory"); + + /* wait until ISR signals receipt of interrupt */ + while (test_bit(SCLP_RUNNING, &sclp_status)) + barrier(); + + /* disable external interruptions */ + asm volatile ("SSM 0(%0)" + : : "a" (&psw_mask) : "memory"); + + /* restore cr0 */ + __ctl_load(cr0, 0, 0); +} + +/* + * Queue an SCLP request. Request will immediately be processed if queue is + * empty. + */ +void +sclp_add_request(struct sclp_req *req) +{ + unsigned long flags; + + if (!test_bit(SCLP_INIT, &sclp_status)) { + req->status = SCLP_REQ_FAILED; + if (req->callback != NULL) + req->callback(req, req->callback_data); + return; + } + spin_lock_irqsave(&sclp_lock, flags); + /* queue the request */ + req->status = SCLP_REQ_QUEUED; + list_add_tail(&req->list, &sclp_req_queue); + spin_unlock_irqrestore(&sclp_lock, flags); + /* try to start the first request on the queue */ + sclp_start_request(); +} + +/* state change notification */ +struct sclp_statechangebuf { + struct evbuf_header header; + u8 validity_sclp_active_facility_mask : 1; + u8 validity_sclp_receive_mask : 1; + u8 validity_sclp_send_mask : 1; + u8 validity_read_data_function_mask : 1; + u16 _zeros : 12; + u16 mask_length; + u64 sclp_active_facility_mask; + sccb_mask_t sclp_receive_mask; + sccb_mask_t sclp_send_mask; + u32 read_data_function_mask; +} __attribute__((packed)); + +static inline void +__sclp_notify_state_change(void) +{ + struct list_head *l; + struct sclp_register *t; + sccb_mask_t receive_mask, send_mask; + + list_for_each(l, &sclp_reg_list) { + t = list_entry(l, struct sclp_register, list); + receive_mask = t->receive_mask & sclp_receive_mask; + send_mask = t->send_mask & sclp_send_mask; + if (t->sclp_receive_mask != receive_mask || + t->sclp_send_mask != send_mask) { + t->sclp_receive_mask = receive_mask; + t->sclp_send_mask = send_mask; + if (t->state_change_fn != NULL) + t->state_change_fn(t); + } + } +} + +static void +sclp_state_change(struct evbuf_header *evbuf) +{ + unsigned long flags; + struct sclp_statechangebuf *scbuf; + + spin_lock_irqsave(&sclp_lock, flags); + scbuf = (struct sclp_statechangebuf *) evbuf; + + if (scbuf->validity_sclp_receive_mask) { + if (scbuf->mask_length != sizeof(sccb_mask_t)) + printk(KERN_WARNING SCLP_CORE_PRINT_HEADER + "state change event with mask length %i\n", + scbuf->mask_length); + else + /* set new receive mask */ + sclp_receive_mask = scbuf->sclp_receive_mask; + } + + if (scbuf->validity_sclp_send_mask) { + if (scbuf->mask_length != sizeof(sccb_mask_t)) + printk(KERN_WARNING SCLP_CORE_PRINT_HEADER + "state change event with mask length %i\n", + scbuf->mask_length); + else + /* set new send mask */ + sclp_send_mask = scbuf->sclp_send_mask; + } + + __sclp_notify_state_change(); + spin_unlock_irqrestore(&sclp_lock, flags); +} + +static struct sclp_register sclp_state_change_event = { + .receive_mask = EvTyp_StateChange_Mask, + .receiver_fn = sclp_state_change +}; + + +/* + * SCLP quiesce event handler + */ +#ifdef CONFIG_SMP +static volatile unsigned long cpu_quiesce_map; + +static void +do_load_quiesce_psw(void * __unused) +{ + psw_t quiesce_psw; + + clear_bit(smp_processor_id(), &cpu_quiesce_map); + if (smp_processor_id() == 0) { + /* Wait for all other cpus to enter do_load_quiesce_psw */ + while (cpu_quiesce_map != 0); + /* Quiesce the last cpu with the special psw */ + quiesce_psw.mask = _DW_PSW_MASK; + quiesce_psw.addr = 0xfff; + __load_psw(quiesce_psw); + } + signal_processor(smp_processor_id(), sigp_stop); +} + +static void +do_machine_quiesce(void) +{ + cpu_quiesce_map = cpu_online_map; + smp_call_function(do_load_quiesce_psw, NULL, 0, 0); + do_load_quiesce_psw(NULL); +} +#else +static void +do_machine_quiesce(void) +{ + psw_t quiesce_psw; + + quiesce_psw.mask = _DW_PSW_MASK; + quiesce_psw.addr = 0xfff; + __load_psw(quiesce_psw); +} +#endif + +extern void ctrl_alt_del(void); + +static void +sclp_quiesce(struct evbuf_header *evbuf) +{ + /* + * We got a "shutdown" request. + * Add a call to an appropriate "shutdown" routine here. This + * routine should set all PSWs to 'disabled-wait', 'stopped' + * or 'check-stopped' - except 1 PSW which needs to carry a + * special bit pattern called 'quiesce PSW'. + */ + _machine_restart = (void *) do_machine_quiesce; + _machine_halt = do_machine_quiesce; + _machine_power_off = do_machine_quiesce; + ctrl_alt_del(); +} + +static struct sclp_register sclp_quiesce_event = { + .receive_mask = EvTyp_SigQuiesce_Mask, + .receiver_fn = sclp_quiesce +}; + +/* initialisation of SCLP */ +struct init_sccb { + struct sccb_header header; + u16 _reserved; + u16 mask_length; + sccb_mask_t receive_mask; + sccb_mask_t send_mask; + sccb_mask_t sclp_send_mask; + sccb_mask_t sclp_receive_mask; +} __attribute__((packed)); + +static void sclp_init_mask_retry(unsigned long); + +static int +sclp_init_mask(void) +{ + unsigned long flags; + struct init_sccb *sccb; + struct sclp_req *req; + struct list_head *l; + struct sclp_register *t; + int rc; + + sccb = (struct init_sccb *) sclp_init_sccb; + /* stick the request structure to the end of the init sccb page */ + req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1; + + /* SCLP setup concerning receiving and sending Event Buffers */ + req->command = SCLP_CMDW_WRITEMASK; + req->status = SCLP_REQ_QUEUED; + req->callback = NULL; + req->sccb = sccb; + /* setup sccb for writemask command */ + memset(sccb, 0, sizeof(struct init_sccb)); + sccb->header.length = sizeof(struct init_sccb); + sccb->mask_length = sizeof(sccb_mask_t); + /* copy in the sccb mask of the registered event types */ + spin_lock_irqsave(&sclp_lock, flags); + list_for_each(l, &sclp_reg_list) { + t = list_entry(l, struct sclp_register, list); + sccb->receive_mask |= t->receive_mask; + sccb->send_mask |= t->send_mask; + } + sccb->sclp_receive_mask = 0; + sccb->sclp_send_mask = 0; + if (test_bit(SCLP_INIT, &sclp_status)) { + /* add request to sclp queue */ + list_add_tail(&req->list, &sclp_req_queue); + spin_unlock_irqrestore(&sclp_lock, flags); + /* and start if SCLP is idle */ + sclp_start_request(); + /* now wait for completion */ + while (req->status != SCLP_REQ_DONE && + req->status != SCLP_REQ_FAILED) + sclp_sync_wait(); + spin_lock_irqsave(&sclp_lock, flags); + } else { + /* + * Special case for the very first write mask command. + * The interrupt handler is not removing request from + * the request queue and doesn't call callbacks yet + * because there might be an pending old interrupt + * after a Re-IPL. We have to receive and ignore it. + */ + do { + rc = __service_call(req->command, req->sccb); + spin_unlock_irqrestore(&sclp_lock, flags); + if (rc == -EIO) + return -ENOSYS; + sclp_sync_wait(); + spin_lock_irqsave(&sclp_lock, flags); + } while (rc == -EBUSY); + } + if (sccb->header.response_code != 0x0020) { + /* WRITEMASK failed - we cannot rely on receiving a state + change event, so initially, polling is the only alternative + for us to ever become operational. */ + if (!timer_pending(&retry_timer) || + !mod_timer(&retry_timer, + jiffies + SCLP_INIT_POLL_INTERVAL*HZ)) { + retry_timer.function = sclp_init_mask_retry; + retry_timer.data = 0; + retry_timer.expires = jiffies + + SCLP_INIT_POLL_INTERVAL*HZ; + add_timer(&retry_timer); + } + } else { + sclp_receive_mask = sccb->sclp_receive_mask; + sclp_send_mask = sccb->sclp_send_mask; + __sclp_notify_state_change(); + } + spin_unlock_irqrestore(&sclp_lock, flags); + return 0; +} + +static void +sclp_init_mask_retry(unsigned long data) +{ + sclp_init_mask(); +} + +/* + * sclp setup function. Called early (no kmalloc!) from sclp_console_init(). + */ +static int +sclp_init(void) +{ + int rc; + + if (test_bit(SCLP_INIT, &sclp_status)) + /* Already initialized. */ + return 0; + + /* + * request the 0x2401 external interrupt + * The sclp driver is initialized early (before kmalloc works). We + * need to use register_early_external_interrupt. + */ + if (register_early_external_interrupt(0x2401, sclp_interrupt_handler, + &ext_int_info_hwc) != 0) + return -EBUSY; + + spin_lock_init(&sclp_lock); + INIT_LIST_HEAD(&sclp_req_queue); + + /* init event list */ + INIT_LIST_HEAD(&sclp_reg_list); + list_add(&sclp_state_change_event.list, &sclp_reg_list); + list_add(&sclp_quiesce_event.list, &sclp_reg_list); + + /* enable service-signal external interruptions, + * Control Register 0 bit 22 := 1 + * (besides PSW bit 7 must be set to 1 sometimes for external + * interruptions) + */ + ctl_set_bit(0, 9); + + init_timer(&retry_timer); + /* do the initial write event mask */ + rc = sclp_init_mask(); + if (rc == 0) { + /* Ok, now everything is setup right. */ + set_bit(SCLP_INIT, &sclp_status); + return 0; + } + + /* The sclp_init_mask failed. SCLP is broken, unregister and exit. */ + ctl_clear_bit(0,9); + unregister_early_external_interrupt(0x2401, sclp_interrupt_handler, + &ext_int_info_hwc); + + return rc; +} + +/* + * Register the SCLP event listener identified by REG. Return 0 on success. + * Some error codes and their meaning: + * + * -ENODEV = SCLP interface is not supported on this machine + * -EBUSY = there is already a listener registered for the requested + * event type + * -EIO = SCLP interface is currently not operational + */ +int +sclp_register(struct sclp_register *reg) +{ + unsigned long flags; + struct list_head *l; + struct sclp_register *t; + + if (!MACHINE_HAS_SCLP) + return -ENODEV; + + if (!test_bit(SCLP_INIT, &sclp_status)) + sclp_init(); + spin_lock_irqsave(&sclp_lock, flags); + /* check already registered event masks for collisions */ + list_for_each(l, &sclp_reg_list) { + t = list_entry(l, struct sclp_register, list); + if (t->receive_mask & reg->receive_mask || + t->send_mask & reg->send_mask) { + spin_unlock_irqrestore(&sclp_lock, flags); + return -EBUSY; + } + } + /* + * set present mask to 0 to trigger state change + * callback in sclp_init_mask + */ + reg->sclp_receive_mask = 0; + reg->sclp_send_mask = 0; + list_add(®->list, &sclp_reg_list); + spin_unlock_irqrestore(&sclp_lock, flags); + sclp_init_mask(); + return 0; +} + +/* + * Unregister the SCLP event listener identified by REG. + */ +void +sclp_unregister(struct sclp_register *reg) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_lock, flags); + list_del(®->list); + spin_unlock_irqrestore(&sclp_lock, flags); + sclp_init_mask(); +} + +#define SCLP_EVBUF_PROCESSED 0x80 + +/* + * Traverse array of event buffers contained in SCCB and remove all buffers + * with a set "processed" flag. Return the number of unprocessed buffers. + */ +int +sclp_remove_processed(struct sccb_header *sccb) +{ + struct evbuf_header *evbuf; + int unprocessed; + u16 remaining; + + evbuf = (struct evbuf_header *) (sccb + 1); + unprocessed = 0; + remaining = sccb->length - sizeof(struct sccb_header); + while (remaining > 0) { + remaining -= evbuf->length; + if (evbuf->flags & SCLP_EVBUF_PROCESSED) { + sccb->length -= evbuf->length; + memcpy((void *) evbuf, + (void *) ((addr_t) evbuf + evbuf->length), + remaining); + } else { + unprocessed++; + evbuf = (struct evbuf_header *) + ((addr_t) evbuf + evbuf->length); + } + } + + return unprocessed; +} + +EXPORT_SYMBOL(sclp_add_request); +EXPORT_SYMBOL(sclp_sync_wait); +EXPORT_SYMBOL(sclp_register); +EXPORT_SYMBOL(sclp_unregister); +EXPORT_SYMBOL(sclp_error_message); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_con.c linux.21rc1-ac2/drivers/s390/char/sclp_con.c --- linux.21rc1/drivers/s390/char/sclp_con.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_con.c 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,240 @@ +/* + * drivers/s390/char/sclp_con.c + * SCLP line mode console driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sclp.h" +#include "sclp_rw.h" + +#define SCLP_CON_PRINT_HEADER "sclp console driver: " + +#define sclp_console_major 4 /* TTYAUX_MAJOR */ +#define sclp_console_minor 64 +#define sclp_console_name "ttyS" + +/* Lock to guard over changes to global variables */ +static spinlock_t sclp_con_lock; +/* List of free pages that can be used for console output buffering */ +static struct list_head sclp_con_pages; +/* List of full struct sclp_buffer structures ready for output */ +static struct list_head sclp_con_outqueue; +/* Counter how many buffers are emitted (max 1) and how many */ +/* are on the output queue. */ +static int sclp_con_buffer_count; +/* Pointer to current console buffer */ +static struct sclp_buffer *sclp_conbuf; +/* Timer for delayed output of console messages */ +static struct timer_list sclp_con_timer; + +/* Output format for console messages */ +static unsigned short sclp_con_columns; +static unsigned short sclp_con_width_htab; + +static void +sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) +{ + unsigned long flags; + struct sclp_buffer *next; + void *page; + + /* Ignore return code - because console-writes aren't critical, + we do without a sophisticated error recovery mechanism. */ + page = sclp_unmake_buffer(buffer); + spin_lock_irqsave(&sclp_con_lock, flags); + /* Remove buffer from outqueue */ + list_del(&buffer->list); + sclp_con_buffer_count--; + list_add_tail((struct list_head *) page, &sclp_con_pages); + /* Check if there is a pending buffer on the out queue. */ + next = NULL; + if (!list_empty(&sclp_con_outqueue)) + next = list_entry(sclp_con_outqueue.next, + struct sclp_buffer, list); + spin_unlock_irqrestore(&sclp_con_lock, flags); + if (next != NULL) + sclp_emit_buffer(next, sclp_conbuf_callback); +} + +static inline void +__sclp_conbuf_emit(struct sclp_buffer *buffer) +{ + list_add_tail(&buffer->list, &sclp_con_outqueue); + if (sclp_con_buffer_count++ == 0) + sclp_emit_buffer(buffer, sclp_conbuf_callback); +} + +/* + * When this routine is called from the timer then we flush the + * temporary write buffer without further waiting on a final new line. + */ +static void +sclp_console_timeout(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_con_lock, flags); + if (sclp_conbuf != NULL) { + __sclp_conbuf_emit(sclp_conbuf); + sclp_conbuf = NULL; + } + spin_unlock_irqrestore(&sclp_con_lock, flags); +} + +/* + * Writes the given message to S390 system console + */ +static void +sclp_console_write(struct console *console, const char *message, + unsigned int count) +{ + unsigned long flags; + void *page; + int written; + + if (count == 0) + return; + spin_lock_irqsave(&sclp_con_lock, flags); + /* + * process escape characters, write message into buffer, + * send buffer to SCLP + */ + do { + /* make sure we have a console output buffer */ + if (sclp_conbuf == NULL) { + while (list_empty(&sclp_con_pages)) { + spin_unlock_irqrestore(&sclp_con_lock, flags); + sclp_sync_wait(); + spin_lock_irqsave(&sclp_con_lock, flags); + } + page = sclp_con_pages.next; + list_del((struct list_head *) page); + sclp_conbuf = sclp_make_buffer(page, sclp_con_columns, + sclp_con_width_htab); + } + /* try to write the string to the current output buffer */ + written = sclp_write(sclp_conbuf, (const unsigned char *) + message, count, 0); + if (written == -EFAULT || written == count) + break; + /* + * Not all characters could be written to the current + * output buffer. Emit the buffer, create a new buffer + * and then output the rest of the string. + */ + __sclp_conbuf_emit(sclp_conbuf); + sclp_conbuf = NULL; + message += written; + count -= written; + } while (count > 0); + /* Setup timer to output current console buffer after 1/10 second */ + if (sclp_conbuf != NULL && !timer_pending(&sclp_con_timer)) { + init_timer(&sclp_con_timer); + sclp_con_timer.function = sclp_console_timeout; + sclp_con_timer.data = 0UL; + sclp_con_timer.expires = jiffies + HZ/10; + add_timer(&sclp_con_timer); + } + spin_unlock_irqrestore(&sclp_con_lock, flags); +} + +/* returns the device number of the SCLP console */ +static kdev_t +sclp_console_device(struct console *c) +{ + return mk_kdev(sclp_console_major, sclp_console_minor); +} + +/* + * This routine is called from panic when the kernel + * is going to give up. We have to make sure that all buffers + * will be flushed to the SCLP. + */ +static void +sclp_console_unblank(void) +{ + unsigned long flags; + + spin_lock_irqsave(&sclp_con_lock, flags); + if (timer_pending(&sclp_con_timer)) + del_timer(&sclp_con_timer); + if (sclp_conbuf != NULL) { + __sclp_conbuf_emit(sclp_conbuf); + sclp_conbuf = NULL; + } + while (sclp_con_buffer_count > 0) { + spin_unlock_irqrestore(&sclp_con_lock, flags); + sclp_sync_wait(); + spin_lock_irqsave(&sclp_con_lock, flags); + } + spin_unlock_irqrestore(&sclp_con_lock, flags); +} + +/* + * used to register the SCLP console to the kernel and to + * give printk necessary information + */ +static struct console sclp_console = +{ + .name = sclp_console_name, + .write = sclp_console_write, + .device = sclp_console_device, + .unblank = sclp_console_unblank, + .flags = CON_PRINTBUFFER, + .index = 0 /* ttyS0 */ +}; + +/* + * called by console_init() in drivers/char/tty_io.c at boot-time. + */ +void __init +sclp_console_init(void) +{ + void *page; + int i; + + if (!CONSOLE_IS_SCLP) + return; + if (sclp_rw_init() != 0) + return; + /* Allocate pages for output buffering */ + INIT_LIST_HEAD(&sclp_con_pages); + for (i = 0; i < MAX_CONSOLE_PAGES; i++) { + page = alloc_bootmem_low_pages(PAGE_SIZE); + if (page == NULL) + return; + list_add_tail((struct list_head *) page, &sclp_con_pages); + } + INIT_LIST_HEAD(&sclp_con_outqueue); + spin_lock_init(&sclp_con_lock); + sclp_con_buffer_count = 0; + sclp_conbuf = NULL; + init_timer(&sclp_con_timer); + + /* Set output format */ + if (MACHINE_IS_VM) + /* + * save 4 characters for the CPU number + * written at start of each line by VM/CP + */ + sclp_con_columns = 76; + else + sclp_con_columns = 80; + sclp_con_width_htab = 8; + + /* enable printk-access to this driver */ + register_console(&sclp_console); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_cpi.c linux.21rc1-ac2/drivers/s390/char/sclp_cpi.c --- linux.21rc1/drivers/s390/char/sclp_cpi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_cpi.c 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,244 @@ +/* + * Author: Martin Peschke + * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation + * + * SCLP Control-Program Identification. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sclp.h" +#include "sclp_rw.h" + +#define CPI_LENGTH_SYSTEM_TYPE 8 +#define CPI_LENGTH_SYSTEM_NAME 8 +#define CPI_LENGTH_SYSPLEX_NAME 8 + +struct cpi_evbuf { + struct evbuf_header header; + u8 id_format; + u8 reserved0; + u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; + u64 reserved1; + u8 system_name[CPI_LENGTH_SYSTEM_NAME]; + u64 reserved2; + u64 system_level; + u64 reserved3; + u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; + u8 reserved4[16]; +} __attribute__((packed)); + +struct cpi_sccb { + struct sccb_header header; + struct cpi_evbuf cpi_evbuf; +} __attribute__((packed)); + +/* Event type structure for write message and write priority message */ +static struct sclp_register sclp_cpi_event = +{ + .send_mask = EvTyp_CtlProgIdent_Mask +}; + +MODULE_AUTHOR( + "Martin Peschke, IBM Deutschland Entwicklung GmbH " + ""); + +MODULE_DESCRIPTION( + "identify this operating system instance to the S/390 " + "or zSeries hardware"); + +static char *system_name = NULL; +MODULE_PARM(system_name, "s"); +MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); + +static char *sysplex_name = NULL; +#ifdef ALLOW_SYSPLEX_NAME +MODULE_PARM(sysplex_name, "s"); +MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); +#endif + +/* use default value for this field (as well as for system level) */ +static char *system_type = "LINUX"; + +static int +cpi_check_parms(void) +{ + /* reject if no system type specified */ + if (!system_type) { + printk("cpi: bug: no system type specified\n"); + return -EINVAL; + } + + /* reject if system type larger than 8 characters */ + if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { + printk("cpi: bug: system type has length of %li characters - " + "only %i characters supported\n", + strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); + return -EINVAL; + } + + /* reject if no system name specified */ + if (!system_name) { + printk("cpi: no system name specified\n"); + return -EINVAL; + } + + /* reject if system name larger than 8 characters */ + if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { + printk("cpi: system name has length of %li characters - " + "only %i characters supported\n", + strlen(system_name), CPI_LENGTH_SYSTEM_NAME); + return -EINVAL; + } + + /* reject if specified sysplex name larger than 8 characters */ + if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { + printk("cpi: sysplex name has length of %li characters" + " - only %i characters supported\n", + strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); + return -EINVAL; + } + return 0; +} + +static void +cpi_callback(struct sclp_req *req, void *data) +{ + struct semaphore *sem; + + sem = (struct semaphore *) data; + up(sem); +} + +static struct sclp_req * +cpi_prepare_req(void) +{ + struct sclp_req *req; + struct cpi_sccb *sccb; + struct cpi_evbuf *evb; + + req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL); + if (req == NULL) + return ERR_PTR(-ENOMEM); + sccb = (struct cpi_sccb *) get_free_page(GFP_KERNEL | GFP_DMA); + if (sccb == NULL) { + kfree(req); + return ERR_PTR(-ENOMEM); + } + memset(sccb, 0, sizeof(struct cpi_sccb)); + + /* setup SCCB for Control-Program Identification */ + sccb->header.length = sizeof(struct cpi_sccb); + sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); + sccb->cpi_evbuf.header.type = 0x0B; + evb = &sccb->cpi_evbuf; + + /* set system type */ + memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); + memcpy(evb->system_type, system_type, strlen(system_type)); + sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); + EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); + + /* set system name */ + memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); + memcpy(evb->system_name, system_name, strlen(system_name)); + sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); + EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); + + /* set sytem level */ + evb->system_level = LINUX_VERSION_CODE; + + /* set sysplex name */ + if (sysplex_name) { + memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); + memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); + sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); + EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); + } + + /* prepare request data structure presented to SCLP driver */ + req->command = SCLP_CMDW_WRITEDATA; + req->sccb = sccb; + req->status = SCLP_REQ_FILLED; + req->callback = cpi_callback; + return req; +} + +static void +cpi_free_req(struct sclp_req *req) +{ + free_page((unsigned long) req->sccb); + kfree(req); +} + +static int __init +cpi_module_init(void) +{ + struct semaphore sem; + struct sclp_req *req; + int rc; + + rc = cpi_check_parms(); + if (rc) + return rc; + + rc = sclp_register(&sclp_cpi_event); + if (rc) { + /* could not register sclp event. Die. */ + printk("cpi: could not register to hardware console.\n"); + return -EINVAL; + } + if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { + printk("cpi: no control program identification support\n"); + sclp_unregister(&sclp_cpi_event); + return -ENOTSUPP; + } + + req = cpi_prepare_req(); + if (IS_ERR(req)) { + printk("cpi: couldn't allocate request\n"); + sclp_unregister(&sclp_cpi_event); + return PTR_ERR(req); + } + + /* Prepare semaphore */ + sema_init(&sem, 0); + req->callback_data = &sem; + /* Add request to sclp queue */ + sclp_add_request(req); + /* make "insmod" sleep until callback arrives */ + down(&sem); + + rc = ((struct cpi_sccb *) req->sccb)->header.response_code; + if (rc != 0x0020) { + printk("cpi: failed with response code 0x%x\n", rc); + rc = -ECOMM; + } else + rc = 0; + + cpi_free_req(req); + sclp_unregister(&sclp_cpi_event); + + return rc; +} + + +static void __exit cpi_module_exit(void) +{ +} + + +/* declare driver module init/cleanup functions */ +module_init(cpi_module_init); +module_exit(cpi_module_exit); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp.h linux.21rc1-ac2/drivers/s390/char/sclp.h --- linux.21rc1/drivers/s390/char/sclp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp.h 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,157 @@ +/* + * drivers/s390/char/sclp.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#ifndef __SCLP_H__ +#define __SCLP_H__ + +#include +#include + +#include + +/* maximum number of pages concerning our own memory management */ +#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) +#define MAX_CONSOLE_PAGES 4 + +#define EvTyp_OpCmd 0x01 +#define EvTyp_Msg 0x02 +#define EvTyp_StateChange 0x08 +#define EvTyp_PMsgCmd 0x09 +#define EvTyp_CntlProgOpCmd 0x20 +#define EvTyp_CntlProgIdent 0x0B +#define EvTyp_SigQuiesce 0x1D +#define EvTyp_VT220Msg 0x1A + +#define EvTyp_OpCmd_Mask 0x80000000 +#define EvTyp_Msg_Mask 0x40000000 +#define EvTyp_StateChange_Mask 0x01000000 +#define EvTyp_PMsgCmd_Mask 0x00800000 +#define EvTyp_CtlProgOpCmd_Mask 0x00000001 +#define EvTyp_CtlProgIdent_Mask 0x00200000 +#define EvTyp_SigQuiesce_Mask 0x00000008 +#define EvTyp_VT220Msg_Mask 0x00000040 + +#define GnrlMsgFlgs_DOM 0x8000 +#define GnrlMsgFlgs_SndAlrm 0x4000 +#define GnrlMsgFlgs_HoldMsg 0x2000 + +#define LnTpFlgs_CntlText 0x8000 +#define LnTpFlgs_LabelText 0x4000 +#define LnTpFlgs_DataText 0x2000 +#define LnTpFlgs_EndText 0x1000 +#define LnTpFlgs_PromptText 0x0800 + +typedef unsigned int sclp_cmdw_t; + +#define SCLP_CMDW_READDATA 0x00770005 +#define SCLP_CMDW_WRITEDATA 0x00760005 +#define SCLP_CMDW_WRITEMASK 0x00780005 + +#define GDS_ID_MDSMU 0x1310 +#define GDS_ID_MDSRouteInfo 0x1311 +#define GDS_ID_AgUnWrkCorr 0x1549 +#define GDS_ID_SNACondReport 0x1532 +#define GDS_ID_CPMSU 0x1212 +#define GDS_ID_RoutTargInstr 0x154D +#define GDS_ID_OpReq 0x8070 +#define GDS_ID_TextCmd 0x1320 + +#define GDS_KEY_SelfDefTextMsg 0x31 + +typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ + +struct sccb_header { + u16 length; + u8 function_code; + u8 control_mask[3]; + u16 response_code; +} __attribute__((packed)); + +struct gds_subvector { + u8 length; + u8 key; +} __attribute__((packed)); + +struct gds_vector { + u16 length; + u16 gds_id; +} __attribute__((packed)); + +struct evbuf_header { + u16 length; + u8 type; + u8 flags; + u16 _reserved; +} __attribute__((packed)); + +struct sclp_req { + struct list_head list; /* list_head for request queueing. */ + sclp_cmdw_t command; /* sclp command to execute */ + void *sccb; /* pointer to the sccb to execute */ + char status; /* status of this request */ + /* Callback that is called after reaching final status. */ + void (*callback)(struct sclp_req *, void *data); + void *callback_data; +}; + +#define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */ +#define SCLP_REQ_QUEUED 0x01 /* request is queued to be processed */ +#define SCLP_REQ_RUNNING 0x02 /* request is currently running */ +#define SCLP_REQ_DONE 0x03 /* request is completed successfully */ +#define SCLP_REQ_FAILED 0x05 /* request is finally failed */ + +/* function pointers that a high level driver has to use for registration */ +/* of some routines it wants to be called from the low level driver */ +struct sclp_register { + struct list_head list; + /* event masks this user is registered for */ + sccb_mask_t receive_mask; + sccb_mask_t send_mask; + /* actually present events */ + sccb_mask_t sclp_receive_mask; + sccb_mask_t sclp_send_mask; + /* called if event type availability changes */ + void (*state_change_fn)(struct sclp_register *); + /* called for events in cp_receive_mask/sclp_receive_mask */ + void (*receiver_fn)(struct evbuf_header *); +}; + +/* externals from sclp.c */ +void sclp_add_request(struct sclp_req *req); +void sclp_sync_wait(void); +int sclp_register(struct sclp_register *reg); +void sclp_unregister(struct sclp_register *reg); +char *sclp_error_message(u16 response_code); +int sclp_remove_processed(struct sccb_header *sccb); + +/* useful inlines */ + +/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ +/* translate single character from ASCII to EBCDIC */ +static inline unsigned char +sclp_ascebc(unsigned char ch) +{ + return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch]; +} + +/* translate string from EBCDIC to ASCII */ +static inline void +sclp_ebcasc_str(unsigned char *str, int nr) +{ + (MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr); +} + +/* translate string from ASCII to EBCDIC */ +static inline void +sclp_ascebc_str(unsigned char *str, int nr) +{ + (MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr); +} + +#endif /* __SCLP_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_rw.c linux.21rc1-ac2/drivers/s390/char/sclp_rw.c --- linux.21rc1/drivers/s390/char/sclp_rw.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_rw.c 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,496 @@ +/* + * drivers/s390/char/sclp_rw.c + * driver: reading from and writing to system console on S/390 via SCLP + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sclp.h" +#include "sclp_rw.h" + +#define SCLP_RW_PRINT_HEADER "sclp low level driver: " + +/* + * The room for the SCCB (only for writing) is not equal to a pages size + * (as it is specified as the maximum size in the the SCLP ducumentation) + * because of the additional data structure described above. + */ +#define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) + +/* Event type structure for write message and write priority message */ +static struct sclp_register sclp_rw_event = { + .send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask +}; + +/* + * Setup a sclp write buffer. Gets a page as input (4K) and returns + * a pointer to a struct sclp_buffer structure that is located at the + * end of the input page. This reduces the buffer space by a few + * bytes but simplifies things. + */ +struct sclp_buffer * +sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) +{ + struct sclp_buffer *buffer; + struct write_sccb *sccb; + + sccb = (struct write_sccb *) page; + /* + * We keep the struct sclp_buffer structure at the end + * of the sccb page. + */ + buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1; + buffer->sccb = sccb; + buffer->retry_count = 0; + init_timer(&buffer->retry_timer); + buffer->mto_number = 0; + buffer->mto_char_sum = 0; + buffer->current_line = NULL; + buffer->current_length = 0; + buffer->columns = columns; + buffer->htab = htab; + + /* initialize sccb */ + memset(sccb, 0, sizeof(struct write_sccb)); + sccb->header.length = sizeof(struct write_sccb); + sccb->msg_buf.header.length = sizeof(struct msg_buf); + sccb->msg_buf.header.type = EvTyp_Msg; + sccb->msg_buf.mdb.header.length = sizeof(struct mdb); + sccb->msg_buf.mdb.header.type = 1; + sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */ + sccb->msg_buf.mdb.header.revision_code = 1; + sccb->msg_buf.mdb.go.length = sizeof(struct go); + sccb->msg_buf.mdb.go.type = 1; + + return buffer; +} + +/* + * Return a pointer to the orignal page that has been used to create + * the buffer. + */ +void * +sclp_unmake_buffer(struct sclp_buffer *buffer) +{ + return buffer->sccb; +} + +/* + * Initialize a new Message Text Object (MTO) at the end of the provided buffer + * with enough room for max_len characters. Return 0 on success. + */ +static int +sclp_initialize_mto(struct sclp_buffer *buffer, int max_len) +{ + struct write_sccb *sccb; + struct mto *mto; + int mto_size; + + /* max size of new Message Text Object including message text */ + mto_size = sizeof(struct mto) + max_len; + + /* check if current buffer sccb can contain the mto */ + sccb = buffer->sccb; + if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size) + return -ENOMEM; + + /* find address of new message text object */ + mto = (struct mto *)(((addr_t) sccb) + sccb->header.length); + + /* + * fill the new Message-Text Object, + * starting behind the former last byte of the SCCB + */ + memset(mto, 0, sizeof(struct mto)); + mto->length = sizeof(struct mto); + mto->type = 4; /* message text object */ + mto->line_type_flags = LnTpFlgs_EndText; /* end text */ + + /* set pointer to first byte after struct mto. */ + buffer->current_line = (char *) (mto + 1); + buffer->current_length = 0; + + return 0; +} + +/* + * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of + * MTO, enclosing MDB, event buffer and SCCB. + */ +static void +sclp_finalize_mto(struct sclp_buffer *buffer) +{ + struct write_sccb *sccb; + struct mto *mto; + int str_len, mto_size; + + str_len = buffer->current_length; + buffer->current_line = NULL; + buffer->current_length = 0; + + /* real size of new Message Text Object including message text */ + mto_size = sizeof(struct mto) + str_len; + + /* find address of new message text object */ + sccb = buffer->sccb; + mto = (struct mto *)(((addr_t) sccb) + sccb->header.length); + + /* set size of message text object */ + mto->length = mto_size; + + /* + * update values of sizes + * (SCCB, Event(Message) Buffer, Message Data Block) + */ + sccb->header.length += mto_size; + sccb->msg_buf.header.length += mto_size; + sccb->msg_buf.mdb.header.length += mto_size; + + /* + * count number of buffered messages (= number of Message Text + * Objects) and number of buffered characters + * for the SCCB currently used for buffering and at all + */ + buffer->mto_number++; + buffer->mto_char_sum += str_len; +} + +/* + * processing of a message including escape characters, + * returns number of characters written to the output sccb + * ("processed" means that is not guaranteed that the character have already + * been sent to the SCLP but that it will be done at least next time the SCLP + * is not busy) + */ +int +sclp_write(struct sclp_buffer *buffer, + const unsigned char *msg, int count, int from_user) +{ + int spaces, i_msg; + char ch; + int rc; + + /* + * parse msg for escape sequences (\t,\v ...) and put formated + * msg into an mto (created by sclp_initialize_mto). + * + * We have to do this work ourselfs because there is no support for + * these characters on the native machine and only partial support + * under VM (Why does VM interpret \n but the native machine doesn't ?) + * + * Depending on i/o-control setting the message is always written + * immediately or we wait for a final new line maybe coming with the + * next message. Besides we avoid a buffer overrun by writing its + * content. + * + * RESTRICTIONS: + * + * \r and \b work within one line because we are not able to modify + * previous output that have already been accepted by the SCLP. + * + * \t combined with following \r is not correctly represented because + * \t is expanded to some spaces but \r does not know about a + * previous \t and decreases the current position by one column. + * This is in order to a slim and quick implementation. + */ + for (i_msg = 0; i_msg < count; i_msg++) { + if (from_user) { + if (get_user(ch, msg + i_msg) != 0) + return -EFAULT; + } else + ch = msg[i_msg]; + + switch (ch) { + case '\n': /* new line, line feed (ASCII) */ + /* check if new mto needs to be created */ + if (buffer->current_line == NULL) { + rc = sclp_initialize_mto(buffer, 0); + if (rc) + return i_msg; + } + sclp_finalize_mto(buffer); + break; + case '\a': /* bell, one for several times */ + /* set SCLP sound alarm bit in General Object */ + buffer->sccb->msg_buf.mdb.go.general_msg_flags |= + GnrlMsgFlgs_SndAlrm; + break; + case '\t': /* horizontal tabulator */ + /* check if new mto needs to be created */ + if (buffer->current_line == NULL) { + rc = sclp_initialize_mto(buffer, + buffer->columns); + if (rc) + return i_msg; + } + /* "go to (next htab-boundary + 1, same line)" */ + do { + if (buffer->current_length >= buffer->columns) + break; + /* ok, add a blank */ + *buffer->current_line++ = 0x40; + buffer->current_length++; + } while (buffer->current_length % buffer->htab); + break; + case '\f': /* form feed */ + case '\v': /* vertical tabulator */ + /* "go to (actual column, actual line + 1)" */ + /* = new line, leading spaces */ + if (buffer->current_line != NULL) { + spaces = buffer->current_length; + sclp_finalize_mto(buffer); + rc = sclp_initialize_mto(buffer, + buffer->columns); + if (rc) + return i_msg; + memset(buffer->current_line, 0x40, spaces); + buffer->current_line += spaces; + buffer->current_length = spaces; + } else { + /* one an empty line this is the same as \n */ + rc = sclp_initialize_mto(buffer, + buffer->columns); + if (rc) + return i_msg; + sclp_finalize_mto(buffer); + } + break; + case '\b': /* backspace */ + /* "go to (actual column - 1, actual line)" */ + /* decrement counter indicating position, */ + /* do not remove last character */ + if (buffer->current_line != NULL && + buffer->current_length > 0) { + buffer->current_length--; + buffer->current_line--; + } + break; + case 0x00: /* end of string */ + /* transfer current line to SCCB */ + if (buffer->current_line != NULL) + sclp_finalize_mto(buffer); + /* skip the rest of the message including the 0 byte */ + i_msg = count; + break; + default: /* no escape character */ + /* do not output unprintable characters */ + if (!isprint(ch)) + break; + /* check if new mto needs to be created */ + if (buffer->current_line == NULL) { + rc = sclp_initialize_mto(buffer, + buffer->columns); + if (rc) + return i_msg; + } + *buffer->current_line++ = sclp_ascebc(ch); + buffer->current_length++; + break; + } + /* check if current mto is full */ + if (buffer->current_line != NULL && + buffer->current_length >= buffer->columns) + sclp_finalize_mto(buffer); + } + + /* return number of processed characters */ + return i_msg; +} + +/* + * Return the number of free bytes in the sccb + */ +int +sclp_buffer_space(struct sclp_buffer *buffer) +{ + int count; + + count = MAX_SCCB_ROOM - buffer->sccb->header.length; + if (buffer->current_line != NULL) + count -= sizeof(struct mto) + buffer->current_length; + return count; +} + +/* + * Return number of characters in buffer + */ +int +sclp_chars_in_buffer(struct sclp_buffer *buffer) +{ + int count; + + count = buffer->mto_char_sum; + if (buffer->current_line != NULL) + count += buffer->current_length; + return count; +} + +/* + * sets or provides some values that influence the drivers behaviour + */ +void +sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns) +{ + buffer->columns = columns; + if (buffer->current_line != NULL && + buffer->current_length > buffer->columns) + sclp_finalize_mto(buffer); +} + +void +sclp_set_htab(struct sclp_buffer *buffer, unsigned short htab) +{ + buffer->htab = htab; +} + +/* + * called by sclp_console_init and/or sclp_tty_init + */ +int +sclp_rw_init(void) +{ + static int init_done = 0; + int rc; + + if (init_done) + return 0; + + rc = sclp_register(&sclp_rw_event); + if (rc == 0) + init_done = 1; + return rc; +} + +static void +sclp_buffer_retry(unsigned long data) +{ + struct sclp_buffer *buffer = (struct sclp_buffer *) data; + buffer->request.status = SCLP_REQ_FILLED; + buffer->sccb->header.response_code = 0x0000; + sclp_add_request(&buffer->request); +} + +#define SCLP_BUFFER_MAX_RETRY 5 +#define SCLP_BUFFER_RETRY_INTERVAL 2 + +/* + * second half of Write Event Data-function that has to be done after + * interruption indicating completion of Service Call. + */ +static void +sclp_writedata_callback(struct sclp_req *request, void *data) +{ + int rc; + struct sclp_buffer *buffer; + struct write_sccb *sccb; + + buffer = (struct sclp_buffer *) data; + sccb = buffer->sccb; + + if (request->status == SCLP_REQ_FAILED) { + if (buffer->callback != NULL) + buffer->callback(buffer, -EIO); + return; + } + /* check SCLP response code and choose suitable action */ + switch (sccb->header.response_code) { + case 0x0020 : + /* Normal completion, buffer processed, message(s) sent */ + rc = 0; + break; + + case 0x0340: /* Contained SCLP equipment check */ + if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) { + rc = -EIO; + break; + } + /* remove processed buffers and requeue rest */ + if (sclp_remove_processed((struct sccb_header *) sccb) > 0) { + /* not all buffers were processed */ + sccb->header.response_code = 0x0000; + buffer->request.status = SCLP_REQ_FILLED; + sclp_add_request(request); + return; + } + rc = 0; + break; + + case 0x0040: /* SCLP equipment check */ + case 0x05f0: /* Target resource in improper state */ + if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) { + rc = -EIO; + break; + } + /* wait some time, then retry request */ + buffer->retry_timer.function = sclp_buffer_retry; + buffer->retry_timer.data = (unsigned long) buffer; + buffer->retry_timer.expires = jiffies + + SCLP_BUFFER_RETRY_INTERVAL*HZ; + add_timer(&buffer->retry_timer); + return; + + default: + if (sccb->header.response_code == 0x71f0) + rc = -ENOMEM; + else + rc = -EINVAL; + break; + } + if (buffer->callback != NULL) + buffer->callback(buffer, rc); +} + +/* + * Setup the request structure in the struct sclp_buffer to do SCLP Write + * Event Data and pass the request to the core SCLP loop. + */ +void +sclp_emit_buffer(struct sclp_buffer *buffer, + void (*callback)(struct sclp_buffer *, int)) +{ + struct write_sccb *sccb; + + /* add current line if there is one */ + if (buffer->current_line != NULL) + sclp_finalize_mto(buffer); + + /* Are there messages in the output buffer ? */ + if (buffer->mto_number == 0) { + if (callback != NULL) + callback(buffer, 0); + return; + } + + sccb = buffer->sccb; + if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask) + /* Use normal write message */ + sccb->msg_buf.header.type = EvTyp_Msg; + else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask) + /* Use write priority message */ + sccb->msg_buf.header.type = EvTyp_PMsgCmd; + else { + if (callback != NULL) + callback(buffer, -ENOSYS); + return; + } + buffer->request.command = SCLP_CMDW_WRITEDATA; + buffer->request.status = SCLP_REQ_FILLED; + buffer->request.callback = sclp_writedata_callback; + buffer->request.callback_data = buffer; + buffer->request.sccb = sccb; + buffer->callback = callback; + sclp_add_request(&buffer->request); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_rw.h linux.21rc1-ac2/drivers/s390/char/sclp_rw.h --- linux.21rc1/drivers/s390/char/sclp_rw.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_rw.h 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,98 @@ +/* + * drivers/s390/char/sclp_rw.h + * interface to the SCLP-read/write driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#ifndef __SCLP_RW_H__ +#define __SCLP_RW_H__ + +#include +#include + +struct mto { + u16 length; + u16 type; + u16 line_type_flags; + u8 alarm_control; + u8 _reserved[3]; +} __attribute__((packed)); + +struct go { + u16 length; + u16 type; + u32 domid; + u8 hhmmss_time[8]; + u8 th_time[3]; + u8 reserved_0; + u8 dddyyyy_date[7]; + u8 _reserved_1; + u16 general_msg_flags; + u8 _reserved_2[10]; + u8 originating_system_name[8]; + u8 job_guest_name[8]; +} __attribute__((packed)); + +struct mdb_header { + u16 length; + u16 type; + u32 tag; + u32 revision_code; +} __attribute__((packed)); + +struct mdb { + struct mdb_header header; + struct go go; +} __attribute__((packed)); + +struct msg_buf { + struct evbuf_header header; + struct mdb mdb; +} __attribute__((packed)); + +struct write_sccb { + struct sccb_header header; + struct msg_buf msg_buf; +} __attribute__((packed)); + +/* The number of empty mto buffers that can be contained in a single sccb. */ +#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \ + sizeof(struct write_sccb)) / sizeof(struct mto)) + +/* + * data structure for information about list of SCCBs (only for writing), + * will be located at the end of a SCCBs page + */ +struct sclp_buffer { + struct list_head list; /* list_head for sccb_info chain */ + struct sclp_req request; + struct write_sccb *sccb; + char *current_line; + int current_length; + int retry_count; + struct timer_list retry_timer; + /* output format settings */ + unsigned short columns; + unsigned short htab; + /* statistics about this buffer */ + unsigned int mto_char_sum; /* # chars in sccb */ + unsigned int mto_number; /* # mtos in sccb */ + /* Callback that is called after reaching final status. */ + void (*callback)(struct sclp_buffer *, int); +}; + +int sclp_rw_init(void); +struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short); +void *sclp_unmake_buffer(struct sclp_buffer *); +int sclp_buffer_space(struct sclp_buffer *); +int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int, int); +void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int)); +void sclp_set_columns(struct sclp_buffer *, unsigned short); +void sclp_set_htab(struct sclp_buffer *, unsigned short); +int sclp_chars_in_buffer(struct sclp_buffer *); + +#endif /* __SCLP_RW_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_tty.c linux.21rc1-ac2/drivers/s390/char/sclp_tty.c --- linux.21rc1/drivers/s390/char/sclp_tty.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_tty.c 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,816 @@ +/* + * drivers/s390/char/sclp_tty.c + * SCLP line mode terminal driver. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctrlchar.h" +#include "sclp.h" +#include "sclp_rw.h" +#include "sclp_tty.h" + +#define SCLP_TTY_PRINT_HEADER "sclp tty driver: " + +/* + * size of a buffer that collects single characters coming in + * via sclp_tty_put_char() + */ +#define SCLP_TTY_BUF_SIZE 512 + +/* + * There is exactly one SCLP terminal, so we can keep things simple + * and allocate all variables statically. + */ + +/* Lock to guard over changes to global variables. */ +static spinlock_t sclp_tty_lock; +/* List of free pages that can be used for console output buffering. */ +static struct list_head sclp_tty_pages; +/* List of full struct sclp_buffer structures ready for output. */ +static struct list_head sclp_tty_outqueue; +/* Counter how many buffers are emitted. */ +static int sclp_tty_buffer_count; +/* Pointer to current console buffer. */ +static struct sclp_buffer *sclp_ttybuf; +/* Timer for delayed output of console messages. */ +static struct timer_list sclp_tty_timer; +/* Waitqueue to wait for buffers to get empty. */ +static wait_queue_head_t sclp_tty_waitq; + +static struct tty_struct *sclp_tty; +static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; +static unsigned short int sclp_tty_chars_count; + +static struct tty_driver sclp_tty_driver; +static struct tty_struct * sclp_tty_table[1]; +static struct termios * sclp_tty_termios[1]; +static struct termios * sclp_tty_termios_locked[1]; +static int sclp_tty_refcount = 0; + +extern struct termios tty_std_termios; + +static struct sclp_ioctls sclp_ioctls; +static struct sclp_ioctls sclp_ioctls_init = +{ + 8, /* 1 hor. tab. = 8 spaces */ + 0, /* no echo of input by this driver */ + 80, /* 80 characters/line */ + 1, /* write after 1/10 s without final new line */ + MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */ + MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */ + 0, /* do not convert to lower case */ + 0x6c /* to seprate upper and lower case */ + /* ('%' in EBCDIC) */ +}; + +/* This routine is called whenever we try to open a SCLP terminal. */ +static int +sclp_tty_open(struct tty_struct *tty, struct file *filp) +{ + sclp_tty = tty; + tty->driver_data = NULL; + tty->low_latency = 0; + return 0; +} + +/* This routine is called when the SCLP terminal is closed. */ +static void +sclp_tty_close(struct tty_struct *tty, struct file *filp) +{ + if (tty->count > 1) + return; + sclp_tty = NULL; +} + +/* execute commands to control the i/o behaviour of the SCLP tty at runtime */ +static int +sclp_tty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned int obuf; + int check; + int rc; + + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + rc = 0; + check = 0; + switch (cmd) { + case TIOCSCLPSHTAB: + /* set width of horizontal tab */ + if (get_user(sclp_ioctls.htab, (unsigned short *) arg)) + rc = -EFAULT; + else + check = 1; + break; + case TIOCSCLPGHTAB: + /* get width of horizontal tab */ + if (put_user(sclp_ioctls.htab, (unsigned short *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSECHO: + /* enable/disable echo of input */ + if (get_user(sclp_ioctls.echo, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPGECHO: + /* Is echo of input enabled ? */ + if (put_user(sclp_ioctls.echo, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSCOLS: + /* set number of columns for output */ + if (get_user(sclp_ioctls.columns, (unsigned short *) arg)) + rc = -EFAULT; + else + check = 1; + break; + case TIOCSCLPGCOLS: + /* get number of columns for output */ + if (put_user(sclp_ioctls.columns, (unsigned short *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSNL: + /* enable/disable writing without final new line character */ + if (get_user(sclp_ioctls.final_nl, (signed char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPGNL: + /* Is writing without final new line character enabled ? */ + if (put_user(sclp_ioctls.final_nl, (signed char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSOBUF: + /* + * set the maximum buffers size for output, will be rounded + * up to next 4kB boundary and stored as number of SCCBs + * (4kB Buffers) limitation: 256 x 4kB + */ + if (get_user(obuf, (unsigned int *) arg) == 0) { + if (obuf & 0xFFF) + sclp_ioctls.max_sccb = (obuf >> 12) + 1; + else + sclp_ioctls.max_sccb = (obuf >> 12); + } else + rc = -EFAULT; + break; + case TIOCSCLPGOBUF: + /* get the maximum buffers size for output */ + obuf = sclp_ioctls.max_sccb << 12; + if (put_user(obuf, (unsigned int *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPGKBUF: + /* get the number of buffers got from kernel at startup */ + if (put_user(sclp_ioctls.kmem_sccb, (unsigned short *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSCASE: + /* enable/disable conversion from upper to lower case */ + if (get_user(sclp_ioctls.tolower, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPGCASE: + /* Is conversion from upper to lower case of input enabled? */ + if (put_user(sclp_ioctls.tolower, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSDELIM: + /* + * set special character used for separating upper and + * lower case, 0x00 disables this feature + */ + if (get_user(sclp_ioctls.delim, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPGDELIM: + /* + * get special character used for separating upper and + * lower case, 0x00 disables this feature + */ + if (put_user(sclp_ioctls.delim, (unsigned char *) arg)) + rc = -EFAULT; + break; + case TIOCSCLPSINIT: + /* set initial (default) sclp ioctls */ + sclp_ioctls = sclp_ioctls_init; + check = 1; + break; + default: + rc = -ENOIOCTLCMD; + break; + } + if (check) { + spin_lock_irqsave(&sclp_tty_lock, flags); + if (sclp_ttybuf != NULL) { + sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab); + sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns); + } + spin_unlock_irqrestore(&sclp_tty_lock, flags); + } + return rc; +} + +/* + * This routine returns the numbers of characters the tty driver + * will accept for queuing to be written. This number is subject + * to change as output buffers get emptied, or if the output flow + * control is acted. This is not an exact number because not every + * character needs the same space in the sccb. The worst case is + * a string of newlines. Every newlines creates a new mto which + * needs 8 bytes. + */ +static int +sclp_tty_write_room (struct tty_struct *tty) +{ + unsigned long flags; + struct list_head *l; + int count; + + spin_lock_irqsave(&sclp_tty_lock, flags); + count = 0; + if (sclp_ttybuf != NULL) + count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto); + list_for_each(l, &sclp_tty_pages) + count += NR_EMPTY_MTO_PER_SCCB; + spin_unlock_irqrestore(&sclp_tty_lock, flags); + return count; +} + +static void +sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) +{ + unsigned long flags; + struct sclp_buffer *next; + void *page; + + /* Ignore return code - because tty-writes aren't critical, + we do without a sophisticated error recovery mechanism. */ + page = sclp_unmake_buffer(buffer); + spin_lock_irqsave(&sclp_tty_lock, flags); + /* Remove buffer from outqueue */ + list_del(&buffer->list); + sclp_tty_buffer_count--; + list_add_tail((struct list_head *) page, &sclp_tty_pages); + /* Check if there is a pending buffer on the out queue. */ + next = NULL; + if (!list_empty(&sclp_tty_outqueue)) + next = list_entry(sclp_tty_outqueue.next, + struct sclp_buffer, list); + spin_unlock_irqrestore(&sclp_tty_lock, flags); + if (next != NULL) + sclp_emit_buffer(next, sclp_ttybuf_callback); + wake_up(&sclp_tty_waitq); + /* check if the tty needs a wake up call */ + if (sclp_tty != NULL) { + if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + sclp_tty->ldisc.write_wakeup) + (sclp_tty->ldisc.write_wakeup)(sclp_tty); + wake_up_interruptible(&sclp_tty->write_wait); + } +} + +static inline void +__sclp_ttybuf_emit(struct sclp_buffer *buffer) +{ + unsigned long flags; + int count; + + spin_lock_irqsave(&sclp_tty_lock, flags); + list_add_tail(&buffer->list, &sclp_tty_outqueue); + count = sclp_tty_buffer_count++; + spin_unlock_irqrestore(&sclp_tty_lock, flags); + + if (count == 0) + sclp_emit_buffer(buffer, sclp_ttybuf_callback); +} + +/* + * When this routine is called from the timer then we flush the + * temporary write buffer. + */ +static void +sclp_tty_timeout(unsigned long data) +{ + unsigned long flags; + struct sclp_buffer *buf; + + spin_lock_irqsave(&sclp_tty_lock, flags); + buf = sclp_ttybuf; + sclp_ttybuf = NULL; + spin_unlock_irqrestore(&sclp_tty_lock, flags); + + if (buf != NULL) { + __sclp_ttybuf_emit(buf); + } +} + +/* + * Write a string to the sclp tty. + */ +static void +sclp_tty_write_string(const unsigned char *str, int count, int from_user) +{ + unsigned long flags; + void *page; + int written; + struct sclp_buffer *buf; + + if (count <= 0) + return; + spin_lock_irqsave(&sclp_tty_lock, flags); + do { + /* Create a sclp output buffer if none exists yet */ + if (sclp_ttybuf == NULL) { + while (list_empty(&sclp_tty_pages)) { + spin_unlock_irqrestore(&sclp_tty_lock, flags); + wait_event(sclp_tty_waitq, + !list_empty(&sclp_tty_pages)); + spin_lock_irqsave(&sclp_tty_lock, flags); + } + page = sclp_tty_pages.next; + list_del((struct list_head *) page); + sclp_ttybuf = sclp_make_buffer(page, + sclp_ioctls.columns, + sclp_ioctls.htab); + } + /* try to write the string to the current output buffer */ + written = sclp_write(sclp_ttybuf, str, count, from_user); + if (written == -EFAULT || written == count) + break; + /* + * Not all characters could be written to the current + * output buffer. Emit the buffer, create a new buffer + * and then output the rest of the string. + */ + buf = sclp_ttybuf; + sclp_ttybuf = NULL; + spin_unlock_irqrestore(&sclp_tty_lock, flags); + __sclp_ttybuf_emit(buf); + spin_lock_irqsave(&sclp_tty_lock, flags); + str += written; + count -= written; + } while (count > 0); + /* Setup timer to output current console buffer after 1/10 second */ + if (sclp_ioctls.final_nl) { + if (sclp_ttybuf != NULL && !timer_pending(&sclp_tty_timer)) { + init_timer(&sclp_tty_timer); + sclp_tty_timer.function = sclp_tty_timeout; + sclp_tty_timer.data = 0UL; + sclp_tty_timer.expires = jiffies + HZ/10; + add_timer(&sclp_tty_timer); + } + } else { + __sclp_ttybuf_emit(sclp_ttybuf); + sclp_ttybuf = NULL; + } + spin_unlock_irqrestore(&sclp_tty_lock, flags); +} + +/* + * This routine is called by the kernel to write a series of characters to the + * tty device. The characters may come from user space or kernel space. This + * routine will return the number of characters actually accepted for writing. + */ +static int +sclp_tty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + if (sclp_tty_chars_count > 0) { + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); + sclp_tty_chars_count = 0; + } + sclp_tty_write_string(buf, count, from_user); + return count; +} + +/* + * This routine is called by the kernel to write a single character to the tty + * device. If the kernel uses this routine, it must call the flush_chars() + * routine (if defined) when it is done stuffing characters into the driver. + * + * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver. + * If the given character is a '\n' the contents of the SCLP write buffer + * - including previous characters from sclp_tty_put_char() and strings from + * sclp_write() without final '\n' - will be written. + */ +static void +sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + sclp_tty_chars[sclp_tty_chars_count++] = ch; + if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); + sclp_tty_chars_count = 0; + } +} + +/* + * This routine is called by the kernel after it has written a series of + * characters to the tty device using put_char(). + */ +static void +sclp_tty_flush_chars(struct tty_struct *tty) +{ + if (sclp_tty_chars_count > 0) { + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); + sclp_tty_chars_count = 0; + } +} + +/* + * This routine returns the number of characters in the write buffer of the + * SCLP driver. The provided number includes all characters that are stored + * in the SCCB (will be written next time the SCLP is not busy) as well as + * characters in the write buffer (will not be written as long as there is a + * final line feed missing). + */ +static int +sclp_tty_chars_in_buffer(struct tty_struct *tty) +{ + unsigned long flags; + struct list_head *l; + struct sclp_buffer *t; + int count; + + spin_lock_irqsave(&sclp_tty_lock, flags); + count = 0; + if (sclp_ttybuf != NULL) + count = sclp_chars_in_buffer(sclp_ttybuf); + list_for_each(l, &sclp_tty_outqueue) { + t = list_entry(l, struct sclp_buffer, list); + count += sclp_chars_in_buffer(sclp_ttybuf); + } + spin_unlock_irqrestore(&sclp_tty_lock, flags); + return count; +} + +/* + * removes all content from buffers of low level driver + */ +static void +sclp_tty_flush_buffer(struct tty_struct *tty) +{ + if (sclp_tty_chars_count > 0) { + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); + sclp_tty_chars_count = 0; + } +} + +/* + * push input to tty + */ +static void +sclp_tty_input(unsigned char* buf, unsigned int count) +{ + unsigned int cchar; + + /* + * If this tty driver is currently closed + * then throw the received input away. + */ + if (sclp_tty == NULL) + return; + cchar = ctrlchar_handle(buf, count, sclp_tty); + switch (cchar & CTRLCHAR_MASK) { + case CTRLCHAR_SYSRQ: + break; + case CTRLCHAR_CTRL: + sclp_tty->flip.count++; + *sclp_tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *sclp_tty->flip.char_buf_ptr++ = cchar; + tty_flip_buffer_push(sclp_tty); + break; + case CTRLCHAR_NONE: + /* send (normal) input to line discipline */ + memcpy(sclp_tty->flip.char_buf_ptr, buf, count); + if (count < 2 || + (strncmp ((const char *) buf + count - 2, "^n", 2) && + strncmp ((const char *) buf + count - 2, "\0252n", 2))) { + sclp_tty->flip.char_buf_ptr[count] = '\n'; + count++; + } else + count -= 2; + memset(sclp_tty->flip.flag_buf_ptr, TTY_NORMAL, count); + sclp_tty->flip.char_buf_ptr += count; + sclp_tty->flip.flag_buf_ptr += count; + sclp_tty->flip.count += count; + tty_flip_buffer_push(sclp_tty); + break; + } +} + +/* + * get a EBCDIC string in upper/lower case, + * find out characters in lower/upper case separated by a special character, + * modifiy original string, + * returns length of resulting string + */ +static int +sclp_switch_cases(unsigned char *buf, int count, + unsigned char delim, int tolower) +{ + unsigned char *ip, *op; + int toggle; + + /* initially changing case is off */ + toggle = 0; + ip = op = buf; + while (count-- > 0) { + /* compare with special character */ + if (*ip == delim) { + /* followed by another special character? */ + if (count && ip[1] == delim) { + /* + * ... then put a single copy of the special + * character to the output string + */ + *op++ = *ip++; + count--; + } else + /* + * ... special character follower by a normal + * character toggles the case change behaviour + */ + toggle = ~toggle; + /* skip special character */ + ip++; + } else + /* not the special character */ + if (toggle) + /* but case switching is on */ + if (tolower) + /* switch to uppercase */ + *op++ = _ebc_toupper[(int) *ip++]; + else + /* switch to lowercase */ + *op++ = _ebc_tolower[(int) *ip++]; + else + /* no case switching, copy the character */ + *op++ = *ip++; + } + /* return length of reformatted string. */ + return op - buf; +} + +static void +sclp_get_input(unsigned char *start, unsigned char *end) +{ + int count; + + count = end - start; + /* + * if set in ioctl convert EBCDIC to lower case + * (modify original input in SCCB) + */ + if (sclp_ioctls.tolower) + EBC_TOLOWER(start, count); + + /* + * if set in ioctl find out characters in lower or upper case + * (depends on current case) separated by a special character, + * works on EBCDIC + */ + if (sclp_ioctls.delim) + count = sclp_switch_cases(start, count, + sclp_ioctls.delim, + sclp_ioctls.tolower); + + /* convert EBCDIC to ASCII (modify original input in SCCB) */ + sclp_ebcasc_str(start, count); + + /* if set in ioctl write operators input to console */ + if (sclp_ioctls.echo) + sclp_tty_write(sclp_tty, 0, start, count); + + /* transfer input to high level driver */ + sclp_tty_input(start, count); +} + +static inline struct gds_vector * +find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) +{ + struct gds_vector *vec; + + for (vec = start; vec < end; (void *) vec += vec->length) + if (vec->gds_id == id) + return vec; + return NULL; +} + +static inline struct gds_subvector * +find_gds_subvector(struct gds_subvector *start, + struct gds_subvector *end, u8 key) +{ + struct gds_subvector *subvec; + + for (subvec = start; subvec < end; (void *) subvec += subvec->length) + if (subvec->key == key) + return subvec; + return NULL; +} + +static inline void +sclp_eval_selfdeftextmsg(struct gds_subvector *start, + struct gds_subvector *end) +{ + struct gds_subvector *subvec; + + subvec = start; + while (subvec < end) { + subvec = find_gds_subvector(subvec, end, 0x30); + if (!subvec) + break; + sclp_get_input((unsigned char *)(subvec + 1), + (unsigned char *) subvec + subvec->length); + (void *) subvec += subvec->length; + } +} + +static inline void +sclp_eval_textcmd(struct gds_subvector *start, + struct gds_subvector *end) +{ + struct gds_subvector *subvec; + + subvec = start; + while (subvec < end) { + subvec = find_gds_subvector(subvec, end, + GDS_KEY_SelfDefTextMsg); + if (!subvec) + break; + sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), + (void *)subvec + subvec->length); + (void *) subvec += subvec->length; + } +} + +static inline void +sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) +{ + struct gds_vector *vec; + + vec = start; + while (vec < end) { + vec = find_gds_vector(vec, end, GDS_ID_TextCmd); + if (!vec) + break; + sclp_eval_textcmd((struct gds_subvector *)(vec + 1), + (void *) vec + vec->length); + (void *) vec += vec->length; + } +} + + +static inline void +sclp_eval_mdsmu(struct gds_vector *start, void *end) +{ + struct gds_vector *vec; + + vec = find_gds_vector(start, end, GDS_ID_CPMSU); + if (vec) + sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length); +} + +static void +sclp_tty_receiver(struct evbuf_header *evbuf) +{ + struct gds_vector *start, *end, *vec; + + start = (struct gds_vector *)(evbuf + 1); + end = (void *) evbuf + evbuf->length; + vec = find_gds_vector(start, end, GDS_ID_MDSMU); + if (vec) + sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length); +} + +static void +sclp_tty_state_change(struct sclp_register *reg) +{ +} + +static struct sclp_register sclp_input_event = +{ + .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask, + .state_change_fn = sclp_tty_state_change, + .receiver_fn = sclp_tty_receiver +}; + +void +sclp_tty_init(void) +{ + void *page; + int i; + int rc; + + if (!CONSOLE_IS_SCLP) + return; + rc = sclp_rw_init(); + if (rc != 0) { + printk(KERN_ERR SCLP_TTY_PRINT_HEADER + "could not register tty - " + "sclp_rw_init returned %d\n", rc); + return; + } + /* Allocate pages for output buffering */ + INIT_LIST_HEAD(&sclp_tty_pages); + for (i = 0; i < MAX_KMEM_PAGES; i++) { + page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (page == NULL) + return; + list_add_tail((struct list_head *) page, &sclp_tty_pages); + } + INIT_LIST_HEAD(&sclp_tty_outqueue); + spin_lock_init(&sclp_tty_lock); + init_waitqueue_head(&sclp_tty_waitq); + init_timer(&sclp_tty_timer); + sclp_ttybuf = NULL; + sclp_tty_buffer_count = 0; + if (MACHINE_IS_VM) { + /* + * save 4 characters for the CPU number + * written at start of each line by VM/CP + */ + sclp_ioctls_init.columns = 76; + /* case input lines to lowercase */ + sclp_ioctls_init.tolower = 1; + } + sclp_ioctls = sclp_ioctls_init; + sclp_tty_chars_count = 0; + sclp_tty = NULL; + + ctrlchar_init(); + + if (sclp_register(&sclp_input_event) != 0) + return; + + memset (&sclp_tty_driver, 0, sizeof(struct tty_driver)); + sclp_tty_driver.magic = TTY_DRIVER_MAGIC; + sclp_tty_driver.driver_name = "sclp_line"; + sclp_tty_driver.name = "ttyS"; + sclp_tty_driver.name_base = 0; + sclp_tty_driver.major = TTY_MAJOR; + sclp_tty_driver.minor_start = 64; + sclp_tty_driver.num = 1; + sclp_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM; + sclp_tty_driver.subtype = SYSTEM_TYPE_TTY; + sclp_tty_driver.init_termios = tty_std_termios; + sclp_tty_driver.flags = TTY_DRIVER_REAL_RAW; + sclp_tty_driver.refcount = &sclp_tty_refcount; + /* sclp_tty_driver.proc_entry ? */ + sclp_tty_driver.table = sclp_tty_table; + sclp_tty_driver.termios = sclp_tty_termios; + sclp_tty_driver.termios_locked = sclp_tty_termios_locked; + sclp_tty_driver.open = sclp_tty_open; + sclp_tty_driver.close = sclp_tty_close; + sclp_tty_driver.write = sclp_tty_write; + sclp_tty_driver.put_char = sclp_tty_put_char; + sclp_tty_driver.flush_chars = sclp_tty_flush_chars; + sclp_tty_driver.write_room = sclp_tty_write_room; + sclp_tty_driver.chars_in_buffer = sclp_tty_chars_in_buffer; + sclp_tty_driver.flush_buffer = sclp_tty_flush_buffer; + sclp_tty_driver.ioctl = sclp_tty_ioctl; + /* + * No need for these function because they would be only called when + * the line discipline is close to full. That means that there must be + * collected nearly 4kB of input data. I suppose it is very difficult + * for the operator to enter lines quickly enough to let overrun the + * line discipline. Besides the n_tty line discipline does not try to + * call such functions if the pointers are set to NULL. Finally I have + * no idea what to do within these function. I can not prevent the + * operator and the SCLP to deliver input. Because of the reasons + * above it seems not worth to implement a buffer mechanism. + */ + sclp_tty_driver.throttle = NULL; + sclp_tty_driver.unthrottle = NULL; + sclp_tty_driver.send_xchar = NULL; + sclp_tty_driver.set_termios = NULL; + sclp_tty_driver.set_ldisc = NULL; + sclp_tty_driver.stop = NULL; + sclp_tty_driver.start = NULL; + sclp_tty_driver.hangup = NULL; + sclp_tty_driver.break_ctl = NULL; + sclp_tty_driver.wait_until_sent = NULL; + sclp_tty_driver.read_proc = NULL; + sclp_tty_driver.write_proc = NULL; + + rc = tty_register_driver(&sclp_tty_driver); + if (rc != 0) + printk(KERN_ERR SCLP_TTY_PRINT_HEADER + "could not register tty - " + "sclp_drv_register returned %d\n", rc); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/sclp_tty.h linux.21rc1-ac2/drivers/s390/char/sclp_tty.h --- linux.21rc1/drivers/s390/char/sclp_tty.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/sclp_tty.h 2003-04-25 13:53:31.000000000 +0100 @@ -0,0 +1,67 @@ +/* + * drivers/s390/char/sclp_tty.h + * interface to the SCLP-read/write driver + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Peschke + * Martin Schwidefsky + */ + +#ifndef __SCLP_TTY_H__ +#define __SCLP_TTY_H__ + +#include + +/* This is the type of data structures storing sclp ioctl setting. */ +struct sclp_ioctls { + unsigned short htab; + unsigned char echo; + unsigned short columns; + unsigned char final_nl; + unsigned short max_sccb; + unsigned short kmem_sccb; /* can't be modified at run time */ + unsigned char tolower; + unsigned char delim; +}; + +/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */ +#define SCLP_IOCTL_LETTER 'B' + +/* set width of horizontal tabulator */ +#define TIOCSCLPSHTAB _IOW(SCLP_IOCTL_LETTER, 0, unsigned short) +/* enable/disable echo of input (independent from line discipline) */ +#define TIOCSCLPSECHO _IOW(SCLP_IOCTL_LETTER, 1, unsigned char) +/* set number of colums for output */ +#define TIOCSCLPSCOLS _IOW(SCLP_IOCTL_LETTER, 2, unsigned short) +/* enable/disable writing without final new line character */ +#define TIOCSCLPSNL _IOW(SCLP_IOCTL_LETTER, 4, signed char) +/* set the maximum buffers size for output, rounded up to next 4kB boundary */ +#define TIOCSCLPSOBUF _IOW(SCLP_IOCTL_LETTER, 5, unsigned short) +/* set initial (default) sclp ioctls */ +#define TIOCSCLPSINIT _IO(SCLP_IOCTL_LETTER, 6) +/* enable/disable conversion from upper to lower case of input */ +#define TIOCSCLPSCASE _IOW(SCLP_IOCTL_LETTER, 7, unsigned char) +/* set special character used for separating upper and lower case, */ +/* 0x00 disables this feature */ +#define TIOCSCLPSDELIM _IOW(SCLP_IOCTL_LETTER, 9, unsigned char) + +/* get width of horizontal tabulator */ +#define TIOCSCLPGHTAB _IOR(SCLP_IOCTL_LETTER, 10, unsigned short) +/* Is echo of input enabled ? (independent from line discipline) */ +#define TIOCSCLPGECHO _IOR(SCLP_IOCTL_LETTER, 11, unsigned char) +/* get number of colums for output */ +#define TIOCSCLPGCOLS _IOR(SCLP_IOCTL_LETTER, 12, unsigned short) +/* Is writing without final new line character enabled ? */ +#define TIOCSCLPGNL _IOR(SCLP_IOCTL_LETTER, 14, signed char) +/* get the maximum buffers size for output */ +#define TIOCSCLPGOBUF _IOR(SCLP_IOCTL_LETTER, 15, unsigned short) +/* Is conversion from upper to lower case of input enabled ? */ +#define TIOCSCLPGCASE _IOR(SCLP_IOCTL_LETTER, 17, unsigned char) +/* get special character used for separating upper and lower case, */ +/* 0x00 disables this feature */ +#define TIOCSCLPGDELIM _IOR(SCLP_IOCTL_LETTER, 19, unsigned char) +/* get the number of buffers/pages got from kernel at startup */ +#define TIOCSCLPGKBUF _IOR(SCLP_IOCTL_LETTER, 20, unsigned short) + +#endif /* __SCLP_TTY_H__ */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3480.c linux.21rc1-ac2/drivers/s390/char/tape3480.c --- linux.21rc1/drivers/s390/char/tape3480.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3480.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,156 +0,0 @@ -/*************************************************************************** - * - * drivers/s390/char/tape3480.c - * tape device discipline for 3480 tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#include "tapedefs.h" -#include -#include /* CCW allocations */ -#include -#include -#include -#include "tape.h" -#include "tape34xx.h" -#include "tape3480.h" - -tape_event_handler_t tape3480_event_handler_table[TS_SIZE][TE_SIZE] = -{ - /* {START , DONE, FAILED, ERROR, OTHER } */ - {NULL, tape34xx_unused_done, NULL, NULL, NULL}, /* TS_UNUSED */ - {NULL, tape34xx_idle_done, NULL, NULL, NULL}, /* TS_IDLE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_DONE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_FAILED */ - {NULL, tape34xx_block_done, NULL, NULL, NULL}, /* TS_BLOCK_INIT */ - {NULL, tape34xx_bsb_init_done, NULL, NULL, NULL}, /* TS_BSB_INIT */ - {NULL, tape34xx_bsf_init_done, NULL, NULL, NULL}, /* TS_BSF_INIT */ - {NULL, tape34xx_dse_init_done, NULL, NULL, NULL}, /* TS_DSE_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_EGA_INIT */ - {NULL, tape34xx_fsb_init_done, NULL, NULL, NULL}, /* TS_FSB_INIT */ - {NULL, tape34xx_fsf_init_done, NULL, NULL, NULL}, /* TS_FSF_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_LDI_INIT */ - {NULL, tape34xx_lbl_init_done, NULL, NULL, NULL}, /* TS_LBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_MSE_INIT */ - {NULL, tape34xx_nop_init_done, NULL, NULL, NULL}, /* TS_NOP_INIT */ - {NULL, tape34xx_rfo_init_done, NULL, NULL, NULL}, /* TS_RBA_INIT */ - {NULL, tape34xx_rbi_init_done, NULL, NULL, NULL}, /* TS_RBI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBU_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RDC_INIT */ - {NULL, tape34xx_rfo_init_done, NULL, NULL, NULL}, /* TS_RFO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RSD_INIT */ - {NULL, tape34xx_rew_init_done, NULL, NULL, NULL}, /* TS_REW_INIT */ - {NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL}, /* TS_REW_RELEASE_IMIT */ - {NULL, tape34xx_run_init_done, NULL, NULL, NULL}, /* TS_RUN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SEN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SID_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SNP_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SPG_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SWI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SMR_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SYN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_TIO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_UNA_INIT */ - {NULL, tape34xx_wri_init_done, NULL, NULL, NULL}, /* TS_WRI_INIT */ - {NULL, tape34xx_wtm_init_done, NULL, NULL, NULL}, /* TS_WTM_INIT */ - {NULL, NULL, NULL, NULL, NULL}}; /* TS_NOT_OPER */ - -devreg_t tape3480_devreg = { - ci: - {hc: - {ctype:0x3480}}, - flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS, - oper_func:tape_oper_handler -}; - - -void -tape3480_setup_assist (tape_info_t * ti) -{ - tape3480_disc_data_t *data = NULL; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"3480 dsetu"); - debug_text_event (tape_debug_area,6,"dev:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif /* TAPE_DEBUG */ - while (data == NULL) - data = kmalloc (sizeof (tape3480_disc_data_t), GFP_KERNEL); - data->modeset_byte = 0x00; - ti->discdata = (void *) data; -} - - -void -tape3480_shutdown (int autoprobe) { - if (autoprobe) - s390_device_unregister(&tape3480_devreg); -} - -tape_discipline_t * -tape3480_init (int autoprobe) -{ - tape_discipline_t *disc; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"3480 init"); -#endif /* TAPE_DEBUG */ - disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL); - if (disc == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"disc:nomem"); -#endif /* TAPE_DEBUG */ - return disc; - } - disc->cu_type = 0x3480; - disc->setup_assist = tape3480_setup_assist; - disc->error_recovery = tape34xx_error_recovery; - disc->write_block = tape34xx_write_block; - disc->free_write_block = tape34xx_free_write_block; - disc->read_block = tape34xx_read_block; - disc->free_read_block = tape34xx_free_read_block; - disc->mtfsf = tape34xx_mtfsf; - disc->mtbsf = tape34xx_mtbsf; - disc->mtfsr = tape34xx_mtfsr; - disc->mtbsr = tape34xx_mtbsr; - disc->mtweof = tape34xx_mtweof; - disc->mtrew = tape34xx_mtrew; - disc->mtoffl = tape34xx_mtoffl; - disc->mtnop = tape34xx_mtnop; - disc->mtbsfm = tape34xx_mtbsfm; - disc->mtfsfm = tape34xx_mtfsfm; - disc->mteom = tape34xx_mteom; - disc->mterase = tape34xx_mterase; - disc->mtsetdensity = tape34xx_mtsetdensity; - disc->mtseek = tape34xx_mtseek; - disc->mttell = tape34xx_mttell; - disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer; - disc->mtlock = tape34xx_mtlock; - disc->mtunlock = tape34xx_mtunlock; - disc->mtload = tape34xx_mtload; - disc->mtunload = tape34xx_mtunload; - disc->mtcompression = tape34xx_mtcompression; - disc->mtsetpart = tape34xx_mtsetpart; - disc->mtmkpart = tape34xx_mtmkpart; - disc->mtiocget = tape34xx_mtiocget; - disc->mtiocpos = tape34xx_mtiocpos; - disc->shutdown = tape3480_shutdown; - disc->discipline_ioctl_overload = tape34xx_ioctl_overload; - disc->event_table = &tape3480_event_handler_table; - disc->default_handler = tape34xx_default_handler; - disc->bread = tape34xx_bread; - disc->free_bread = tape34xx_free_bread; - disc->tape = NULL; /* pointer for backreference */ - disc->next = NULL; - if (autoprobe) - s390_device_register(&tape3480_devreg); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"3480 regis"); -#endif /* TAPE_DEBUG */ - return disc; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3480.h linux.21rc1-ac2/drivers/s390/char/tape3480.h --- linux.21rc1/drivers/s390/char/tape3480.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3480.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,23 +0,0 @@ -/*************************************************************************** - * - * drivers/s390/char/tape3480.h - * tape device discipline for 3480 tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#ifndef _TAPE3480_H - -#define _TAPE3480_H - - -typedef struct _tape3480_disc_data_t { - __u8 modeset_byte; -} tape3480_disc_data_t __attribute__ ((packed, aligned(8))); -tape_discipline_t * tape3480_init (int); -#endif // _TAPE3480_H diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3490.c linux.21rc1-ac2/drivers/s390/char/tape3490.c --- linux.21rc1/drivers/s390/char/tape3490.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3490.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,156 +0,0 @@ -/*************************************************************************** - * - * drivers/s390/char/tape3490.c - * tape device discipline for 3490E tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#include "tapedefs.h" -#include -#include /* CCW allocations */ -#include -#include -#include -#include "tape.h" -#include "tape34xx.h" -#include "tape3490.h" - -tape_event_handler_t tape3490_event_handler_table[TS_SIZE][TE_SIZE] = -{ - /* {START , DONE, FAILED, ERROR, OTHER } */ - {NULL, tape34xx_unused_done, NULL, NULL, NULL}, /* TS_UNUSED */ - {NULL, tape34xx_idle_done, NULL, NULL, NULL}, /* TS_IDLE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_DONE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_FAILED */ - {NULL, tape34xx_block_done, NULL, NULL, NULL}, /* TS_BLOCK_INIT */ - {NULL, tape34xx_bsb_init_done, NULL, NULL, NULL}, /* TS_BSB_INIT */ - {NULL, tape34xx_bsf_init_done, NULL, NULL, NULL}, /* TS_BSF_INIT */ - {NULL, tape34xx_dse_init_done, NULL, NULL, NULL}, /* TS_DSE_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_EGA_INIT */ - {NULL, tape34xx_fsb_init_done, NULL, NULL, NULL}, /* TS_FSB_INIT */ - {NULL, tape34xx_fsf_init_done, NULL, NULL, NULL}, /* TS_FSF_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_LDI_INIT */ - {NULL, tape34xx_lbl_init_done, NULL, NULL, NULL}, /* TS_LBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_MSE_INIT */ - {NULL, tape34xx_nop_init_done, NULL, NULL, NULL}, /* TS_NOP_INIT */ - {NULL, tape34xx_rfo_init_done, NULL, NULL, NULL}, /* TS_RBA_INIT */ - {NULL, tape34xx_rbi_init_done, NULL, NULL, NULL}, /* TS_RBI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBU_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RDC_INIT */ - {NULL, tape34xx_rfo_init_done, NULL, NULL, NULL}, /* TS_RFO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RSD_INIT */ - {NULL, tape34xx_rew_init_done, NULL, NULL, NULL}, /* TS_REW_INIT */ - {NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL}, /* TS_REW_RELEASE_IMIT */ - {NULL, tape34xx_run_init_done, NULL, NULL, NULL}, /* TS_RUN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SEN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SID_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SNP_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SPG_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SWI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SMR_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SYN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_TIO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_UNA_INIT */ - {NULL, tape34xx_wri_init_done, NULL, NULL, NULL}, /* TS_WRI_INIT */ - {NULL, tape34xx_wtm_init_done, NULL, NULL, NULL}, /* TS_WTM_INIT */ - {NULL, NULL, NULL, NULL, NULL}}; /* TS_NOT_OPER */ - -devreg_t tape3490_devreg = { - ci: - {hc: - {ctype:0x3490}}, - flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS, - oper_func:tape_oper_handler -}; - -void -tape3490_setup_assist (tape_info_t * ti) -{ - tape3490_disc_data_t *data = NULL; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"3490 dsetu"); - debug_text_event (tape_debug_area,6,"dev:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif /* TAPE_DEBUG */ - while (data == NULL) - data = kmalloc (sizeof (tape3490_disc_data_t), GFP_KERNEL); - data->modeset_byte = 0x00; - ti->discdata = (void *) data; -} - - -void -tape3490_shutdown (int autoprobe) { - if (autoprobe) - s390_device_unregister(&tape3490_devreg); -} - - -tape_discipline_t * -tape3490_init (int autoprobe) -{ - tape_discipline_t *disc; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"3490 init"); -#endif /* TAPE_DEBUG */ - disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL); - if (disc == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"disc:nomem"); -#endif /* TAPE_DEBUG */ - return disc; - } - disc->cu_type = 0x3490; - disc->setup_assist = tape3490_setup_assist; - disc->error_recovery = tape34xx_error_recovery; - disc->write_block = tape34xx_write_block; - disc->free_write_block = tape34xx_free_write_block; - disc->read_block = tape34xx_read_block; - disc->free_read_block = tape34xx_free_read_block; - disc->mtfsf = tape34xx_mtfsf; - disc->mtbsf = tape34xx_mtbsf; - disc->mtfsr = tape34xx_mtfsr; - disc->mtbsr = tape34xx_mtbsr; - disc->mtweof = tape34xx_mtweof; - disc->mtrew = tape34xx_mtrew; - disc->mtoffl = tape34xx_mtoffl; - disc->mtnop = tape34xx_mtnop; - disc->mtbsfm = tape34xx_mtbsfm; - disc->mtfsfm = tape34xx_mtfsfm; - disc->mteom = tape34xx_mteom; - disc->mterase = tape34xx_mterase; - disc->mtsetdensity = tape34xx_mtsetdensity; - disc->mtseek = tape34xx_mtseek; - disc->mttell = tape34xx_mttell; - disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer; - disc->mtlock = tape34xx_mtlock; - disc->mtunlock = tape34xx_mtunlock; - disc->mtload = tape34xx_mtload; - disc->mtunload = tape34xx_mtunload; - disc->mtcompression = tape34xx_mtcompression; - disc->mtsetpart = tape34xx_mtsetpart; - disc->mtmkpart = tape34xx_mtmkpart; - disc->mtiocget = tape34xx_mtiocget; - disc->mtiocpos = tape34xx_mtiocpos; - disc->shutdown = tape3490_shutdown; - disc->discipline_ioctl_overload = tape34xx_ioctl_overload; - disc->event_table = &tape3490_event_handler_table; - disc->default_handler = tape34xx_default_handler; - disc->bread = tape34xx_bread; - disc->free_bread = tape34xx_free_bread; - disc->tape = NULL; /* pointer for backreference */ - disc->next = NULL; - if (autoprobe) - s390_device_register(&tape3490_devreg); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"3490 regis"); -#endif /* TAPE_DEBUG */ - return disc; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3490.h linux.21rc1-ac2/drivers/s390/char/tape3490.h --- linux.21rc1/drivers/s390/char/tape3490.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3490.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,24 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tape3490.h - * tape device discipline for 3490E tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#ifndef _TAPE3490_H - -#define _TAPE3490_H - - -typedef struct _tape3490_disc_data_t { - __u8 modeset_byte; -} tape3490_disc_data_t __attribute__ ((packed, aligned(8))); -tape_discipline_t * tape3490_init (int); -#endif // _TAPE3490_H diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_34xx.c linux.21rc1-ac2/drivers/s390/char/tape_34xx.c --- linux.21rc1/drivers/s390/char/tape_34xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_34xx.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,1341 @@ +/* + * drivers/s390/char/tape_34xx.c + * tape device discipline for 3480/3490 tapes. + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + */ + +#include +#include +#include +#include +#include + +#include "tape.h" +#include "tape_std.h" + +#define PRINTK_HEADER "T34xx:" + +/* + * The block ID is the complete marker for a specific tape position. + * It contains a physical part (wrap, segment, format) and a logical + * block number. + */ +#define TBI_FORMAT_3480 0x00 +#define TBI_FORMAT_3480_2_XF 0x01 +#define TBI_FORMAT_3480_XF 0x02 +#define TBI_FORMAT_RESERVED 0x03 + +struct tape_34xx_block_id { + unsigned int tbi_wrap : 1; + unsigned int tbi_segment : 7; + unsigned int tbi_format : 2; + unsigned int tbi_block : 22; +} __attribute__ ((packed)); + +struct sbid_entry { + struct list_head list; + struct tape_34xx_block_id bid; +}; + +struct tape_34xx_discdata { + /* A list of block id's of the tape segments (for faster seek) */ + struct list_head sbid_list; +}; + +/* Internal prototypes */ +static void tape_34xx_clear_sbid_list(struct tape_device *); + +/* 34xx specific functions */ +static void +__tape_34xx_medium_sense_callback(struct tape_request *request, void *data) +{ + unsigned char *sense = request->cpdata; + + request->callback = NULL; + + DBF_EVENT(5, "TO_MSEN[0]: %08x\n", *((unsigned int *) sense)); + DBF_EVENT(5, "TO_MSEN[1]: %08x\n", *((unsigned int *) sense+1)); + DBF_EVENT(5, "TO_MSEN[2]: %08x\n", *((unsigned int *) sense+2)); + DBF_EVENT(5, "TO_MSEN[3]: %08x\n", *((unsigned int *) sense+3)); + + if(sense[0] & SENSE_INTERVENTION_REQUIRED) { + tape_med_state_set(request->device, MS_UNLOADED); + } else { + tape_med_state_set(request->device, MS_LOADED); + } + + if(sense[1] & SENSE_WRITE_PROTECT) { + request->device->tape_generic_status |= GMT_WR_PROT(~0); + } else{ + request->device->tape_generic_status &= ~GMT_WR_PROT(~0); + } + + tape_put_request(request); +} + +static int +tape_34xx_medium_sense(struct tape_device *device) +{ + struct tape_request * request; + int rc; + + tape_34xx_clear_sbid_list(device); + + request = tape_alloc_request(1, 32); + if(IS_ERR(request)) { + DBF_EXCEPTION(6, "MSN fail\n"); + return PTR_ERR(request); + } + + request->op = TO_MSEN; + tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); + request->callback = __tape_34xx_medium_sense_callback; + + rc = tape_do_io_async(device, request); + + return rc; +} + +static void +tape_34xx_work_handler(void *data) +{ + struct { + struct tape_device *device; + enum tape_op op; + struct tq_struct task; + } *p = data; + + switch(p->op) { + case TO_MSEN: + tape_34xx_medium_sense(p->device); + break; + default: + DBF_EVENT(3, "T34XX: internal error: unknown work\n"); + } + + tape_put_device(p->device); + kfree(p); +} + +/* + * This function is currently used to schedule a sense for later execution. + * For example whenever a unsolicited interrupt signals a new tape medium + * and we can't call tape_do_io from that interrupt handler. + */ +static int +tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) +{ + struct { + struct tape_device *device; + enum tape_op op; + struct tq_struct task; + } *p; + + if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->task.list); + p->task.routine = tape_34xx_work_handler; + p->task.data = p; + + p->device = tape_clone_device(device); + p->op = op; + + queue_task(&p->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return 0; +} + +/* + * Done Handler is called when dev stat = DEVICE-END (successful operation) + */ +static int +tape_34xx_done(struct tape_device *device, struct tape_request *request) +{ + DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); + // FIXME: Maybe only on assign/unassign + TAPE_CLEAR_STATE(device, TAPE_STATUS_BOXED); + + return TAPE_IO_SUCCESS; +} + +static inline int +tape_34xx_erp_failed(struct tape_device *device, + struct tape_request *request, int rc) +{ + DBF_EVENT(3, "Error recovery failed for %s\n", + tape_op_verbose[request->op]); + return rc; +} + +static inline int +tape_34xx_erp_succeeded(struct tape_device *device, + struct tape_request *request) +{ + DBF_EVENT(3, "Error Recovery successful for %s\n", + tape_op_verbose[request->op]); + return tape_34xx_done(device, request); +} + +static inline int +tape_34xx_erp_retry(struct tape_device *device, struct tape_request *request) +{ + DBF_EVENT(3, "xerp retr %s\n", + tape_op_verbose[request->op]); + return TAPE_IO_RETRY; +} + +/* + * This function is called, when no request is outstanding and we get an + * interrupt + */ +static int +tape_34xx_unsolicited_irq(struct tape_device *device) +{ + if (device->devstat.dstat == 0x85 /* READY */) { + /* A medium was inserted in the drive. */ + DBF_EVENT(6, "T34xx: tape load\n"); + tape_34xx_schedule_work(device, TO_MSEN); + } else { + DBF_EVENT(3, "T34xx: unsol.irq! dev end: %x\n", + device->devinfo.irq); + PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); + tape_dump_sense(device, NULL); + } + return TAPE_IO_SUCCESS; +} + +/* + * Read Opposite Error Recovery Function: + * Used, when Read Forward does not work + */ +static int +tape_34xx_erp_read_opposite(struct tape_device *device, + struct tape_request *request) +{ + if (request->op == TO_RFO) { + /* + * We did read forward, but the data could not be read + * *correctly*. We transform the request to a read backward + * and try again. + */ + tape_std_read_backward(device, request); + return tape_34xx_erp_retry(device, request); + } + if (request->op != TO_RBA) + PRINT_ERR("read_opposite called with state:%s\n", + tape_op_verbose[request->op]); + /* + * We tried to read forward and backward, but hat no + * success -> failed. + */ + return tape_34xx_erp_failed(device, request, -EIO); +} + +static int +tape_34xx_erp_bug(struct tape_device *device, + struct tape_request *request, int no) +{ + if (request->op != TO_ASSIGN) { + PRINT_WARN("An unexpected condition #%d was caught in " + "tape error recovery.\n", no); + PRINT_WARN("Please report this incident.\n"); + if (request) + PRINT_WARN("Operation of tape:%s\n", + tape_op_verbose[request->op]); + tape_dump_sense(device, request); + } + return tape_34xx_erp_failed(device, request, -EIO); +} + +/* + * Handle data overrun between cu and drive. The channel speed might + * be too slow. + */ +static int +tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request) +{ + if (device->devstat.ii.sense.data[3] == 0x40) { + PRINT_WARN ("Data overrun error between control-unit " + "and drive. Use a faster channel connection, " + "if possible! \n"); + return tape_34xx_erp_failed(device, request, -EIO); + } + return tape_34xx_erp_bug(device, request, -1); +} + +/* + * Handle record sequence error. + */ +static int +tape_34xx_erp_sequence(struct tape_device *device, + struct tape_request *request) +{ + if (device->devstat.ii.sense.data[3] == 0x41) { + /* + * cu detected incorrect block-id sequence on tape. + */ + PRINT_WARN("Illegal block-id sequence found!\n"); + return tape_34xx_erp_failed(device, request, -EIO); + } + /* + * Record sequence error bit is set, but erpa does not + * show record sequence error. + */ + return tape_34xx_erp_bug(device, request, -2); +} + +/* + * This function analyses the tape's sense-data in case of a unit-check. + * If possible, it tries to recover from the error. Else the user is + * informed about the problem. + */ +static int +tape_34xx_unit_check(struct tape_device *device, struct tape_request *request) +{ + int inhibit_cu_recovery; + __u8* sense; + + inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0; + sense = device->devstat.ii.sense.data; + +#ifdef CONFIG_S390_TAPE_BLOCK + if (request->op == TO_BLOCK) { + /* + * Recovery for block device requests. Set the block_position + * to something invalid and retry. + */ + device->blk_data.block_position = -1; + if (request->retries-- <= 0) + return tape_34xx_erp_failed(device, request, -EIO); + else + return tape_34xx_erp_retry(device, request); + } +#endif + + if ( + sense[0] & SENSE_COMMAND_REJECT && + sense[1] & SENSE_WRITE_PROTECT + ) { + if ( + request->op == TO_DSE || + request->op == TO_WRI || + request->op == TO_WTM + ) { + /* medium is write protected */ + return tape_34xx_erp_failed(device, request, -EACCES); + } else { + return tape_34xx_erp_bug(device, request, -3); + } + } + + /* + * special cases for various tape-states when reaching + * end of recorded area + */ + /* + * FIXME: Maybe a special case of the special case: + * sense[0] == SENSE_EQUIPMENT_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE && + * sense[3] == 0x47 (Volume Fenced) + * + * This was caused by continued FSF or FSR after an + * 'End Of Data'. + */ + if (( + sense[0] == SENSE_DATA_CHECK || + sense[0] == SENSE_EQUIPMENT_CHECK || + sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK + ) && ( + sense[1] == SENSE_DRIVE_ONLINE || + sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE + )) { + switch (request->op) { + /* + * sense[0] == SENSE_DATA_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE + * sense[3] == 0x36 (End Of Data) + * + * Further seeks might return a 'Volume Fenced'. + */ + case TO_FSF: + case TO_FSB: + /* Trying to seek beyond end of recorded area */ + return tape_34xx_erp_failed(device, request, -ENOSPC); + case TO_BSB: + return tape_34xx_erp_retry(device, request); + /* + * sense[0] == SENSE_DATA_CHECK && + * sense[1] == SENSE_DRIVE_ONLINE && + * sense[3] == 0x36 (End Of Data) + */ + case TO_LBL: + /* Block could not be located. */ + return tape_34xx_erp_failed(device, request, -EIO); + case TO_RFO: + /* Read beyond end of recorded area -> 0 bytes read */ + return tape_34xx_erp_failed(device, request, 0); + default: + PRINT_ERR("Invalid op %s in %s:%i\n", + tape_op_verbose[request->op], + __FUNCTION__, __LINE__); + return tape_34xx_erp_failed(device, request, 0); + } + } + + /* Sensing special bits */ + if (sense[0] & SENSE_BUS_OUT_CHECK) + return tape_34xx_erp_retry(device, request); + + if (sense[0] & SENSE_DATA_CHECK) { + /* + * hardware failure, damaged tape or improper + * operating conditions + */ + switch (sense[3]) { + case 0x23: + /* a read data check occurred */ + if ((sense[2] & SENSE_TAPE_SYNC_MODE) || + inhibit_cu_recovery) + // data check is not permanent, may be + // recovered. We always use async-mode with + // cu-recovery, so this should *never* happen. + return tape_34xx_erp_bug(device, request, -4); + + /* data check is permanent, CU recovery has failed */ + PRINT_WARN("Permanent read error\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x25: + // a write data check occurred + if ((sense[2] & SENSE_TAPE_SYNC_MODE) || + inhibit_cu_recovery) + // data check is not permanent, may be + // recovered. We always use async-mode with + // cu-recovery, so this should *never* happen. + return tape_34xx_erp_bug(device, request, -5); + + // data check is permanent, cu-recovery has failed + PRINT_WARN("Permanent write error\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x26: + /* Data Check (read opposite) occurred. */ + return tape_34xx_erp_read_opposite(device, request); + case 0x28: + /* ID-Mark at tape start couldn't be written */ + PRINT_WARN("ID-Mark could not be written.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x31: + /* Tape void. Tried to read beyond end of device. */ + PRINT_WARN("Read beyond end of recorded area.\n"); + return tape_34xx_erp_failed(device, request, -ENOSPC); + case 0x41: + /* Record sequence error. */ + PRINT_WARN("Invalid block-id sequence found.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + default: + /* all data checks for 3480 should result in one of + * the above erpa-codes. For 3490, other data-check + * conditions do exist. */ + if (device->discipline->cu_type == 0x3480) + return tape_34xx_erp_bug(device, request, -6); + } + } + + if (sense[0] & SENSE_OVERRUN) + return tape_34xx_erp_overrun(device, request); + + if (sense[1] & SENSE_RECORD_SEQUENCE_ERR) + return tape_34xx_erp_sequence(device, request); + + /* Sensing erpa codes */ + switch (sense[3]) { + case 0x00: + /* Unit check with erpa code 0. Report and ignore. */ + PRINT_WARN("Non-error sense was found. " + "Unit-check will be ignored.\n"); + return TAPE_IO_SUCCESS; + case 0x21: + /* + * Data streaming not operational. CU will switch to + * interlock mode. Reissue the command. + */ + PRINT_WARN("Data streaming not operational. " + "Switching to interlock-mode.\n"); + return tape_34xx_erp_retry(device, request); + case 0x22: + /* + * Path equipment check. Might be drive adapter error, buffer + * error on the lower interface, internal path not usable, + * or error during cartridge load. + */ + PRINT_WARN("A path equipment check occurred. One of the " + "following conditions occurred:\n"); + PRINT_WARN("drive adapter error, buffer error on the lower " + "interface, internal path not usable, error " + "during cartridge load.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x24: + /* + * Load display check. Load display was command was issued, + * but the drive is displaying a drive check message. Can + * be threated as "device end". + */ + return tape_34xx_erp_succeeded(device, request); + case 0x27: + /* + * Command reject. May indicate illegal channel program or + * buffer over/underrun. Since all channel programs are + * issued by this driver and ought be correct, we assume a + * over/underrun situation and retry the channel program. + */ + return tape_34xx_erp_retry(device, request); + case 0x29: + /* + * Function incompatible. Either the tape is idrc compressed + * but the hardware isn't capable to do idrc, or a perform + * subsystem func is issued and the CU is not on-line. + */ + PRINT_WARN ("Function incompatible. Try to switch off idrc\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x2a: + /* + * Unsolicited environmental data. An internal counter + * overflows, we can ignore this and reissue the cmd. + */ + return tape_34xx_erp_retry(device, request); + case 0x2b: + /* + * Environmental data present. Indicates either unload + * completed ok or read buffered log command completed ok. + */ + if (request->op == TO_RUN) { + tape_med_state_set(device, MS_UNLOADED); + /* Rewind unload completed ok. */ + return tape_34xx_erp_succeeded(device, request); + } + /* tape_34xx doesn't use read buffered log commands. */ + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x2c: + /* + * Permanent equipment check. CU has tried recovery, but + * did not succeed. + */ + return tape_34xx_erp_failed(device, request, -EIO); + case 0x2d: + /* Data security erase failure. */ + if (request->op == TO_DSE) + return tape_34xx_erp_failed(device, request, -EIO); + /* Data security erase failure, but no such command issued. */ + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x2e: + /* + * Not capable. This indicates either that the drive fails + * reading the format id mark or that that format specified + * is not supported by the drive. + */ + PRINT_WARN("Drive not capable processing the tape format!"); + return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE); + case 0x30: + /* The medium is write protected. */ + PRINT_WARN("Medium is write protected!\n"); + return tape_34xx_erp_failed(device, request, -EACCES); + case 0x32: + // Tension loss. We cannot recover this, it's an I/O error. + PRINT_WARN("The drive lost tape tension.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x33: + /* + * Load Failure. The cartridge was not inserted correctly or + * the tape is not threaded correctly. + */ + PRINT_WARN("Cartridge load failure. Reload the cartridge " + "and try again.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x34: + /* + * Unload failure. The drive cannot maintain tape tension + * and control tape movement during an unload operation. + */ + PRINT_WARN("Failure during cartridge unload. " + "Please try manually.\n"); + if (request->op == TO_RUN) + return tape_34xx_erp_failed(device, request, -EIO); + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x35: + /* + * Drive equipment check. One of the following: + * - cu cannot recover from a drive detected error + * - a check code message is shown on drive display + * - the cartridge loader does not respond correctly + * - a failure occurs during an index, load, or unload cycle + */ + PRINT_WARN("Equipment check! Please check the drive and " + "the cartridge loader.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x36: + if (device->discipline->cu_type == 0x3490) + /* End of data. */ + return tape_34xx_erp_failed(device, request, -EIO); + /* This erpa is reserved for 3480 */ + return tape_34xx_erp_bug(device,request,sense[3]); + case 0x37: + /* + * Tape length error. The tape is shorter than reported in + * the beginning-of-tape data. + */ + PRINT_WARN("Tape length error.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x38: + /* + * Physical end of tape. A read/write operation reached + * the physical end of tape. + */ + if (request->op==TO_WRI || + request->op==TO_DSE || + request->op==TO_WTM) + return tape_34xx_erp_failed(device, request, -ENOSPC); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x39: + /* Backward at Beginning of tape. */ + return tape_34xx_erp_failed(device, request, -EIO); + case 0x3a: + /* Drive switched to not ready. */ + PRINT_WARN("Drive not ready. Turn the ready/not ready switch " + "to ready position and try again.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x3b: + /* Manual rewind or unload. This causes an I/O error. */ + PRINT_WARN("Medium was rewound or unloaded manually.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x42: + /* + * Degraded mode. A condition that can cause degraded + * performance is detected. + */ + PRINT_WARN("Subsystem is running in degraded mode.\n"); + return tape_34xx_erp_retry(device, request); + case 0x43: + /* Drive not ready. */ + tape_med_state_set(device, MS_UNLOADED); + /* SMB: some commands do not need a tape inserted */ + if((sense[1] & SENSE_DRIVE_ONLINE)) { + switch(request->op) { + case TO_ASSIGN: + case TO_UNASSIGN: + case TO_DIS: + return tape_34xx_done(device, request); + break; + default: + break; + } + } + PRINT_WARN("The drive is not ready.\n"); + return tape_34xx_erp_failed(device, request, -ENOMEDIUM); + case 0x44: + /* Locate Block unsuccessful. */ + if (request->op != TO_BLOCK && request->op != TO_LBL) + /* No locate block was issued. */ + return tape_34xx_erp_bug(device, request, sense[3]); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x45: + /* The drive is assigned to a different channel path. */ + PRINT_WARN("The drive is assigned elsewhere.\n"); + TAPE_SET_STATE(device, TAPE_STATUS_BOXED); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x46: + /* + * Drive not on-line. Drive may be switched offline, + * the power supply may be switched off or + * the drive address may not be set correctly. + */ + PRINT_WARN("The drive is not on-line."); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x47: + /* Volume fenced. CU reports volume integrity is lost. */ + PRINT_WARN("Volume fenced. The volume integrity is lost because\n"); + PRINT_WARN("assignment or tape position was lost.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x48: + /* Log sense data and retry request. */ + return tape_34xx_erp_retry(device, request); + case 0x49: + /* Bus out check. A parity check error on the bus was found. */ + PRINT_WARN("Bus out check. A data transfer over the bus " + "has been corrupted.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x4a: + /* Control unit erp failed. */ + PRINT_WARN("The control unit I/O error recovery failed.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x4b: + /* + * CU and drive incompatible. The drive requests micro-program + * patches, which are not available on the CU. + */ + PRINT_WARN("The drive needs microprogram patches from the " + "control unit, which are not available.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x4c: + /* + * Recovered Check-One failure. Cu develops a hardware error, + * but is able to recover. + */ + return tape_34xx_erp_retry(device, request); + case 0x4d: + if (device->discipline->cu_type == 0x3490) + /* + * Resetting event received. Since the driver does + * not support resetting event recovery (which has to + * be handled by the I/O Layer), retry our command. + */ + return tape_34xx_erp_retry(device, request); + /* This erpa is reserved for 3480. */ + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x4e: + if (device->discipline->cu_type == 0x3490) { + /* + * Maximum block size exceeded. This indicates, that + * the block to be written is larger than allowed for + * buffered mode. + */ + PRINT_WARN("Maximum block size for buffered " + "mode exceeded.\n"); + return tape_34xx_erp_failed(device, request, -ENOBUFS); + } + /* This erpa is reserved for 3480. */ + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x50: + /* + * Read buffered log (Overflow). CU is running in extended + * buffered log mode, and a counter overflows. This should + * never happen, since we're never running in extended + * buffered log mode. + */ + return tape_34xx_erp_retry(device, request); + case 0x51: + /* + * Read buffered log (EOV). EOF processing occurs while the + * CU is in extended buffered log mode. This should never + * happen, since we're never running in extended buffered + * log mode. + */ + return tape_34xx_erp_retry(device, request); + case 0x52: + /* End of Volume complete. Rewind unload completed ok. */ + if (request->op == TO_RUN) { + /* SMB */ + tape_med_state_set(device, MS_UNLOADED); + return tape_34xx_erp_succeeded(device, request); + } + return tape_34xx_erp_bug(device, request, sense[3]); + case 0x53: + /* Global command intercept. */ + return tape_34xx_erp_retry(device, request); + case 0x54: + /* Channel interface recovery (temporary). */ + return tape_34xx_erp_retry(device, request); + case 0x55: + /* Channel interface recovery (permanent). */ + PRINT_WARN("A permanent channel interface error occurred.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x56: + /* Channel protocol error. */ + PRINT_WARN("A channel protocol error occurred.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x57: + if (device->discipline->cu_type == 0x3480) { + /* Attention intercept. */ + PRINT_WARN("An attention intercept occurred, " + "which will be recovered.\n"); + return tape_34xx_erp_retry(device, request); + } else { + /* Global status intercept. */ + PRINT_WARN("An global status intercept was received, " + "which will be recovered.\n"); + return tape_34xx_erp_retry(device, request); + } + case 0x5a: + /* + * Tape length incompatible. The tape inserted is too long, + * which could cause damage to the tape or the drive. + */ + PRINT_WARN("Tape length incompatible [should be IBM Cartridge " + "System Tape]. May cause damage to drive or tape.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x5b: + /* Format 3480 XF incompatible */ + if (sense[1] & SENSE_BEGINNING_OF_TAPE) + /* The tape will get overwritten. */ + return tape_34xx_erp_retry(device, request); + PRINT_WARN("Tape format is incompatible to the drive, " + "which writes 3480-2 XF.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x5c: + /* Format 3480-2 XF incompatible */ + PRINT_WARN("Tape format is incompatible to the drive. " + "The drive cannot access 3480-2 XF volumes.\n"); + return tape_34xx_erp_failed(device, request, -EIO); + case 0x5d: + /* Tape length violation. */ + PRINT_WARN("Tape length violation [should be IBM Enhanced " + "Capacity Cartridge System Tape]. May cause " + "damage to drive or tape.\n"); + return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE); + case 0x5e: + /* Compaction algorithm incompatible. */ + PRINT_WARN("The volume is recorded using an incompatible " + "compaction algorithm, which is not supported by " + "the control unit.\n"); + return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE); + + /* The following erpas should have been covered earlier. */ + case 0x23: /* Read data check. */ + case 0x25: /* Write data check. */ + case 0x26: /* Data check (read opposite). */ + case 0x28: /* Write id mark check. */ + case 0x31: /* Tape void. */ + case 0x40: /* Overrun error. */ + case 0x41: /* Record sequence error. */ + /* All other erpas are reserved for future use. */ + default: + return tape_34xx_erp_bug(device, request, sense[3]); + } +} + +/* + * 3480/3490 interrupt handler + */ +static int +tape_34xx_irq(struct tape_device *device, struct tape_request *request) +{ + if (request == NULL) + return tape_34xx_unsolicited_irq(device); + + if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) && + (device->devstat.dstat & DEV_STAT_DEV_END) && + (request->op == TO_WRI)) { + /* Write at end of volume */ + PRINT_INFO("End of volume\n"); /* XXX */ + return tape_34xx_erp_failed(device, request, -ENOSPC); + } + + if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) && + (request->op == TO_BSB || request->op == TO_FSB)) + DBF_EVENT(5, "Skipped over tapemark\n"); + + if (device->devstat.dstat & DEV_STAT_UNIT_CHECK) + return tape_34xx_unit_check(device, request); + + if (device->devstat.dstat & DEV_STAT_DEV_END) + return tape_34xx_done(device, request); + + DBF_EVENT(6, "xunknownirq\n"); + PRINT_ERR("Unexpected interrupt.\n"); + PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]); + tape_dump_sense(device, request); + return TAPE_IO_STOP; +} + +/* + * ioctl_overload + */ +static int +tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg) +{ + if (cmd == TAPE390_DISPLAY) { + struct display_struct disp; + + if(copy_from_user(&disp, (char *) arg, sizeof(disp)) != 0) + return -EFAULT; + + return tape_std_display(device, &disp); + } else + return -EINVAL; +} + +static int +tape_34xx_setup_device(struct tape_device * device) +{ + struct tape_34xx_discdata *discdata; + + DBF_EVENT(5, "tape_34xx_setup_device(%p)\n", device); + DBF_EVENT(6, "34xx minor1: %x\n", device->first_minor); + discdata = kmalloc(sizeof(struct tape_34xx_discdata), GFP_ATOMIC); + if(discdata) { + memset(discdata, 0, sizeof(struct tape_34xx_discdata)); + INIT_LIST_HEAD(&discdata->sbid_list); + device->discdata = discdata; + } + tape_34xx_medium_sense(device); + return 0; +} + +static void +tape_34xx_cleanup_device(struct tape_device * device) +{ + if (device->discdata) { + tape_34xx_clear_sbid_list(device); + kfree(device->discdata); + device->discdata = NULL; + } +} + +/* + * Build up the lookup table... + */ +static void +tape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid) +{ + struct tape_34xx_discdata * discdata = device->discdata; + struct sbid_entry * new; + struct sbid_entry * cur; + struct list_head * l; + + if(discdata == NULL) + return; + if((new = kmalloc(sizeof(struct sbid_entry), GFP_ATOMIC)) == NULL) + return; + + new->bid = bid; + new->bid.tbi_format = 0; + + /* + * Search the position where to insert the new entry. It is possible + * that the entry should not be added but the block number has to be + * updated to approximate the logical block, where a segment starts. + */ + list_for_each(l, &discdata->sbid_list) { + cur = list_entry(l, struct sbid_entry, list); + + /* + * If the current entry has the same segment and wrap, then + * there is no new entry needed. Only the block number of the + * current entry might be adjusted to reflect an earlier start + * of the segment. + */ + if( + (cur->bid.tbi_segment == new->bid.tbi_segment) && + (cur->bid.tbi_wrap == new->bid.tbi_wrap) + ) { + if(new->bid.tbi_block < cur->bid.tbi_block) { + cur->bid.tbi_block = new->bid.tbi_block; + } + kfree(new); + break; + } + + /* + * Otherwise the list is sorted by block number because it + * is alway ascending while the segment number decreases on + * the second wrap. + */ + if(cur->bid.tbi_block > new->bid.tbi_block) { + list_add_tail(&new->list, l); + break; + } + } + + /* + * The loop went through without finding a merge or adding an entry + * add the new entry to the end of the list. + */ + if(l == &discdata->sbid_list) { + list_add_tail(&new->list, &discdata->sbid_list); + } + + list_for_each(l, &discdata->sbid_list) { + cur = list_entry(l, struct sbid_entry, list); + + DBF_EVENT(3, "sbid_list(%03i:%1i:%08i)\n", + cur->bid.tbi_segment, cur->bid.tbi_wrap, + cur->bid.tbi_block); + } + + return; +} + +/* + * Fill hardware positioning information into the given block id. With that + * seeks don't have to go back to the beginning of the tape and are done at + * faster speed because the vicinity of a segment can be located at faster + * speed. + * + * The caller must have set tbi_block. + */ +static void +tape_34xx_merge_sbid( + struct tape_device * device, + struct tape_34xx_block_id * bid +) { + struct tape_34xx_discdata * discdata = device->discdata; + struct sbid_entry * cur; + struct list_head * l; + + bid->tbi_wrap = 0; + bid->tbi_segment = 1; + bid->tbi_format = (*device->modeset_byte & 0x08) ? + TBI_FORMAT_3480_XF : TBI_FORMAT_3480; + + if(discdata == NULL) + goto tape_34xx_merge_sbid_exit; + if(list_empty(&discdata->sbid_list)) + goto tape_34xx_merge_sbid_exit; + + list_for_each(l, &discdata->sbid_list) { + cur = list_entry(l, struct sbid_entry, list); + + if(cur->bid.tbi_block > bid->tbi_block) + break; + } + + /* If block comes before first entries block, use seek from start. */ + if(l->prev == &discdata->sbid_list) + goto tape_34xx_merge_sbid_exit; + + cur = list_entry(l->prev, struct sbid_entry, list); + bid->tbi_wrap = cur->bid.tbi_wrap; + bid->tbi_segment = cur->bid.tbi_segment; + +tape_34xx_merge_sbid_exit: + DBF_EVENT(6, "merged_bid = %08x\n", *((unsigned int *) bid)); + return; +} + +static void +tape_34xx_clear_sbid_list(struct tape_device *device) +{ + struct list_head * l; + struct list_head * n; + struct tape_34xx_discdata * discdata = device->discdata; + + list_for_each_safe(l, n, &discdata->sbid_list) { + list_del(l); + kfree(list_entry(l, struct sbid_entry, list)); + } +} + +/* + * MTTELL: Tell block. Return the number of block relative to current file. + */ +int +tape_34xx_mttell(struct tape_device *device, int mt_count) +{ + struct tape_34xx_block_id bid; + int rc; + + rc = tape_std_read_block_id(device, (unsigned int *) &bid); + if (rc) + return rc; + + /* + * Build up a lookup table. The format id is ingored. + */ + tape_34xx_add_sbid(device, bid); + + return bid.tbi_block; +} + +/* + * MTSEEK: seek to the specified block. + */ +int +tape_34xx_mtseek(struct tape_device *device, int mt_count) +{ + struct tape_34xx_block_id bid; + + if (mt_count > 0x400000) { + DBF_EXCEPTION(6, "xsee parm\n"); + return -EINVAL; + } + + bid.tbi_block = mt_count; + + /* + * Set hardware seek information in the block id. + */ + tape_34xx_merge_sbid(device, &bid); + + return tape_std_seek_block_id(device, *((unsigned int *) &bid)); +} + +/* + * Tape block read for 34xx. + */ +#ifdef CONFIG_S390_TAPE_BLOCK +struct tape_request * +tape_34xx_bread(struct tape_device *device, struct request *req) +{ + struct tape_request *request; + struct buffer_head *bh; + ccw1_t *ccw; + int count; + int size; + + DBF_EVENT(6, "tape_34xx_bread(sector=%u,size=%u)\n", + req->sector, req->nr_sectors); + + /* Count the number of blocks for the request. */ + count = 0; + size = 0; + for(bh = req->bh; bh; bh = bh->b_reqnext) { + for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE) + count++; + } + + /* Allocate the ccw request. */ + request = tape_alloc_request(3+count+1, 8); + if (IS_ERR(request)) + return request; + + /* + * Setup the tape block id to start the read from. The block number + * is later compared to the current position to decide whether a + * locate block is required. If one is needed this block id is used + * to locate it. + */ + ((struct tape_34xx_block_id *) request->cpdata)->tbi_block = + req->sector >> TAPEBLOCK_HSEC_S2B; + + /* Setup ccws. */ + request->op = TO_BLOCK; + ccw = request->cpaddr; + ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte); + + /* + * We always setup a nop after the mode set ccw. This slot is + * used in tape_std_check_locate to insert a locate ccw if the + * current tape position doesn't match the start block to be read. + * The second nop will be filled with a read block id which is in + * turn used by tape_34xx_free_bread to populate the segment bid + * table. + */ + ccw = tape_ccw_cc(ccw, NOP, 0, NULL); + ccw = tape_ccw_cc(ccw, NOP, 0, NULL); + + for(bh = req->bh; bh; bh = bh->b_reqnext) { + for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE) { + ccw->flags = CCW_FLAG_CC; + ccw->cmd_code = READ_FORWARD; + ccw->count = TAPEBLOCK_HSEC_SIZE; + set_normalized_cda(ccw, (void *) __pa(bh->b_data+size)); + ccw++; + } + } + + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + + return request; +} + +void +tape_34xx_free_bread (struct tape_request *request) +{ + ccw1_t* ccw = request->cpaddr; + + if((ccw + 2)->cmd_code == READ_BLOCK_ID) { + struct { + struct tape_34xx_block_id channel_block_id; + struct tape_34xx_block_id device_block_id; + } __attribute__ ((packed)) *rbi_data; + + rbi_data = request->cpdata; + + if(!request->device) + DBF_EVENT(6, "tape_34xx_free_bread: no device!\n"); + DBF_EVENT(6, "tape_34xx_free_bread: update_sbid\n"); + tape_34xx_add_sbid( + request->device, + rbi_data->channel_block_id + ); + } else { + DBF_EVENT(3, "tape_34xx_free_bread: no block info\n"); + } + + /* Last ccw is a nop and doesn't need clear_normalized_cda */ + for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++) + if (ccw->cmd_code == READ_FORWARD) + clear_normalized_cda(ccw); + tape_put_request(request); +} + +/* + * check_locate is called just before the tape request is passed to + * the common io layer for execution. It has to check the current + * tape position and insert a locate ccw if it doesn't match the + * start block for the request. + */ +void +tape_34xx_check_locate(struct tape_device *device, struct tape_request *request) +{ + struct tape_34xx_block_id *id; + struct tape_34xx_block_id *start; + + id = (struct tape_34xx_block_id *) request->cpdata; + + /* + * The tape is already at the correct position. No seek needed. + */ + if (id->tbi_block == device->blk_data.block_position) + return; + + /* + * In case that the block device image doesn't start at the beginning + * of the tape, adjust the blocknumber for the locate request. + */ + start = (struct tape_34xx_block_id *) &device->blk_data.start_block_id; + if(start->tbi_block) + id->tbi_block = id->tbi_block + start->tbi_block; + + /* + * Merge HW positioning information to the block id. This information + * is used by the device for faster seeks. + */ + tape_34xx_merge_sbid(device, id); + + /* + * Transform the NOP to a LOCATE entry. + */ + tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); + tape_ccw_cc(request->cpaddr + 2, READ_BLOCK_ID, 8, request->cpdata); + + return; +} +#endif + +static int +tape_34xx_mtweof(struct tape_device *device, int count) +{ + tape_34xx_clear_sbid_list(device); + return tape_std_mtweof(device, count); +} + +/* + * List of 3480/3490 magnetic tape commands. + */ +static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = +{ + [MTRESET] = tape_std_mtreset, + [MTFSF] = tape_std_mtfsf, + [MTBSF] = tape_std_mtbsf, + [MTFSR] = tape_std_mtfsr, + [MTBSR] = tape_std_mtbsr, + [MTWEOF] = tape_34xx_mtweof, + [MTREW] = tape_std_mtrew, + [MTOFFL] = tape_std_mtoffl, + [MTNOP] = tape_std_mtnop, + [MTRETEN] = tape_std_mtreten, + [MTBSFM] = tape_std_mtbsfm, + [MTFSFM] = tape_std_mtfsfm, + [MTEOM] = tape_std_mteom, + [MTERASE] = tape_std_mterase, + [MTRAS1] = NULL, + [MTRAS2] = NULL, + [MTRAS3] = NULL, + [MTSETBLK] = tape_std_mtsetblk, + [MTSETDENSITY] = NULL, + [MTSEEK] = tape_34xx_mtseek, + [MTTELL] = tape_34xx_mttell, + [MTSETDRVBUFFER] = NULL, + [MTFSS] = NULL, + [MTBSS] = NULL, + [MTWSM] = NULL, + [MTLOCK] = NULL, + [MTUNLOCK] = NULL, + [MTLOAD] = tape_std_mtload, + [MTUNLOAD] = tape_std_mtunload, + [MTCOMPRESSION] = tape_std_mtcompression, + [MTSETPART] = NULL, + [MTMKPART] = NULL +}; + +/* + * Tape discipline structures for 3480 and 3490. + */ +static struct tape_discipline tape_discipline_3480 = { + .owner = THIS_MODULE, + .cu_type = 0x3480, + .setup_device = tape_34xx_setup_device, + .cleanup_device = tape_34xx_cleanup_device, + .process_eov = tape_std_process_eov, + .irq = tape_34xx_irq, + .read_block = tape_std_read_block, + .write_block = tape_std_write_block, + .assign = tape_std_assign, + .unassign = tape_std_unassign, +#ifdef TAPE390_FORCE_UNASSIGN + .force_unassign = tape_std_force_unassign, +#endif +#ifdef CONFIG_S390_TAPE_BLOCK + .bread = tape_34xx_bread, + .free_bread = tape_34xx_free_bread, + .check_locate = tape_34xx_check_locate, +#endif + .ioctl_fn = tape_34xx_ioctl, + .mtop_array = tape_34xx_mtop +}; + +static struct tape_discipline tape_discipline_3490 = { + .owner = THIS_MODULE, + .cu_type = 0x3490, + .setup_device = tape_34xx_setup_device, + .cleanup_device = tape_34xx_cleanup_device, + .process_eov = tape_std_process_eov, + .irq = tape_34xx_irq, + .read_block = tape_std_read_block, + .write_block = tape_std_write_block, + .assign = tape_std_assign, + .unassign = tape_std_unassign, +#ifdef TAPE390_FORCE_UNASSIGN + .force_unassign = tape_std_force_unassign, +#endif +#ifdef CONFIG_S390_TAPE_BLOCK + .bread = tape_34xx_bread, + .free_bread = tape_34xx_free_bread, + .check_locate = tape_34xx_check_locate, +#endif + .ioctl_fn = tape_34xx_ioctl, + .mtop_array = tape_34xx_mtop +}; + +int +tape_34xx_init (void) +{ + int rc; + + DBF_EVENT(3, "34xx init: $Revision: 1.8 $\n"); + /* Register discipline. */ + rc = tape_register_discipline(&tape_discipline_3480); + if (rc == 0) { + rc = tape_register_discipline(&tape_discipline_3490); + if (rc) + tape_unregister_discipline(&tape_discipline_3480); + } + if (rc) + DBF_EVENT(3, "34xx init failed\n"); + else + DBF_EVENT(3, "34xx registered\n"); + return rc; +} + +void +tape_34xx_exit(void) +{ + tape_unregister_discipline(&tape_discipline_3480); + tape_unregister_discipline(&tape_discipline_3490); +} + +MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); +MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape " + "device driver ($Revision: 1.8 $)"); +MODULE_LICENSE("GPL"); + +module_init(tape_34xx_init); +module_exit(tape_34xx_exit); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape34xx.c linux.21rc1-ac2/drivers/s390/char/tape34xx.c --- linux.21rc1/drivers/s390/char/tape34xx.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape34xx.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,2389 +0,0 @@ -/*************************************************************************** - * - * drivers/s390/char/tape34xx.c - * common tape device discipline for 34xx tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#include "tapedefs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_S390_TAPE_DYNAMIC -#include -#endif -#include -#include -#include "tape.h" -#include "tape34xx.h" - -#define PRINTK_HEADER "T34xx:" - -tape_event_handler_t tape34xx_event_handler_table[TS_SIZE][TE_SIZE] = -{ - /* {START , DONE, FAILED, ERROR, OTHER } */ - {NULL, tape34xx_unused_done, NULL, NULL, NULL}, /* TS_UNUSED */ - {NULL, tape34xx_idle_done, NULL, NULL, NULL}, /* TS_IDLE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_DONE */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_FAILED */ - {NULL, tape34xx_block_done, NULL, NULL, NULL}, /* TS_BLOCK_INIT */ - {NULL, tape34xx_bsb_init_done, NULL, NULL, NULL}, /* TS_BSB_INIT */ - {NULL, tape34xx_bsf_init_done, NULL, NULL, NULL}, /* TS_BSF_INIT */ - {NULL, tape34xx_dse_init_done, NULL, NULL, NULL}, /* TS_DSE_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_EGA_INIT */ - {NULL, tape34xx_fsb_init_done, NULL, NULL, NULL}, /* TS_FSB_INIT */ - {NULL, tape34xx_fsf_init_done, NULL, NULL, NULL}, /* TS_FSF_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_LDI_INIT */ - {NULL, tape34xx_lbl_init_done, NULL, NULL, NULL}, /* TS_LBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_MSE_INIT */ - {NULL, tape34xx_nop_init_done, NULL, NULL, NULL}, /* TS_NOP_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBA_INIT */ - {NULL, tape34xx_rbi_init_done, NULL, NULL, NULL}, /* TS_RBI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBU_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RBL_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RDC_INIT */ - {NULL, tape34xx_rfo_init_done, NULL, NULL, NULL}, /* TS_RFO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_RSD_INIT */ - {NULL, tape34xx_rew_init_done, NULL, NULL, NULL}, /* TS_REW_INIT */ - {NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL}, /* TS_REW_RELEASE_IMIT */ - {NULL, tape34xx_run_init_done, NULL, NULL, NULL}, /* TS_RUN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SEN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SID_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SNP_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SPG_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SWI_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SMR_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_SYN_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_TIO_INIT */ - {NULL, NULL, NULL, NULL, NULL}, /* TS_UNA_INIT */ - {NULL, tape34xx_wri_init_done, NULL, NULL, NULL}, /* TS_WRI_INIT */ - {NULL, tape34xx_wtm_init_done, NULL, NULL, NULL}, /* TS_WTM_INIT */ - {NULL, NULL, NULL, NULL, NULL}}; /* TS_NOT_OPER */ - - -int -tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) -{ - return -EINVAL; // no additional ioctls - -} - -ccw_req_t * -tape34xx_write_block (const char *data, size_t count, tape_info_t * ti) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - void *mem; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xwbl nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - mem = kmalloc (count, GFP_KERNEL); - if (!mem) { - tape_free_request (cqr); -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xwbl nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - if (copy_from_user (mem, data, count)) { - kfree (mem); - tape_free_request (cqr); -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xwbl segf."); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - - ccw->cmd_code = WRITE_CMD; - ccw->flags = 0; - ccw->count = count; - set_normalized_cda (ccw, (unsigned long) mem); - if ((ccw->cda) == 0) { - kfree (mem); - tape_free_request (cqr); - return NULL; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = mem; - ti->userbuf = (void *) data; - tapestate_set (ti, TS_WRI_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xwbl ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -void -tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti) -{ - unsigned long lockflags; - ccw1_t *ccw; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ccw = cqr->cpaddr; - ccw++; - clear_normalized_cda (ccw); - kfree (ti->kernbuf); - tape_free_request (cqr); - ti->kernbuf = ti->userbuf = NULL; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xfwb free"); -#endif /* TAPE_DEBUG */ -} - -ccw_req_t * -tape34xx_read_block (const char *data, size_t count, tape_info_t * ti) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - void *mem; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xrbl nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - mem = kmalloc (count, GFP_KERNEL); - if (!mem) { - tape_free_request (cqr); -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xrbl nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - - ccw->cmd_code = READ_FORWARD; - ccw->flags = 0; - ccw->count = count; - set_normalized_cda (ccw, (unsigned long) mem); - if ((ccw->cda) == 0) { - kfree (mem); - tape_free_request (cqr); - return NULL; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = mem; - ti->userbuf = (void *) data; - tapestate_set (ti, TS_RFO_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xrbl ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -ccw_req_t * -tape34xx_read_opposite (tape_info_t * ti,int novalue) -{ - ccw_req_t *cqr; - ccw1_t *ccw; - size_t count; - // first, retrieve the count from the old cqr. - cqr = ti->cqr; - ccw = cqr->cpaddr; - ccw++; - count=ccw->count; - // free old cqr. - clear_normalized_cda (ccw); - tape_free_request (cqr); - // build new cqr - cqr = tape_alloc_ccw_req (ti, 3, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xrop nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - - ccw->cmd_code = READ_BACKWARD; - ccw->flags = CCW_FLAG_CC; - ccw->count = count; - set_normalized_cda (ccw, (unsigned long) ti->kernbuf); - if ((ccw->cda) == 0) { - tape_free_request (cqr); - return NULL; - } - ccw++; - ccw->cmd_code = FORSPACEBLOCK; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - ccw->cda = (unsigned long)ccw; - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 1; - ccw->cda = (unsigned long)ccw; - tapestate_set (ti, TS_RBA_INIT); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xrop ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -void -tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti) -{ - unsigned long lockflags; - size_t cpysize; - ccw1_t *ccw; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ccw = cqr->cpaddr; - ccw++; - cpysize = ccw->count - ti->devstat.rescnt; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if (copy_to_user (ti->userbuf, ti->kernbuf, cpysize)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfrb segf."); -#endif /* TAPE_DEBUG */ - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - clear_normalized_cda (ccw); - kfree (ti->kernbuf); - tape_free_request (cqr); - ti->kernbuf = ti->userbuf = NULL; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xfrb free"); -#endif /* TAPE_DEBUG */ -} - -/* - * The IOCTL interface is implemented in the following section, - * excepted the MTRESET, MTSETBLK which are handled by tapechar.c - */ -/* - * MTFSF: Forward space over 'count' file marks. The tape is positioned - * at the EOT (End of Tape) side of the file mark. - */ -ccw_req_t * -tape34xx_mtfsf (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsf parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsf nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = FORSPACEFILE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_FSF_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xfsf ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTBSF: Backward space over 'count' file marks. The tape is positioned at - * the EOT (End of Tape) side of the last skipped file mark. - */ -ccw_req_t * -tape34xx_mtbsf (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsf parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsf nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = BACKSPACEFILE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_BSF_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xbsf ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTFSR: Forward space over 'count' tape blocks (blocksize is set - * via MTSETBLK. - */ -ccw_req_t * -tape34xx_mtfsr (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsr parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsr nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = FORSPACEBLOCK; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_FSB_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xfsr ccwgen"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTBSR: Backward space over 'count' tape blocks. - * (blocksize is set via MTSETBLK. - */ -ccw_req_t * -tape34xx_mtbsr (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsr parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsr nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = BACKSPACEBLOCK; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_BSB_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xbsr ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTWEOF: Write 'count' file marks at the current position. - */ -ccw_req_t * -tape34xx_mtweof (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xweo parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xweo nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = WRITETAPEMARK; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_WTM_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xweo ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTREW: Rewind the tape. - */ -ccw_req_t * -tape34xx_mtrew (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 3, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xrew nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = REWIND; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_REW_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xrew ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTOFFL: Rewind the tape and put the drive off-line. - * Implement 'rewind unload' - */ -ccw_req_t * -tape34xx_mtoffl (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 3, 32); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xoff nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = REWIND_UNLOAD; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = SENSE; - ccw->flags = 0; - ccw->count = 32; - ccw->cda = (unsigned long) cqr->cpaddr; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_RUN_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xoff ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTNOP: 'No operation'. - */ -ccw_req_t * -tape34xx_mtnop (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 1, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xnop nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) ccw->cmd_code; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xnop ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTBSFM: Backward space over 'count' file marks. - * The tape is positioned at the BOT (Begin Of Tape) side of the - * last skipped file mark. - */ -ccw_req_t * -tape34xx_mtbsfm (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsm parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbsm nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = BACKSPACEFILE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_BSF_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xbsm ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTFSFM: Forward space over 'count' file marks. - * The tape is positioned at the BOT (Begin Of Tape) side - * of the last skipped file mark. - */ -ccw_req_t * -tape34xx_mtfsfm (tape_info_t * ti, int count) -{ - long lockflags; - int i; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count == 0) || (count > 510)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsm parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - cqr = tape_alloc_ccw_req (ti, 2 + count, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xfsm nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - for (i = 0; i < count; i++) { - ccw->cmd_code = FORSPACEFILE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - } - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_FSF_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xfsm ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTEOM: positions at the end of the portion of the tape already used - * for recordind data. MTEOM positions after the last file mark, ready for - * appending another file. - * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind. - */ -ccw_req_t * -tape34xx_mteom (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 4, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xeom nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = FORSPACEFILE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = CCW_CMD_TIC; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (cqr->cpaddr); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_FSF_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xeom ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTERASE: erases the tape. - */ -ccw_req_t * -tape34xx_mterase (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 5, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xera nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = REWIND; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = ERASE_GAP; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = DATA_SEC_ERASE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_DSE_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xera ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTSETDENSITY: set tape density. - */ -ccw_req_t * -tape34xx_mtsetdensity (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xden nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xden ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTSEEK: seek to the specified block. - */ -ccw_req_t * -tape34xx_mtseek (tape_info_t * ti, int count) -{ - long lockflags; - __u8 *data; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((data = kmalloc (4 * sizeof (__u8), GFP_KERNEL)) == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xsee nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - data[0] = 0x01; - data[1] = data[2] = data[3] = 0x00; - if (count >= 4194304) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xsee parm"); -#endif /* TAPE_DEBUG */ - kfree(data); - return NULL; - } - if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08) // IDRC on - - data[1] = data[1] | 0x80; - data[3] += count % 256; - data[2] += (count / 256) % 256; - data[1] += (count / 65536); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xsee id:"); - debug_int_event (tape_debug_area,6,count); -#endif /* TAPE_DEBUG */ - cqr = tape_alloc_ccw_req (ti, 3, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xsee nomem"); -#endif /* TAPE_DEBUG */ - kfree (data); - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = LOCATE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 4; - set_normalized_cda (ccw, (unsigned long) data); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = data; - ti->userbuf = NULL; - tapestate_set (ti, TS_LBL_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xsee ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTTELL: Tell block. Return the number of block relative to current file. - */ -ccw_req_t * -tape34xx_mttell (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - void *mem; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xtel nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - mem = kmalloc (8, GFP_KERNEL); - if (!mem) { - tape_free_request (cqr); -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xtel nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - - ccw->cmd_code = READ_BLOCK_ID; - ccw->flags = 0; - ccw->count = 8; - set_normalized_cda (ccw, (unsigned long) mem); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = mem; - ti->userbuf = NULL; - tapestate_set (ti, TS_RBI_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xtel ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTSETDRVBUFFER: Set the tape drive buffer code to number. - * Implement NOP. - */ -ccw_req_t * -tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xbuf nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xbuf ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTLOCK: Locks the tape drive door. - * Implement NOP CCW command. - */ -ccw_req_t * -tape34xx_mtlock (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xloc nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xloc ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTUNLOCK: Unlocks the tape drive door. - * Implement the NOP CCW command. - */ -ccw_req_t * -tape34xx_mtunlock (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xulk nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xulk ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTLOAD: Loads the tape. - * This function is not implemented and returns NULL, which causes the Frontend to wait for a medium being loaded. - * The 3480/3490 type Tapes do not support a load command - */ -ccw_req_t * -tape34xx_mtload (tape_info_t * ti, int count) -{ - return NULL; -} - -/* - * MTUNLOAD: Rewind the tape and unload it. - */ -ccw_req_t * -tape34xx_mtunload (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 3, 32); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xunl nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = REWIND_UNLOAD; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ccw++; - ccw->cmd_code = SENSE; - ccw->flags = 0; - ccw->count = 32; - ccw->cda = (unsigned long) cqr->cpaddr; - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_RUN_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xunl ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTCOMPRESSION: used to enable compression. - * Sets the IDRC on/off. - */ -ccw_req_t * -tape34xx_mtcompression (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - if ((count < 0) || (count > 1)) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xcom parm"); -#endif /* TAPE_DEBUG */ - return NULL; - } - if (count == 0) - ((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x00; // IDRC off - - else - ((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x08; // IDRC on - - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xcom nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xcom ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTSTPART: Move the tape head at the partition with the number 'count'. - * Implement the NOP CCW command. - */ -ccw_req_t * -tape34xx_mtsetpart (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xspa nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xspa ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTMKPART: .... dummy . - * Implement the NOP CCW command. - */ -ccw_req_t * -tape34xx_mtmkpart (tape_info_t * ti, int count) -{ - long lockflags; - ccw_req_t *cqr; - ccw1_t *ccw; - cqr = tape_alloc_ccw_req (ti, 2, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xnpa nomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = NULL; - ti->userbuf = NULL; - tapestate_set (ti, TS_NOP_INIT); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xnpa ccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} - -/* - * MTIOCGET: query the tape drive status. - */ -ccw_req_t * -tape34xx_mtiocget (tape_info_t * ti, int count) -{ - return NULL; -} - -/* - * MTIOCPOS: query the tape position. - */ -ccw_req_t * -tape34xx_mtiocpos (tape_info_t * ti, int count) -{ - return NULL; -} - -ccw_req_t * tape34xx_bread (struct request *req,tape_info_t* ti,int tapeblock_major) { - ccw_req_t *cqr; - ccw1_t *ccw; - __u8 *data; - int s2b = blksize_size[tapeblock_major][ti->blk_minor]/hardsect_size[tapeblock_major][ti->blk_minor]; - int realcount; - int size,bhct = 0; - struct buffer_head* bh; - for (bh = req->bh; bh; bh = bh->b_reqnext) { - if (bh->b_size > blksize_size[tapeblock_major][ti->blk_minor]) - for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor]) - bhct++; - else - bhct++; - } - if ((data = kmalloc (4 * sizeof (__u8), GFP_ATOMIC)) == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"xBREDnomem"); -#endif /* TAPE_DEBUG */ - return NULL; - } - data[0] = 0x01; - data[1] = data[2] = data[3] = 0x00; - realcount=req->sector/s2b; - if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08) // IDRC on - - data[1] = data[1] | 0x80; - data[3] += realcount % 256; - data[2] += (realcount / 256) % 256; - data[1] += (realcount / 65536); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xBREDid:"); - debug_int_event (tape_debug_area,6,realcount); -#endif /* TAPE_DEBUG */ - cqr = tape_alloc_ccw_req (ti, 2+bhct+1, 0); - if (!cqr) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,6,"xBREDnomem"); -#endif /* TAPE_DEBUG */ - kfree(data); - return NULL; - } - ccw = cqr->cpaddr; - ccw->cmd_code = MODE_SET_DB; - ccw->flags = CCW_FLAG_CC; - ccw->count = 1; - set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte))); - if (realcount!=ti->position) { - ccw++; - ccw->cmd_code = LOCATE; - ccw->flags = CCW_FLAG_CC; - ccw->count = 4; - set_normalized_cda (ccw, (unsigned long) data); - } - ti->position=realcount+req->nr_sectors/s2b; - for (bh=req->bh;bh!=NULL;) { - ccw->flags = CCW_FLAG_CC; - if (bh->b_size >= blksize_size[tapeblock_major][ti->blk_minor]) { - for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor]) { - ccw++; - ccw->flags = CCW_FLAG_CC; - ccw->cmd_code = READ_FORWARD; - ccw->count = blksize_size[tapeblock_major][ti->blk_minor]; - set_normalized_cda (ccw, __pa (bh->b_data + size)); - } - bh = bh->b_reqnext; - } else { /* group N bhs to fit into byt_per_blk */ - for (size = 0; bh != NULL && size < blksize_size[tapeblock_major][ti->blk_minor];) { - ccw++; - ccw->flags = CCW_FLAG_DC; - ccw->cmd_code = READ_FORWARD; - ccw->count = bh->b_size; - set_normalized_cda (ccw, __pa (bh->b_data)); - size += bh->b_size; - bh = bh->b_reqnext; - } - if (size != blksize_size[tapeblock_major][ti->blk_minor]) { - PRINT_WARN ("Cannot fulfill small request %d vs. %d (%ld sects)\n", - size, - blksize_size[tapeblock_major][ti->blk_minor], - req->nr_sectors); - kfree(data); - tape_free_request (cqr); - return NULL; - } - } - } - ccw -> flags &= ~(CCW_FLAG_DC); - ccw -> flags |= (CCW_FLAG_CC); - ccw++; - ccw->cmd_code = NOP; - ccw->flags = 0; - ccw->count = 0; - ccw->cda = (unsigned long) (&(ccw->cmd_code)); - ti->kernbuf = data; - ti->userbuf = NULL; - tapestate_set (ti, TS_BLOCK_INIT); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xBREDccwg"); -#endif /* TAPE_DEBUG */ - return cqr; -} -void tape34xx_free_bread (ccw_req_t* cqr,struct _tape_info_t* ti) { - ccw1_t* ccw; - for (ccw=(ccw1_t*)cqr->cpaddr;(ccw->flags & CCW_FLAG_CC)||(ccw->flags & CCW_FLAG_DC);ccw++) - if ((ccw->cmd_code == MODE_SET_DB) || - (ccw->cmd_code == LOCATE) || - (ccw->cmd_code == READ_FORWARD)) - clear_normalized_cda(ccw); - tape_free_request(cqr); - kfree(ti->kernbuf); - ti->kernbuf=NULL; -} - -/* event handlers */ -void -tape34xx_default_handler (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xdefhandle"); -#endif /* TAPE_DEBUG */ - PRINT_ERR ("TAPE34XX: An unexpected Unit Check occurred.\n"); - PRINT_ERR ("TAPE34XX: Please read Documentation/s390/TAPE and report it!\n"); - PRINT_ERR ("TAPE34XX: Current state is: %s", - (((tapestate_get (ti) < TS_SIZE) && (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "->UNKNOWN STATE<-")); - tape_dump_sense (&ti->devstat); - ti->rc = -EIO; - ti->wanna_wakeup=1; - switch (tapestate_get(ti)) { - case TS_REW_RELEASE_INIT: - tapestate_set(ti,TS_FAILED); - wake_up (&ti->wq); - break; - case TS_BLOCK_INIT: - tapestate_set(ti,TS_FAILED); - schedule_tapeblock_exec_IO(ti); - break; - default: - tapestate_set(ti,TS_FAILED); - wake_up_interruptible (&ti->wq); - } -} - -void -tape34xx_unexpect_uchk_handler (tape_info_t * ti) -{ - if ((ti->devstat.ii.sense.data[0] == 0x40) && - (ti->devstat.ii.sense.data[1] == 0x40) && - (ti->devstat.ii.sense.data[3] == 0x43)) { - // no tape in the drive - PRINT_INFO ("Drive %d not ready. No volume loaded.\n", ti->rew_minor / 2); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xuuh nomed"); -#endif /* TAPE_DEBUG */ - tapestate_set (ti, TS_FAILED); - ti->rc = -ENOMEDIUM; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); - } else if ((ti->devstat.ii.sense.data[0] == 0x42) && - (ti->devstat.ii.sense.data[1] == 0x44) && - (ti->devstat.ii.sense.data[3] == 0x3b)) { - PRINT_INFO ("Media in drive %d was changed!\n", - ti->rew_minor / 2); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xuuh medchg"); -#endif - /* nothing to do. chan end & dev end will be reported when io is finished */ - } else { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xuuh unexp"); - debug_text_event (tape_debug_area,3,"state:"); - debug_text_event (tape_debug_area,3,((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : - "TS UNKNOWN"); -#endif /* TAPE_DEBUG */ - tape34xx_default_handler (ti); - } -} - -void -tape34xx_unused_done (tape_info_t * ti) -{ - if (ti->medium_is_unloaded) { - // A medium was inserted in the drive! -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xuui med"); -#endif /* TAPE_DEBUG */ - PRINT_WARN ("A medium was inserted into the tape.\n"); - ti->medium_is_unloaded=0; - } else { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"unsol.irq!"); - debug_text_event (tape_debug_area,3,"dev end"); - debug_int_exception (tape_debug_area,3,ti->devinfo.irq); -#endif /* TAPE_DEBUG */ - PRINT_WARN ("Unsolicited IRQ (Device End) caught in unused state.\n"); - tape_dump_sense (&ti->devstat); - } -} - - -void -tape34xx_idle_done (tape_info_t * ti) -{ - if (ti->medium_is_unloaded) { - // A medium was inserted in the drive! -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"xuud med"); -#endif /* TAPE_DEBUG */ - PRINT_WARN ("A medium was inserted into the tape.\n"); - ti->medium_is_unloaded=0; - wake_up_interruptible (&ti->wq); - } else { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"unsol.irq!"); - debug_text_event (tape_debug_area,3,"dev end"); - debug_int_exception (tape_debug_area,3,ti->devinfo.irq); -#endif /* TAPE_DEBUG */ - PRINT_WARN ("Unsolicited IRQ (Device End) caught in idle state.\n"); - tape_dump_sense (&ti->devstat); - } -} - -void -tape34xx_block_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"x:bREQdone"); -#endif /* TAPE_DEBUG */ - tapestate_set(ti,TS_DONE); - schedule_tapeblock_exec_IO(ti); -} - -void -tape34xx_bsf_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"bsf done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_dse_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"dse done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_fsf_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"fsf done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_fsb_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"fsb done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_bsb_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"bsb done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up (&ti->wq); -} - -void -tape34xx_lbl_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"lbl done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - //s390irq_spin_unlock(tape->devinfo.irq); - ti->wanna_wakeup=1; - wake_up (&ti->wq); -} - -void -tape34xx_nop_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"nop done.."); - debug_text_exception (tape_debug_area,6,"or rew/rel"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - //s390irq_spin_unlock(tape->devinfo.irq); - ti->wanna_wakeup=1; - wake_up (&ti->wq); -} - -void -tape34xx_rfo_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"rfo done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up (&ti->wq); -} - -void -tape34xx_rbi_init_done (tape_info_t * ti) -{ - __u8 *data; -#ifdef TAPE_DEBUG - int i; -#endif - tapestate_set (ti, TS_FAILED); - data = ti->kernbuf; - ti->rc = data[3]; - ti->rc += 256 * data[2]; - ti->rc += 65536 * (data[1] & 0x3F); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"rbi done"); - debug_text_event (tape_debug_area,6,"data:"); - for (i=0;i<8;i++) - debug_int_event (tape_debug_area,6,data[i]); -#endif - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_rew_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"rew done"); -#endif - //BH: use irqsave - //s390irq_spin_lock(tape->devinfo.irq); - tapestate_set (ti, TS_DONE); - ti->rc = 0; - //s390irq_spin_unlock(tape->devinfo.irq); - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_rew_release_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"rewR done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - //s390irq_spin_unlock(tape->devinfo.irq); - ti->wanna_wakeup=1; - wake_up (&ti->wq); -} - -void -tape34xx_run_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"rew done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_wri_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"wri done"); -#endif - //BH: use irqsave - //s390irq_spin_lock(ti->devinfo.irq); - tapestate_set (ti, TS_DONE); - ti->rc = 0; - //s390irq_spin_unlock(ti->devinfo.irq); - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -void -tape34xx_wtm_init_done (tape_info_t * ti) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"wtm done"); -#endif - tapestate_set (ti, TS_DONE); - ti->rc = 0; - ti->wanna_wakeup=1; - wake_up_interruptible (&ti->wq); -} - -/* This function analyses the tape's sense-data in case of a unit-check. If possible, - it tries to recover from the error. Else the user is informed about the problem. */ -void -tape34xx_error_recovery (tape_info_t* ti) -{ - __u8* sense=ti->devstat.ii.sense.data; - int inhibit_cu_recovery=0; - int cu_type=ti->discipline->cu_type; - if ((((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)&0x80) inhibit_cu_recovery=1; - if (tapestate_get(ti)==TS_BLOCK_INIT) { - // no recovery for block device, bottom half will retry... - tape34xx_error_recovery_has_failed(ti,EIO); - return; - } - if (sense[0]&SENSE_COMMAND_REJECT) - switch (tapestate_get(ti)) { - case TS_BLOCK_INIT: - case TS_DSE_INIT: - case TS_EGA_INIT: - case TS_WRI_INIT: - case TS_WTM_INIT: - if (sense[1]&SENSE_WRITE_PROTECT) { - // trying to write, but medium is write protected - tape34xx_error_recovery_has_failed(ti,EACCES); - return; - } - default: - tape34xx_error_recovery_HWBUG(ti,1); - return; - } - // special cases for various tape-states when reaching end of recorded area - if (((sense[0]==0x08) || (sense[0]==0x10) || (sense[0]==0x12)) && - ((sense[1]==0x40) || (sense[1]==0x0c))) - switch (tapestate_get(ti)) { - case TS_FSF_INIT: - // Trying to seek beyond end of recorded area - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case TS_LBL_INIT: - // Block could not be located. - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case TS_RFO_INIT: - // Try to read beyond end of recorded area -> 0 bytes read - tape34xx_error_recovery_has_failed(ti,0); - return; - } - // Sensing special bits - if (sense[0]&SENSE_BUS_OUT_CHECK) { - tape34xx_error_recovery_do_retry(ti); - return; - } - if (sense[0]&SENSE_DATA_CHECK) { - // hardware failure, damaged tape or improper operating conditions - switch (sense[3]) { - case 0x23: - // a read data check occurred - if ((sense[2]&SENSE_TAPE_SYNC_MODE) || - (inhibit_cu_recovery)) { - // data check is not permanent, may be recovered. - // We always use async-mode with cu-recovery, so this should *never* happen. - tape34xx_error_recovery_HWBUG(ti,2); - return; - } else { - // data check is permanent, CU recovery has failed - PRINT_WARN("Permanent read error, recovery failed!\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - } - case 0x25: - // a write data check occurred - if ((sense[2]&SENSE_TAPE_SYNC_MODE) || - (inhibit_cu_recovery)) { - // data check is not permanent, may be recovered. - // We always use async-mode with cu-recovery, so this should *never* happen. - tape34xx_error_recovery_HWBUG(ti,3); - return; - } else { - // data check is permanent, cu-recovery has failed - PRINT_WARN("Permanent write error, recovery failed!\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - } - case 0x26: - // Data Check (read opposite) occurred. We'll recover this. - tape34xx_error_recovery_read_opposite(ti); - return; - case 0x28: - // The ID-Mark at the beginning of the tape could not be written. This is fatal, we'll report and exit. - PRINT_WARN("ID-Mark could not be written. Check your hardware!\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x31: - // Tape void. Tried to read beyond end of device. We'll report and exit. - PRINT_WARN("Try to read beyond end of recorded area!\n"); - tape34xx_error_recovery_has_failed(ti,ENOSPC); - return; - case 0x41: - // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit. - PRINT_WARN("Illegal block-id sequence found!\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - default: - // well, all data checks for 3480 should result in one of the above erpa-codes. if not -> bug - // On 3490, other data-check conditions do exist. - if (cu_type==0x3480) { - tape34xx_error_recovery_HWBUG(ti,4); - return; - } - } - } - if (sense[0]&SENSE_OVERRUN) { - // A data overrun between cu and drive occurred. The channel speed is to slow! We'll report this and exit! - switch (sense[3]) { - case 0x40: // overrun error - PRINT_WARN ("Data overrun error between control-unit and drive. Use a faster channel connection, if possible! \n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - default: - // Overrun bit is set, but erpa does not show overrun error. This is a bug. - tape34xx_error_recovery_HWBUG(ti,5); - return; - } - } - if (sense[1]&SENSE_RECORD_SEQUENCE_ERR) { - switch (sense[3]) { - case 0x41: - // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit. - PRINT_WARN("Illegal block-id sequence found!\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - default: - // Record sequence error bit is set, but erpa does not show record sequence error. This is a bug. - tape34xx_error_recovery_HWBUG(ti,6); - return; - } - } - // Sensing erpa codes - switch (sense[3]) { - case 0x00: - // Everything is fine, but we got a unit check. Report and ignore! - PRINT_WARN ("Non-error sense was found. Unit-check will be ignored, expect errors...\n"); - return; - case 0x21: - // Data streaming not operational. Cu switches to interlock mode, we reissue the command. - PRINT_WARN ("Data streaming not operational. Switching to interlock-mode! \n"); - tape34xx_error_recovery_do_retry(ti); - return; - case 0x22: - // Path equipment check. Might be drive adapter error, buffer error on the lower interface, internal path not useable, or error during cartridge load. - // All of the above are not recoverable - PRINT_WARN ("A path equipment check occurred. One of the following conditions occurred:\n"); - PRINT_WARN ("drive adapter error,buffer error on the lower interface, internal path not useable, error during cartridge load.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x23: - // Read data check. Should have been be covered earlier -> Bug! - tape34xx_error_recovery_HWBUG(ti,7); - return; - case 0x24: - // Load display check. Load display was command was issued, but the drive is displaying a drive check message. Can be threated as "device end". - tape34xx_error_recovery_succeded(ti); - return; - case 0x25: - // Write data check. Should have been covered earlier -> Bug! - tape34xx_error_recovery_HWBUG(ti,8); - return; - case 0x26: - // Data check (read opposite). Should have been covered earlier -> Bug! - tape34xx_error_recovery_HWBUG(ti,9); - return; - case 0x27: - // Command reject. May indicate illegal channel program or buffer over/underrun. - // Since all channel programms are issued by this driver and ought be correct, - // we assume a over/underrun situaltion and retry the channel program. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x28: - // Write id mark check. Should have beed covered earlier -> bug! - tape34xx_error_recovery_HWBUG(ti,10); - return; - case 0x29: - // Function incompatible. Either idrc is on but hardware not capable doing idrc - // or a perform subsystem func is issued and the cu is not online. Anyway, this - // cannot be recovered and is an I/O error. - PRINT_WARN ("Function incompatible. Try to switch off idrc! \n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x2a: - // Unsolicited environmental data. An internal counter overflows, we can ignore - // this and reissue the cmd. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x2b: - // Environmental data present. Indicates either unload completed ok or read buffered - // log command completed ok. - if (tapestate_get(ti)==TS_RUN_INIT) { - // Rewind unload completed ok. - tape34xx_error_recovery_succeded(ti); - return; - } - // Since we do not issue read buffered log commands, this should never occur -> bug. - tape34xx_error_recovery_HWBUG(ti,11); - return; - case 0x2c: - // Permanent equipment check. cu has tried recovery, but did not succeed. This is an - // I/O error. - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x2d: - // Data security erase failure. - if (tapestate_get(ti)==TS_DSE_INIT) { - // report an I/O error - tape34xx_error_recovery_has_failed(ti,EIO); - return; - } - // Data security erase failure, but no such command issued. This is a bug. - tape34xx_error_recovery_HWBUG(ti,12); - return; - case 0x2e: - // Not capable. This indicates either that the drive fails reading the format id mark - // or that that format specified is not supported by the drive. We write a message and - // return an I/O error. - PRINT_WARN("Drive not capable processing the tape format!"); - tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE); - return; - case 0x2f: - // This erpa is reserved. This is a bug. - tape34xx_error_recovery_HWBUG(ti,13); - return; - case 0x30: - // The medium is write protected, while trying to write on it. We'll report this. - PRINT_WARN("Medium is write protected!\n"); - tape34xx_error_recovery_has_failed(ti,EACCES); - return; - case 0x31: - // Tape void. Should have beed covered ealier -> bug - tape34xx_error_recovery_HWBUG(ti,14); - return; - case 0x32: - // Tension loss. We cannot recover this, it's an I/O error. - PRINT_WARN("The drive lost tape tension.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x33: - // Load Failure. The catridge was not inserted correctly or the tape is not threaded - // correctly. We cannot recover this, the user has to reload the catridge. - PRINT_WARN("Cartridge load failure. Reload the cartridge and try again.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x34: - // Unload failure. The drive cannot maintain tape tension and control tape movement - // during an unload operation. - PRINT_WARN("Failure during cartridge unload. Please try manually.\n"); - if (tapestate_get(ti)!=TS_RUN_INIT) { - tape34xx_error_recovery_HWBUG(ti,15); - return; - } - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x35: - // Drive equipment check. One of the following: - // - cu cannot recover from a drive detected error - // - a check code message is displayed on drive message/load displays - // - the cartridge loader does not respond correctly - // - a failure occurs during an index, load, or unload cycle - PRINT_WARN("Equipment check! Please check the drive and the cartridge loader.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x36: - switch (cu_type) { - case 0x3480: - // This erpa is reserved for 3480 -> BUG - tape34xx_error_recovery_HWBUG(ti,16); - return; - case 0x3490: - // End of data. This is a permanent I/O error, which cannot be recovered. - // A read-type command has reached the end-of-data mark. - tape34xx_error_recovery_has_failed(ti,EIO); - return; - } - case 0x37: - // Tape length error. The tape is shorter than reported in the beginning-of-tape data. - PRINT_WARN("Tape length error.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x38: - // Physical end of tape. A read/write operation reached the physical end of tape. - if (tapestate_get(ti)==TS_WRI_INIT || - tapestate_get(ti)==TS_DSE_INIT || - tapestate_get(ti)==TS_EGA_INIT || - tapestate_get(ti)==TS_WTM_INIT){ - tape34xx_error_recovery_has_failed(ti,ENOSPC); - } else { - tape34xx_error_recovery_has_failed(ti,EIO); - } - return; - case 0x39: - // Backward at BOT. The drive is at BOT and is requestet to move backward. - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x3a: - // Drive switched not ready, but the command needs the drive to be ready. - PRINT_WARN("Drive not ready. Turn the ready/not ready switch to ready position and try again.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x3b: - // Manual rewind or unload. This causes an I/O error. - PRINT_WARN("Medium was rewound or unloaded manually. Expect errors! Please do only use the mtoffl and mtrew ioctl to unload tapes or rewind tapes.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - // These erpas are reserved -> BUG - tape34xx_error_recovery_HWBUG(ti,17); - return; - case 0x40: - // Overrun error. This should have been covered earlier -> bug. - tape34xx_error_recovery_HWBUG(ti,18); - return; - case 0x41: - // Record sequence error. This should have been covered earlier -> bug. - tape34xx_error_recovery_HWBUG(ti,19); - return; - case 0x42: - // Degraded mode. A condition that can cause degraded performace is detected. - PRINT_WARN("Subsystem is running in degraded mode. This may compromise your performace.\n"); - tape34xx_error_recovery_do_retry(ti); - return; - case 0x43: - // Drive not ready. Probably swith the ready/not ready switch to ready? - PRINT_WARN("The drive is not ready. Maybe no medium in?\n"); - tape34xx_error_recovery_has_failed(ti,ENOMEDIUM); - return; - case 0x44: - // Locate Block unsuccessfull. We'll report this. - if ((tapestate_get(ti)!=TS_BLOCK_INIT) && - (tapestate_get(ti)!=TS_LBL_INIT)) { - tape34xx_error_recovery_HWBUG(ti,20); // No locate block was issued... - return; - } - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x45: - // The drive is assigned elsewhere [to a different channel path/computer]. - PRINT_WARN("The drive is assigned elsewhere.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x46: - // Drive not online. Drive may be switched offline, the power supply may be switched off - // or the drive address may not be set correctly. - PRINT_WARN("The drive is not online."); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x47: - // Volume fenced. cu reports volume integrity is lost! - PRINT_WARN("Volume fenced. The volume integrity is lost! \n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x48: - // Log sense data and retry request. We'll do so... - tape34xx_error_recovery_do_retry(ti); - return; - case 0x49: - // Bus out check. A parity check error on the bus was found. PRINT_WARN("Bus out check. A data transfer over the bus was corrupted.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x4a: - // Control unit erp failed. We'll report this. - PRINT_WARN("The control unit failed recovering an I/O error.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x4b: - // Cu and drive incompatible. The drive requests micro-program patches, which are not available on the cu. - PRINT_WARN("The drive needs microprogram patches from the control unit, which are not available.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x4c: - // Recovered Check-One failure. Cu develops a hardware error, but is able to recover. We'll reissue the command. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x4d: - switch (cu_type) { - case 0x3480: - // This erpa is reserved for 3480 -> bug - tape34xx_error_recovery_HWBUG(ti,21); - return; - case 0x3490: - // Resetting event received. Since the driver does not support resetting event recovery - // (which has to be handled by the I/O Layer), we'll report and retry our command. - tape34xx_error_recovery_do_retry(ti); - return; - } - case 0x4e: - switch (cu_type) { - case 0x3480: - // This erpa is reserved for 3480 -> bug. - tape34xx_error_recovery_HWBUG(ti,22); - return; - case 0x3490: - // Maximum block size exeeded. This indicates, that the block to be written is larger - // than allowed for buffered mode. We'll report this... - PRINT_WARN("Maximum block size for buffered mode exceeded.\n"); - tape34xx_error_recovery_has_failed(ti,ENOBUFS); - return; - } - case 0x4f: - // These erpas are reserved -> bug - tape34xx_error_recovery_HWBUG(ti,23); - return; - case 0x50: - // Read buffered log (Overflow). Cu is running in extended beffered log mode, and a counter overflows. - // This should never happen, since we're never running in extended buffered log mode -> bug. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x51: - // Read buffered log (EOV). EOF processing occurs while the cu is in extended buffered log mode. - // This should never happen, since we're never running in extended buffered log mode -> bug. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x52: - // End of Volume complete. Rewind unload completed ok. We'll report to the user... - if (tapestate_get(ti)!=TS_RUN_INIT) { - tape34xx_error_recovery_HWBUG(ti,24); - return; - } - tape34xx_error_recovery_succeded(ti); - return; - case 0x53: - // Global command intercept. We'll have to reissue our command. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x54: - // Channel interface recovery (temporary). This can be recovered by reissuing the command. - tape34xx_error_recovery_do_retry(ti); - return; - case 0x55: - // Channel interface recovery (permanent). This cannot be recovered, we'll inform the user. - PRINT_WARN("A permanent channel interface error occurred.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x56: - // Channel protocol error. This cannot be recovered. - PRINT_WARN("A channel protocol error occurred.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x57: - switch (cu_type) { - case 0x3480: - // Attention intercept. We have to reissue the command. - PRINT_WARN("An attention intercept occurred, which will be recovered.\n"); - tape34xx_error_recovery_do_retry(ti); - return; - case 0x3490: - // Global status intercept. We have to reissue the command. - PRINT_WARN("An global status intercept was received, which will be recovered.\n"); - tape34xx_error_recovery_do_retry(ti); - return; - } - case 0x58: - case 0x59: - // These erpas are reserved -> bug. - tape34xx_error_recovery_HWBUG(ti,25); - return; - case 0x5a: - // Tape length incompatible. The tape inserted is too long, - // which could cause damage to the tape or the drive. - PRINT_WARN("Tape length incompatible [should be IBM Cartridge System Tape]. May cause damage to drive or tape.n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x5b: - // Format 3480 XF incompatible - if (sense[1]&SENSE_BEGINNING_OF_TAPE) { - // Everything is fine. The tape will be overwritten in a different format. - tape34xx_error_recovery_do_retry(ti); - return; - } - PRINT_WARN("Tape format is incompatible to the drive, which writes 3480-2 XF.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x5c: - // Format 3480-2 XF incompatible - PRINT_WARN("Tape format is incompatible to the drive. The drive cannot access 3480-2 XF volumes.\n"); - tape34xx_error_recovery_has_failed(ti,EIO); - return; - case 0x5d: - // Tape length violation. - PRINT_WARN("Tape length violation [should be IBM Enhanced Capacity Cartridge System Tape]. May cause damage to drive or tape.\n"); - tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE); - return; - case 0x5e: - // Compaction algorithm incompatible. - PRINT_WARN("The volume is recorded using an incompatible compaction algorith, which is not supported by the control unit.\n"); - tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE); - return; - default: - // Reserved erpas -> bug - tape34xx_error_recovery_HWBUG(ti,26); - return; - } -} - -void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xerp fail"); - debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "UNKNOWN")); -#endif - if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) { - tape_dump_sense(&ti->devstat); - ti->rc = -error_id; - ti->wanna_wakeup=1; - switch (tapestate_get(ti)) { - case TS_REW_RELEASE_INIT: - case TS_RFO_INIT: - case TS_RBA_INIT: - tapestate_set(ti,TS_FAILED); - wake_up (&ti->wq); - break; - case TS_BLOCK_INIT: - tapestate_set(ti,TS_FAILED); - schedule_tapeblock_exec_IO(ti); - break; - default: - tapestate_set(ti,TS_FAILED); - wake_up_interruptible (&ti->wq); - } - } else { - PRINT_WARN("Recieved an unsolicited IRQ.\n"); - tape_dump_sense(&ti->devstat); - } -} - -void tape34xx_error_recovery_succeded(tape_info_t* ti) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xerp done"); - debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "UNKNOWN")); -#endif - if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_DONE)) { - tapestate_event (ti, TE_DONE); - } else { - PRINT_WARN("Recieved an unsolicited IRQ.\n"); - tape_dump_sense(&ti->devstat); - } -} - -void tape34xx_error_recovery_do_retry(tape_info_t* ti) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"xerp retr"); - debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "UNKNOWN")); -#endif - if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) { - tape_dump_sense(&ti->devstat); - while (do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, 0x00, ti->cqr->options)); - } else { - PRINT_WARN("Recieved an unsolicited IRQ.\n"); - tape_dump_sense(&ti->devstat); - } -} - -void -tape34xx_error_recovery_read_opposite (tape_info_t* ti) { - switch (tapestate_get(ti)) { - case TS_RFO_INIT: - // We did read forward, but the data could not be read *correctly*. - // We will read backward and then skip forward again. - ti->cqr=tape34xx_read_opposite(ti,0); - if (ti->cqr==NULL) - tape34xx_error_recovery_has_failed(ti,EIO); - else - tape34xx_error_recovery_do_retry(ti); - break; - case TS_RBA_INIT: - // We tried to read forward and backward, but hat no success -> failed. - tape34xx_error_recovery_has_failed(ti,EIO); - break; - case TS_BLOCK_INIT: - tape34xx_error_recovery_do_retry(ti); - break; - default: - PRINT_WARN("read_opposite_recovery_called_with_state:%s\n", - (((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "UNKNOWN")); - } -} - -void -tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno) { - devstat_t* stat=&ti->devstat; - PRINT_WARN("An unexpected condition #%d was caught in tape error recovery.\n",condno); - PRINT_WARN("Please report this incident.\n"); - PRINT_WARN("State of the tape:%s\n", - (((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >= 0)) ? - state_verbose[tapestate_get (ti)] : "UNKNOWN")); - PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X " - " %02X%02X%02X%02X %02X%02X%02X%02X \n", - stat->ii.sense.data[0], stat->ii.sense.data[1], - stat->ii.sense.data[2], stat->ii.sense.data[3], - stat->ii.sense.data[4], stat->ii.sense.data[5], - stat->ii.sense.data[6], stat->ii.sense.data[7], - stat->ii.sense.data[8], stat->ii.sense.data[9], - stat->ii.sense.data[10], stat->ii.sense.data[11], - stat->ii.sense.data[12], stat->ii.sense.data[13], - stat->ii.sense.data[14], stat->ii.sense.data[15]); - PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X " - " %02X%02X%02X%02X %02X%02X%02X%02X \n", - stat->ii.sense.data[16], stat->ii.sense.data[17], - stat->ii.sense.data[18], stat->ii.sense.data[19], - stat->ii.sense.data[20], stat->ii.sense.data[21], - stat->ii.sense.data[22], stat->ii.sense.data[23], - stat->ii.sense.data[24], stat->ii.sense.data[25], - stat->ii.sense.data[26], stat->ii.sense.data[27], - stat->ii.sense.data[28], stat->ii.sense.data[29], - stat->ii.sense.data[30], stat->ii.sense.data[31]); - tape34xx_error_recovery_has_failed(ti,EIO); -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape34xx.h linux.21rc1-ac2/drivers/s390/char/tape34xx.h --- linux.21rc1/drivers/s390/char/tape34xx.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape34xx.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,183 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tape34xx.h - * common tape device discipline for 34xx tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** - */ - -#ifndef _TAPE34XX_H - -#define _TAPE34XX_H - -/* - * The CCW commands for the Tape type of command. - */ - -#define INVALID_00 0x00 /* Invalid cmd */ -#define BACKSPACEBLOCK 0x27 /* Back Space block */ -#define BACKSPACEFILE 0x2f /* Back Space file */ -#define DATA_SEC_ERASE 0x97 /* Data security erase */ -#define ERASE_GAP 0x17 /* Erase Gap */ -#define FORSPACEBLOCK 0x37 /* Forward space block */ -#define FORSPACEFILE 0x3F /* Forward Space file */ -#define FORCE_STREAM_CNT 0xEB /* Forced streaming count # */ -#define NOP 0x03 /* No operation */ -#define READ_FORWARD 0x02 /* Read forward */ -#define REWIND 0x07 /* Rewind */ -#define REWIND_UNLOAD 0x0F /* Rewind and Unload */ -#define SENSE 0x04 /* Sense */ -#define NEW_MODE_SET 0xEB /* Guess it is Mode set */ -#define WRITE_CMD 0x01 /* Write */ -#define WRITETAPEMARK 0x1F /* Write Tape Mark */ - -#define ASSIGN 0xB7 /* 3420 REJECT,3480 OK */ -#define CONTROL_ACCESS 0xE3 /* Set high speed */ -#define DIAG_MODE_SET 0x0B /* 3420 NOP, 3480 REJECT*/ -#define LOAD_DISPLAY 0x9F /* 3420 REJECT,3480 OK */ -#define LOCATE 0x4F /* 3420 REJ, 3480 NOP */ -#define LOOP_WRITE_TO_READ 0x8B /* 3480 REJECT */ -#define MODE_SET_DB 0xDB /* 3420 REJECT,3480 OK */ -#define MODE_SET_C3 0xC3 /* for 3420 */ -#define MODE_SET_CB 0xCB /* for 3420 */ -#define MODE_SET_D3 0xD3 /* for 3420 */ -#define READ_BACKWARD 0x0C /* */ -#define READ_BLOCK_ID 0x22 /* 3420 REJECT,3480 OK */ -#define READ_BUFFER 0x12 /* 3420 REJECT,3480 OK */ -#define READ_BUFF_LOG 0x24 /* 3420 REJECT,3480 OK */ -#define RELEASE 0xD4 /* 3420 NOP, 3480 REJECT*/ -#define REQ_TRK_IN_ERROR 0x1B /* 3420 NOP, 3480 REJECT*/ -#define RESERVE 0xF4 /* 3420 NOP, 3480 REJECT*/ -#define SENSE_GROUP_ID 0x34 /* 3420 REJECT,3480 OK */ -#define SENSE_ID 0xE4 /* 3420 REJECT,3480 OK */ -#define READ_DEV_CHAR 0x64 /* Read device characteristics */ -#define SET_DIAGNOSE 0x4B /* 3420 NOP, 3480 REJECT*/ -#define SET_GROUP_ID 0xAF /* 3420 REJECT,3480 OK */ -#define SET_TAPE_WRITE_IMMED 0xC3 /* for 3480 */ -#define SUSPEND 0x5B /* 3420 REJ, 3480 NOP */ -#define SYNC 0x43 /* Synchronize (flush buffer) */ -#define UNASSIGN 0xC7 /* 3420 REJECT,3480 OK */ -#define PERF_SUBSYS_FUNC 0x77 /* 3490 CMD */ -#define READ_CONFIG_DATA 0xFA /* 3490 CMD */ -#define READ_MESSAGE_ID 0x4E /* 3490 CMD */ -#define READ_SUBSYS_DATA 0x3E /* 3490 CMD */ -#define SET_INTERFACE_ID 0x73 /* 3490 CMD */ - -#ifndef MIN -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) -#endif - - -#define BLOCKSIZE 4096 /* size of the tape rcds */ - -#define COMMAND_CHAIN CCW_FLAG_CC /* redefine from irq.h */ -#define CHANNEL_END DEV_STAT_CHN_END /* redefine from irq.h */ -#define DEVICE_END DEV_STAT_DEV_END /* redefine from irq.h */ -#define UNIT_CHECK DEV_STAT_UNIT_CHECK /* redefine from irq.h */ -#define UNIT_EXCEPTION DEV_STAT_UNIT_EXCEP /* redefine from irq.h */ -#define CONTROL_UNIT_END DEV_STAT_CU_END /* redefine from irq.h */ -#define INCORR_LEN SCHN_STAT_INCORR_LEN /* redefine from irq.h */ - -#define SENSE_COMMAND_REJECT 0x80 -#define SENSE_INTERVENTION_REQUIRED 0x40 -#define SENSE_BUS_OUT_CHECK 0x20 -#define SENSE_EQUIPMENT_CHECK 0x10 -#define SENSE_DATA_CHECK 0x08 -#define SENSE_OVERRUN 0x04 -#define SENSE_DEFERRED_UNIT_CHECK 0x02 -#define SENSE_ASSIGNED_ELSEWHERE 0x01 - -#define SENSE_LOCATE_FAILURE 0x80 -#define SENSE_DRIVE_ONLINE 0x40 -#define SENSE_RESERVED 0x20 -#define SENSE_RECORD_SEQUENCE_ERR 0x10 -#define SENSE_BEGINNING_OF_TAPE 0x08 -#define SENSE_WRITE_MODE 0x04 -#define SENSE_WRITE_PROTECT 0x02 -#define SENSE_NOT_CAPABLE 0x01 - -#define SENSE_CHANNEL_ADAPTER_CODE 0xE0 -#define SENSE_CHANNEL_ADAPTER_LOC 0x10 -#define SENSE_REPORTING_CU 0x08 -#define SENSE_AUTOMATIC_LOADER 0x04 -#define SENSE_TAPE_SYNC_MODE 0x02 -#define SENSE_TAPE_POSITIONING 0x01 - -typedef struct _tape34xx_disc_data_t { - __u8 modeset_byte; -} tape34xx_disc_data_t __attribute__ ((packed, aligned(8))); - -/* discipline functions */ -int tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -ccw_req_t * tape34xx_write_block (const char *data, size_t count, tape_info_t * ti); -void tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti); -ccw_req_t * tape34xx_read_block (const char *data, size_t count, tape_info_t * ti); -void tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti); -void tape34xx_clear_read_block (ccw_req_t * cqr, tape_info_t * ti); -ccw_req_t * tape34xx_mtfsf (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtbsf (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtfsr (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtbsr (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtweof (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtrew (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtoffl (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtnop (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtbsfm (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtfsfm (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mteom (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mterase (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtsetdensity (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtseek (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mttell (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtlock (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtunlock (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtload (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtunload (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtcompression (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtsetpart (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtmkpart (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtiocget (tape_info_t * ti, int count); -ccw_req_t * tape34xx_mtiocpos (tape_info_t * ti, int count); -ccw_req_t * tape34xx_bread (struct request *req, tape_info_t* ti,int tapeblock_major); -ccw_req_t * tape34xx_bwrite (struct request *req, tape_info_t* ti,int tapeblock_major); -void tape34xx_free_bread (ccw_req_t*,struct _tape_info_t*); -void tape34xx_free_bwrite (ccw_req_t*,struct _tape_info_t*); - -/* Event handlers */ -void tape34xx_default_handler (tape_info_t * ti); -void tape34xx_unexpect_uchk_handler (tape_info_t * ti); -void tape34xx_unused_done(tape_info_t* ti); -void tape34xx_idle_done(tape_info_t* ti); -void tape34xx_block_done(tape_info_t* ti); -void tape34xx_bsf_init_done(tape_info_t* ti); -void tape34xx_dse_init_done(tape_info_t* ti); -void tape34xx_fsf_init_done(tape_info_t* ti); -void tape34xx_bsb_init_done(tape_info_t* ti); -void tape34xx_fsb_init_done(tape_info_t* ti); -void tape34xx_lbl_init_done(tape_info_t* ti); -void tape34xx_nop_init_done(tape_info_t* ti); -void tape34xx_rfo_init_done(tape_info_t* ti); -void tape34xx_rbi_init_done(tape_info_t* ti); -void tape34xx_rew_init_done(tape_info_t* ti); -void tape34xx_rew_release_init_done(tape_info_t* ti); -void tape34xx_run_init_done(tape_info_t* ti); -void tape34xx_wri_init_done(tape_info_t* ti); -void tape34xx_wtm_init_done(tape_info_t* ti); - -extern void schedule_tapeblock_exec_IO (tape_info_t *ti); - -// the error recovery stuff: -void tape34xx_error_recovery (tape_info_t* ti); -void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id); -void tape34xx_error_recovery_succeded(tape_info_t* ti); -void tape34xx_error_recovery_do_retry(tape_info_t* ti); -void tape34xx_error_recovery_read_opposite (tape_info_t* ti); -void tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno); -#endif // _TAPE34XX_H diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3590.c linux.21rc1-ac2/drivers/s390/char/tape3590.c --- linux.21rc1/drivers/s390/char/tape3590.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3590.c 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -// tbd diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape3590.h linux.21rc1-ac2/drivers/s390/char/tape3590.h --- linux.21rc1/drivers/s390/char/tape3590.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape3590.h 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -// tbd diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_block.c linux.21rc1-ac2/drivers/s390/char/tape_block.c --- linux.21rc1/drivers/s390/char/tape_block.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_block.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,676 @@ +/* + * drivers/s390/char/tape_block.c + * block device frontend for tape device driver + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Tuan Ngo-Anh + * Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tape.h" +#include "tape_std.h" + +#define PRINTK_HEADER "TBLOCK:" + +#define TAPEBLOCK_DEVFSMODE 0060644 /* brwxrw-rw- */ +#define TAPEBLOCK_MAX_SEC 100 +#define TAPEBLOCK_MIN_REQUEUE 3 + +/* + * file operation structure for tape block frontend + */ +static int tapeblock_open(struct inode *, struct file *); +static int tapeblock_release(struct inode *, struct file *); +static int tapeblock_ioctl( + struct inode *, struct file *, unsigned int, unsigned long); + +static struct block_device_operations tapeblock_bdops = { + .owner = THIS_MODULE, + .open = tapeblock_open, + .release = tapeblock_release, + .ioctl = tapeblock_ioctl, +}; + +int tapeblock_major = 0; + +/* + * Some helper inlines + */ +static inline int tapeblock_size(int minor) { + return blk_size[tapeblock_major][minor]; +} +static inline int tapeblock_ssize(int minor) { + return blksize_size[tapeblock_major][minor]; +} +static inline int tapeblock_hw_ssize(int minor) { + return hardsect_size[tapeblock_major][minor]; +} + +/* + * Post finished request. + */ +static inline void +tapeblock_end_request(struct request *req, int uptodate) +{ + if (end_that_request_first(req, uptodate, "tBLK")) + BUG(); + end_that_request_last(req); +} + +static void +__tapeblock_end_request(struct tape_request *ccw_req, void *data) +{ + struct tape_device *device; + struct request *req; + + device = ccw_req->device; + req = (struct request *) data; + if(!device || !req) + BUG(); + + tapeblock_end_request(req, ccw_req->rc == 0); + if (ccw_req->rc == 0) + /* Update position. */ + device->blk_data.block_position = + (req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B; + else + /* We lost the position information due to an error. */ + device->blk_data.block_position = -1; + + device->discipline->free_bread(ccw_req); + + if (!list_empty(&device->req_queue) || + !list_empty(&device->blk_data.request_queue.queue_head)) + tasklet_schedule(&device->blk_data.tasklet); +} + +/* + * Fetch requests from block device queue. + */ +static inline void +__tape_process_blk_queue(struct tape_device *device, struct list_head *new_req) +{ + request_queue_t *queue; + struct list_head *l; + struct request *req; + struct tape_request *ccw_req; + int nr_queued; + + if (!TAPE_BLOCKDEV(device)) { + PRINT_WARN("can't process queue. Not a tape blockdevice.\n"); + return; + } + + nr_queued = 0; + queue = &device->blk_data.request_queue; + + /* Count number of requests on ccw queue. */ + list_for_each(l, &device->req_queue) + nr_queued++; + + while ( + !queue->plugged && + !list_empty(&queue->queue_head) && + nr_queued < TAPEBLOCK_MIN_REQUEUE + ) { + /* tape_block_next_request(queue); */ + req = blkdev_entry_next_request(&queue->queue_head); + + if (req->cmd == WRITE) { + DBF_EVENT(1, "TBLOCK: Rejecting write request\n"); + blkdev_dequeue_request(req); + tapeblock_end_request(req, 0); + continue; + } + ccw_req = device->discipline->bread(device, req); + if (IS_ERR(ccw_req)) { + if (PTR_ERR(ccw_req) == -ENOMEM) + break; /* don't try again */ + DBF_EVENT(1, "TBLOCK: bread failed\n"); + blkdev_dequeue_request(req); + tapeblock_end_request(req, 0); + continue; + } + blkdev_dequeue_request(req); + ccw_req->callback = __tapeblock_end_request; + ccw_req->callback_data = (void *) req; + ccw_req->retries = TAPEBLOCK_RETRIES; + + list_add_tail(&ccw_req->list, new_req); + nr_queued++; + } +} + +/* + * Feed requests to the tape device. + */ +static inline int +tape_queue_requests(struct tape_device *device, struct list_head *new_req) +{ + struct list_head *l, *n; + struct tape_request *ccw_req; + struct request *req; + int rc, fail; + + fail = 0; + list_for_each_safe(l, n, new_req) { + ccw_req = list_entry(l, struct tape_request, list); + list_del(&ccw_req->list); + + rc = tape_do_io_async(device, ccw_req); + if (rc) { + /* + * Start/enqueueing failed. No retries in + * this case. + */ + DBF_EVENT(5, "enqueueing failed\n"); + req = (struct request *) ccw_req->callback_data; + tapeblock_end_request(req, 0); + device->discipline->free_bread(ccw_req); + fail = 1; + } + } + return fail; +} + +/* + * Tape request queue function. Called from ll_rw_blk.c + */ +static void +tapeblock_request_fn(request_queue_t *queue) +{ + struct list_head new_req; + struct tape_device *device; + + device = (struct tape_device *) queue->queuedata; + if(device == NULL) + BUG(); + + while (!list_empty(&queue->queue_head)) { + INIT_LIST_HEAD(&new_req); + spin_lock(get_irq_lock(device->devinfo.irq)); + __tape_process_blk_queue(device, &new_req); + spin_unlock(get_irq_lock(device->devinfo.irq)); + /* + * Now queue the new request to the tape. This needs to be + * done without the device lock held. + */ + if (tape_queue_requests(device, &new_req) == 0) + /* All requests queued. Thats enough for now. */ + break; + } +} + +/* + * Returns block frontend request queue for a tape device. + * FIXME: on shutdown make sure ll_rw_blk can put requests on a dead queue. + */ +static request_queue_t * +tapeblock_get_queue(kdev_t kdev) +{ + struct tape_device *device; + request_queue_t *queue; + + if (major(kdev) != tapeblock_major) + return NULL; + + device = tape_get_device(minor(kdev) >> 1); + if (IS_ERR(device)) + return NULL; + + queue = &device->blk_data.request_queue; + tape_put_device(device); + return queue; +} + +/* + * Acquire the device lock and process queues for the device. + */ +static void +tapeblock_tasklet(unsigned long data) +{ + struct list_head new_req; + struct tape_device *device; + + device = (struct tape_device *) data; + while (!list_empty(&device->blk_data.request_queue.queue_head)) { + INIT_LIST_HEAD(&new_req); + spin_lock_irq(get_irq_lock(device->devinfo.irq)); + __tape_process_blk_queue(device, &new_req); + spin_unlock_irq(get_irq_lock(device->devinfo.irq)); + /* + * Now queue the new request to the tape. This needs to be + * done without the device lock held. + */ + if (tape_queue_requests(device, &new_req) == 0) + /* All requests queued. Thats enough for now. */ + break; + } +} + +/* + * Create block directory with disc entries + */ +static int +tapeblock_mkdevfstree (struct tape_device *device) +{ +#ifdef CONFIG_DEVFS_FS + device->blk_data.devfs_block_dir = + devfs_mk_dir (device->devfs_dir, "block", device); + if (device->blk_data.devfs_block_dir == 0) + return -ENOENT; + device->blk_data.devfs_disc = + devfs_register(device->blk_data.devfs_block_dir, + "disc", DEVFS_FL_DEFAULT, + tapeblock_major, device->first_minor, + TAPEBLOCK_DEVFSMODE, &tapeblock_bdops, device); + if (device->blk_data.devfs_disc == NULL) { + devfs_unregister(device->blk_data.devfs_block_dir); + return -ENOENT; + } +#endif + return 0; +} + +/* + * Remove devfs entries + */ +static void +tapeblock_rmdevfstree (struct tape_device *device) +{ +#ifdef CONFIG_DEVFS_FS + if (device->blk_data.devfs_disc) + devfs_unregister(device->blk_data.devfs_disc); + if (device->blk_data.devfs_block_dir) + devfs_unregister(device->blk_data.devfs_block_dir); +#endif +} + +/* + * This function is called for every new tapedevice + */ +int +tapeblock_setup_device(struct tape_device * device) +{ + int rc; + + /* FIXME: We should be able to sense the sector size */ + blk_size[tapeblock_major][device->first_minor] = 0; + blksize_size[tapeblock_major][device->first_minor] = + hardsect_size[tapeblock_major][device->first_minor] = + TAPEBLOCK_HSEC_SIZE; + + /* Create devfs entries. */ + rc = tapeblock_mkdevfstree(device); + if (rc) + return rc; + + /* Setup request queue and initialize gendisk for this device. */ + device->blk_data.request_queue.queuedata = tape_clone_device(device); + + + /* As long as the tasklet is running it may access the device */ + tasklet_init(&device->blk_data.tasklet, tapeblock_tasklet, + (unsigned long) tape_clone_device(device)); + + blk_init_queue(&device->blk_data.request_queue, tapeblock_request_fn); + blk_queue_headactive(&device->blk_data.request_queue, 0); + + tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_ADD); + + set_device_ro(mk_kdev(tapeblock_major, device->first_minor), 1); + return 0; +} + +void +tapeblock_cleanup_device(struct tape_device *device) +{ + /* Prevent further requests to the block request queue. */ + blk_size[tapeblock_major][device->first_minor] = 0; + + tapeblock_rmdevfstree(device); + + /* With the tasklet gone the reference is gone as well. */ + tasklet_kill(&device->blk_data.tasklet); + tape_put_device(device); + + /* Cleanup the request queue. */ + blk_cleanup_queue(&device->blk_data.request_queue); + + /* Remove reference in private data */ + device->blk_data.request_queue.queuedata = NULL; + tape_put_device(device); + + tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE); +} + +/* + * Detect number of blocks of the tape. + * FIXME: can we extent this to detect the blocks size as well ? + * FIXME: (minor) On 34xx the block id also contains a format specification + * which is unknown before the block was skipped or read at + * least once. So detection is sometimes done a second time. + */ +int tapeblock_mediumdetect(struct tape_device *device) +{ + unsigned int bid; + unsigned int nr_of_blks; + int rc; + + /* + * Identify the first records format + */ + if((rc = tape_mtop(device, MTFSR, 1)) < 0) + return rc; + if((rc = tape_mtop(device, MTBSR, 1)) < 0) + return rc; + + device->blk_data.block_position = 0; + if (tape_std_read_block_id(device, &bid)) { + rc = tape_mtop(device, MTREW, 1); + if (rc) { + device->blk_data.block_position = -1; + blk_size[tapeblock_major][device->first_minor] = 0; + return rc; + } + bid = 0; + } + + if(bid != device->blk_data.start_block_id) { + device->blk_data.start_block_id = bid; + blk_size[tapeblock_major][device->first_minor] = 0; + } + + if(blk_size[tapeblock_major][device->first_minor] > 0) + return 0; + + PRINT_INFO("Detecting media size...\n"); + blk_size[tapeblock_major][device->first_minor] = 0; + + rc = tape_mtop(device, MTFSF, 1); + if (rc) + return rc; + + rc = tape_mtop(device, MTTELL, 1); + if (rc < 0) + return rc; + nr_of_blks = rc - 1; /* don't count FM */ + + if (device->blk_data.start_block_id) { + rc = tape_std_seek_block_id( + device, + device->blk_data.start_block_id); + } else { + rc = tape_mtop(device, MTREW, 1); + } + if (rc) + return rc; + + rc = tape_mtop(device, MTTELL, 1); + if (rc < 0) + return rc; + + /* Don't include start offset */ + nr_of_blks -= rc; + + PRINT_INFO("Found %i blocks on media\n", nr_of_blks); + if (tapeblock_hw_ssize(device->first_minor) > 1024) { + nr_of_blks *= tapeblock_hw_ssize(device->first_minor) / 1024; + } else { + nr_of_blks /= 1024 / tapeblock_hw_ssize(device->first_minor); + } + PRINT_INFO("Tape block device size is %i KB\n", nr_of_blks); + blk_size[tapeblock_major][device->first_minor] = nr_of_blks; + + return 0; +} + +/* + * This function has to be called whenever a new medium has been inserted + * into the drive. + */ +void +tapeblock_medium_change(struct tape_device *device) { + device->blk_data.start_block_id = 0; + blk_size[tapeblock_major][device->first_minor] = 0; +} + +/* + * Block frontend tape device open function. + */ +int +tapeblock_open(struct inode *inode, struct file *filp) { + struct tape_device *device; + int rc; + + if (major(filp->f_dentry->d_inode->i_rdev) != tapeblock_major) + return -ENODEV; + + MOD_INC_USE_COUNT; + device = tape_get_device(minor(filp->f_dentry->d_inode->i_rdev) >> 1); + if (IS_ERR(device)) { + MOD_DEC_USE_COUNT; + return PTR_ERR(device); + } + + DBF_EVENT(6, "TBLOCK: open: %x\n", device->first_minor); + + if(device->required_tapemarks) { + DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); + PRINT_ERR("TBLOCK: Refusing to open tape with missing" + " end of file marks.\n"); + tape_put_device(device); + MOD_DEC_USE_COUNT; + return -EPERM; + } + + rc = tape_open(device); + if (rc == 0) { + rc = tape_assign(device, TAPE_STATUS_ASSIGN_A); + if (rc == 0) { + rc = tapeblock_mediumdetect(device); + if (rc == 0) { + TAPE_SET_STATE(device, TAPE_STATUS_BLOCKDEV); + tape_put_device(device); + return 0; + } + tape_unassign(device, TAPE_STATUS_ASSIGN_A); + } + tape_release(device); + } + tape_put_device(device); + MOD_DEC_USE_COUNT; + return rc; +} + +/* + * Block frontend tape device release function. + */ +int +tapeblock_release(struct inode *inode, struct file *filp) { + struct tape_device *device; + + device = tape_get_device(minor(inode->i_rdev) >> 1); + + DBF_EVENT(4, "TBLOCK: release %i\n", device->first_minor); + + /* Remove all buffers at device close. */ + /* FIXME: can we do that a tape unload ? */ + invalidate_buffers(inode->i_rdev); + + if (device->blk_data.start_block_id) { + tape_std_seek_block_id(device, device->blk_data.start_block_id); + } else { + tape_mtop(device, MTREW, 1); + } + TAPE_CLEAR_STATE(device, TAPE_STATUS_BLOCKDEV); + tape_unassign(device, TAPE_STATUS_ASSIGN_A); + tape_release(device); + tape_put_device(device); + MOD_DEC_USE_COUNT; + + return 0; +} + +int +tapeblock_ioctl( + struct inode *inode, + struct file *file, + unsigned int command, + unsigned long arg +) { + int rc = 0; + int minor = minor(inode->i_rdev); + + DBF_EVENT(6, "tapeblock_ioctl(%x)\n", command); + + switch(command) { + case BLKSSZGET: + if(put_user(tapeblock_ssize(minor), (int *) arg)) + rc = -EFAULT; + break; + case BLKGETSIZE: + if( + put_user( + tapeblock_size(minor), + (unsigned long *) arg + ) + ) + rc = -EFAULT; + break; +#ifdef BLKGETSIZE64 + case BLKGETSIZE64: + if(put_user(tapeblock_size(minor) << 9, (u64 *) arg)) + rc = -EFAULT; + break; +#endif + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + /* No message for these... */ + rc = -EINVAL; + break; + default: + PRINT_WARN("invalid ioctl 0x%x\n", command); + rc = -EINVAL; + } + return rc; +} + +/* + * Initialize block device frontend. + */ +int +tapeblock_init(void) +{ + int rc; + + /* Register the tape major number to the kernel */ +#ifdef CONFIG_DEVFS_FS + if (tapeblock_major == 0) + tapeblock_major = devfs_alloc_major(DEVFS_SPECIAL_BLK); +#endif + rc = register_blkdev(tapeblock_major, "tBLK", &tapeblock_bdops); + if (rc < 0) { + PRINT_ERR("can't get major %d for block device\n", + tapeblock_major); + return rc; + } + if(tapeblock_major == 0) + tapeblock_major = rc; + + /* Allocate memory for kernel block device tables */ + rc = -ENOMEM; + blk_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL); + if(blk_size[tapeblock_major] == NULL) + goto tapeblock_init_fail; + memset(blk_size[tapeblock_major], 0, 256*sizeof(int)); + blksize_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL); + if(blksize_size[tapeblock_major] == NULL) + goto tapeblock_init_fail; + memset(blksize_size[tapeblock_major], 0, 256*sizeof(int)); + hardsect_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL); + if(hardsect_size[tapeblock_major] == NULL) + goto tapeblock_init_fail; + memset(hardsect_size[tapeblock_major], 0, 256*sizeof(int)); + max_sectors[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL); + if(max_sectors[tapeblock_major] == NULL) + goto tapeblock_init_fail; + memset(max_sectors[tapeblock_major], 0, 256*sizeof(int)); + + blk_dev[tapeblock_major].queue = tapeblock_get_queue; + + PRINT_INFO("tape gets major %d for block device\n", tapeblock_major); + DBF_EVENT(3, "TBLOCK: major = %d\n", tapeblock_major); + DBF_EVENT(3, "TBLOCK: init ok\n"); + + return 0; + +tapeblock_init_fail: + if(tapeblock_major > 0) { + if(blk_size[tapeblock_major]) { + kfree(blk_size[tapeblock_major]); + blk_size[tapeblock_major] = NULL; + } + if(blksize_size[tapeblock_major]) { + kfree(blksize_size[tapeblock_major]); + blksize_size[tapeblock_major] = NULL; + } + if(hardsect_size[tapeblock_major]) { + kfree(hardsect_size[tapeblock_major]); + hardsect_size[tapeblock_major] = NULL; + } + if(max_sectors[tapeblock_major]) { + kfree(max_sectors[tapeblock_major]); + max_sectors[tapeblock_major] = NULL; + } +#ifdef CONFIG_DEVFS_FS + devfs_unregister_blkdev(tapeblock_major, "tBLK"); +#else + unregister_blkdev(tapeblock_major, "tBLK"); +#endif + tapeblock_major = -1; + } + + DBF_EVENT(3, "TBLOCK: init failed(%d)\n", rc); + return rc; +} + +/* + * Deregister major for block device frontend + */ +void +tapeblock_exit(void) +{ + if(blk_size[tapeblock_major]) { + kfree(blk_size[tapeblock_major]); + blk_size[tapeblock_major] = NULL; + } + if(blksize_size[tapeblock_major]) { + kfree(blksize_size[tapeblock_major]); + blksize_size[tapeblock_major] = NULL; + } + if(hardsect_size[tapeblock_major]) { + kfree(hardsect_size[tapeblock_major]); + hardsect_size[tapeblock_major] = NULL; + } + if(max_sectors[tapeblock_major]) { + kfree(max_sectors[tapeblock_major]); + max_sectors[tapeblock_major] = NULL; + } + blk_dev[tapeblock_major].queue = NULL; + unregister_blkdev(tapeblock_major, "tBLK"); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tapeblock.c linux.21rc1-ac2/drivers/s390/char/tapeblock.c --- linux.21rc1/drivers/s390/char/tapeblock.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tapeblock.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,593 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tapeblock.c - * block device frontend for tape device driver - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - * - **************************************************************************** - */ - -#include "tapedefs.h" -#include -#include -#include -#include -#include -#include /* CCW allocations */ -#include -#include -#include -#ifdef MODULE -#define __NO_VERSION__ -#include -#endif -#include "tape.h" -#include "tapeblock.h" - -#define PRINTK_HEADER "TBLOCK:" - -/* - * file operation structure for tape devices - */ -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) -static struct block_device_operations tapeblock_fops = { -#else -static struct file_operations tapeblock_fops = { -#endif - owner : THIS_MODULE, - open : tapeblock_open, /* open */ - release : tapeblock_release, /* release */ - }; - -int tapeblock_major = 0; - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) -static void tape_request_fn (request_queue_t * queue); -#else -static void tape_request_fn (void); -#endif - -static request_queue_t* tapeblock_getqueue (kdev_t kdev); - -#ifdef CONFIG_DEVFS_FS -void -tapeblock_mkdevfstree (tape_info_t* ti) { - ti->devfs_block_dir=devfs_mk_dir (ti->devfs_dir, "block", ti); - ti->devfs_disc=devfs_register(ti->devfs_block_dir, "disc",DEVFS_FL_DEFAULT, - tapeblock_major, ti->blk_minor, - TAPEBLOCK_DEFAULTMODE, &tapeblock_fops, ti); -} - -void -tapeblock_rmdevfstree (tape_info_t* ti) { - devfs_unregister(ti->devfs_disc); - devfs_unregister(ti->devfs_block_dir); -} -#endif - -void -tapeblock_setup(tape_info_t* ti) { - blk_size[tapeblock_major][ti->blk_minor]=0; // this will be detected - blksize_size[tapeblock_major][ti->blk_minor]=2048; // blocks are 2k by default. - hardsect_size[tapeblock_major][ti->blk_minor]=512; - blk_init_queue (&ti->request_queue, tape_request_fn); - blk_queue_headactive (&ti->request_queue, 0); -#ifdef CONFIG_DEVFS_FS - tapeblock_mkdevfstree(ti); -#endif -} - -int -tapeblock_init(void) { - int result; - tape_frontend_t* blkfront,*temp; - tape_info_t* ti; - - tape_init(); - /* Register the tape major number to the kernel */ -#ifdef CONFIG_DEVFS_FS - result = devfs_register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops); -#else - result = register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops); -#endif - if (result < 0) { - PRINT_WARN(KERN_ERR "tape: can't get major %d for block device\n", tapeblock_major); - panic ("cannot get major number for tape block device"); - } - if (tapeblock_major == 0) tapeblock_major = result; /* accept dynamic major number*/ - INIT_BLK_DEV(tapeblock_major,tape_request_fn,tapeblock_getqueue,NULL); - read_ahead[tapeblock_major]=TAPEBLOCK_READAHEAD; - PRINT_WARN(KERN_ERR " tape gets major %d for block device\n", result); - blk_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC); - memset(blk_size[tapeblock_major],0,256*sizeof(int)); - blksize_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC); - memset(blksize_size[tapeblock_major],0,256*sizeof(int)); - hardsect_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC); - memset(hardsect_size[tapeblock_major],0,256*sizeof(int)); - max_sectors[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC); - memset(max_sectors[tapeblock_major],0,256*sizeof(int)); - blkfront = kmalloc(sizeof(tape_frontend_t),GFP_KERNEL); - if (blkfront==NULL) panic ("no mem for tape block device structure"); - blkfront->device_setup=tapeblock_setup; -#ifdef CONFIG_DEVFS_FS - blkfront->mkdevfstree = tapeblock_mkdevfstree; - blkfront->rmdevfstree = tapeblock_rmdevfstree; -#endif - blkfront->next=NULL; - if (first_frontend==NULL) { - first_frontend=blkfront; - } else { - temp=first_frontend; - while (temp->next!=NULL) - temp=temp->next; - temp->next=blkfront; - } - ti=first_tape_info; - while (ti!=NULL) { - tapeblock_setup(ti); - ti=ti->next; - } - return 0; -} - - -void -tapeblock_uninit(void) { - unregister_blkdev(tapeblock_major, "tBLK"); -} - -int -tapeblock_open(struct inode *inode, struct file *filp) { - tape_info_t *ti; - kdev_t dev; - int rc; - long lockflags; - - inode = filp->f_dentry->d_inode; - ti = first_tape_info; - while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev))) - ti = (tape_info_t *) ti->next; - if (ti == NULL) - return -ENODEV; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:open:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) != TS_UNUSED) { - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:dbusy"); -#endif - return -EBUSY; - } - tapestate_set (ti, TS_IDLE); - ti->position=-1; - - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - rc=tapeblock_mediumdetect(ti); - if (rc) { - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_UNUSED); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return rc; // in case of errors, we don't have a size of the medium - } - dev = MKDEV (tapeblock_major, MINOR (inode->i_rdev)); /* Get the device */ - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->blk_filp = filp; - filp->private_data = ti; /* save the dev.info for later reference */ - ti->cqr=NULL; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - - return 0; -} - -int -tapeblock_release(struct inode *inode, struct file *filp) { - long lockflags; - tape_info_t *ti,*lastti; - ti = first_tape_info; - while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev))) - ti = (tape_info_t *) ti->next; - if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) { - if (ti==first_tape_info) { - first_tape_info=ti->next; - } else { - lastti=first_tape_info; - while (lastti->next!=ti) lastti=lastti->next; - lastti->next=ti->next; - } - kfree(ti); - return 0; - } - if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:notidle!"); -#endif - return -ENXIO; /* error in tape_release */ - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:release:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_UNUSED); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - invalidate_buffers(inode->i_rdev); - return 0; -} - -static void -tapeblock_end_request(tape_info_t* ti) { - struct buffer_head *bh; - int uptodate; - if ((tapestate_get(ti)!=TS_FAILED) && - (tapestate_get(ti)!=TS_DONE)) - BUG(); // A request has to be completed to end it - uptodate=(tapestate_get(ti)==TS_DONE); // is the buffer up to date? -#ifdef TAPE_DEBUG - if (uptodate) { - debug_text_event (tape_debug_area,6,"b:done:"); - debug_int_event (tape_debug_area,6,(long)ti->cqr); - } else { - debug_text_event (tape_debug_area,3,"b:failed:"); - debug_int_event (tape_debug_area,3,(long)ti->cqr); - } -#endif - // now inform ll_rw_block about a request status - while ((bh = ti->current_request->bh) != NULL) { - ti->current_request->bh = bh->b_reqnext; - bh->b_reqnext = NULL; - bh->b_end_io (bh, uptodate); - } - if (!end_that_request_first (ti->current_request, uptodate, "tBLK")) { -#ifndef DEVICE_NO_RANDOM - add_blkdev_randomness (MAJOR (ti->current_request->rq_dev)); -#endif - end_that_request_last (ti->current_request); - } - ti->discipline->free_bread(ti->cqr,ti); - ti->cqr=NULL; - ti->current_request=NULL; - if (tapestate_get(ti)!=TS_NOT_OPER) tapestate_set(ti,TS_IDLE); - return; -} - -static void -tapeblock_exec_IO (tape_info_t* ti) { - int rc; - struct request* req; - if (ti->cqr) { // process done/failed request - while ((tapestate_get(ti)==TS_FAILED) && - ti->blk_retries>0) { - ti->blk_retries--; - ti->position=-1; - tapestate_set(ti,TS_BLOCK_INIT); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:retryreq:"); - debug_int_event (tape_debug_area,3,(long)ti->cqr); -#endif - rc = do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, - 0x00, ti->cqr->options); - if (rc) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:doIOfail:"); - debug_int_event (tape_debug_area,3,(long)ti->cqr); -#endif - continue; // one retry lost 'cause doIO failed - } - return; - } - tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl - } - if (ti->cqr!=NULL) BUG(); // tape should be idle now, request should be freed! - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - return; - } -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) - if (list_empty (&ti->request_queue.queue_head)) { -#else - if (ti->request_queue==NULL) { -#endif - // nothing more to do or device has dissapeared;) -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:Qempty"); -#endif - tapestate_set(ti,TS_IDLE); - return; - } - // queue is not empty, fetch a request and start IO! - req=ti->current_request=tape_next_request(&ti->request_queue); - if (req==NULL) { - BUG(); // Yo. The queue was not reported empy, but no request found. This is _bad_. - } - if (req->cmd!=READ) { // we only support reading - tapestate_set(ti,TS_FAILED); - tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl - tapestate_set(ti,TS_BLOCK_INIT); - schedule_tapeblock_exec_IO(ti); - return; - } - ti->cqr=ti->discipline->bread(req,ti,tapeblock_major); //build channel program from request - if (!ti->cqr) { - // ccw generation failed. we try again later. -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:cqrNULL"); -#endif - schedule_tapeblock_exec_IO(ti); - ti->current_request=NULL; - return; - } - ti->blk_retries = TAPEBLOCK_RETRIES; - rc= do_IO (ti->devinfo.irq, ti->cqr->cpaddr, - (unsigned long) ti->cqr, 0x00, ti->cqr->options); - if (rc) { - // okay. ssch failed. we try later. -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:doIOfail"); -#endif - ti->discipline->free_bread(ti->cqr,ti); - ti->cqr=NULL; - ti->current_request=NULL; - schedule_tapeblock_exec_IO(ti); - return; - } - // our request is in IO. we remove it from the queue and exit - tape_dequeue_request (&ti->request_queue,req); -} - -static void -do_tape_request (request_queue_t * queue) { - tape_info_t* ti; - long lockflags; - for (ti=first_tape_info; - ((ti!=NULL) && ((&ti->request_queue)!=queue)); - ti=ti->next); - if (ti==NULL) BUG(); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get(ti)!=TS_IDLE) { - s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags); - return; - } - if (tapestate_get(ti)!=TS_IDLE) BUG(); - tapestate_set(ti,TS_BLOCK_INIT); - tapeblock_exec_IO(ti); - s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags); -} - -static void -run_tapeblock_exec_IO (tape_info_t* ti) { - long flags_390irq,flags_ior; - spin_lock_irqsave (&io_request_lock, flags_ior); - s390irq_spin_lock_irqsave(ti->devinfo.irq,flags_390irq); - atomic_set(&ti->bh_scheduled,0); - tapeblock_exec_IO(ti); - s390irq_spin_unlock_irqrestore(ti->devinfo.irq,flags_390irq); - spin_unlock_irqrestore (&io_request_lock, flags_ior); -} - -void -schedule_tapeblock_exec_IO (tape_info_t *ti) -{ - /* Protect against rescheduling, when already running */ - if (atomic_compare_and_swap(0,1,&ti->bh_scheduled)) { - return; - } -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) - INIT_LIST_HEAD(&ti->bh_tq.list); -#endif - ti->bh_tq.sync = 0; - ti->bh_tq.routine = (void *) (void *) run_tapeblock_exec_IO; - ti->bh_tq.data = ti; - - queue_task (&ti->bh_tq, &tq_immediate); - mark_bh (IMMEDIATE_BH); - return; -} - -/* wrappers around do_tape_request for different kernel versions */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) -static void tape_request_fn (void) { - tape_info_t* ti=first_tape_info; - while (ti!=NULL) { - do_tape_request(&ti->request_queue); - ti=ti->next; - } -} -#else -static void tape_request_fn (request_queue_t* queue) { - do_tape_request(queue); -} -#endif - -static request_queue_t* tapeblock_getqueue (kdev_t kdev) { - tape_info_t* ti=first_tape_info; - while ((ti!=NULL) && (MINOR(kdev)!=ti->blk_minor)) - ti=ti->next; - if (ti!=NULL) return &ti->request_queue; - return NULL; -} - -int tapeblock_mediumdetect(tape_info_t* ti) { - ccw_req_t* cqr; - int losize=1,hisize=1,rc; - long lockflags; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"b:medDet"); -#endif - PRINT_WARN("Detecting media size. This will take _long_, so get yourself a coffee...\n"); - while (1) { //is interruped by break - hisize=hisize << 1; // try twice the size tested before - cqr=ti->discipline->mtseek (ti, hisize); - if (cqr == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:ccwg fail"); -#endif - return -ENOSPC; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if (rc) return -EIO; - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - tape_free_request (cqr); - if (ti->kernbuf) { - kfree (ti->kernbuf); - ti->kernbuf=NULL; - } - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - break; - } - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - losize=hisize; - } - cqr = ti->discipline->mtrew (ti, 1); - if (cqr == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:ccwg fail"); -#endif - return -ENOSPC; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - tape_free_request (cqr); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - while (losize!=hisize) { - cqr=ti->discipline->mtseek (ti, (hisize+losize)/2+1); - if (cqr == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:ccwg fail"); -#endif - return -ENOSPC; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if (rc) return -EIO; - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - tape_free_request (cqr); - if (ti->kernbuf) { - kfree (ti->kernbuf); - ti->kernbuf=NULL; - } - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - hisize=(hisize+losize)/2; - cqr = ti->discipline->mtrew (ti, 1); - if (cqr == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"b:ccwg fail"); -#endif - return -ENOSPC; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - tape_free_request (cqr); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - continue; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - losize=(hisize+losize)/2+1; - } - blk_size[tapeblock_major][ti->blk_minor]=(losize)*(blksize_size[tapeblock_major][ti->blk_minor]/1024); - return 0; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tapeblock.h linux.21rc1-ac2/drivers/s390/char/tapeblock.h --- linux.21rc1/drivers/s390/char/tapeblock.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tapeblock.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,36 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tapechar.h - * character device frontend for tape device driver - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - * - **************************************************************************** - */ - -#ifndef TAPEBLOCK_H -#define TAPEBLOCK_H -#include -#define PARTN_BITS 0 - -#define TAPEBLOCK_READAHEAD 30 -#define TAPEBLOCK_MAJOR 0 - -#define TAPEBLOCK_DEFAULTMODE 0060644 - -int tapeblock_open(struct inode *, struct file *); -int tapeblock_release(struct inode *, struct file *); -void tapeblock_setup(tape_info_t* ti); -void schedule_tapeblock_exec_IO (tape_info_t *ti); -int tapeblock_mediumdetect(tape_info_t* ti); -#ifdef CONFIG_DEVFS_FS -void tapeblock_mkdevfstree (tape_info_t* ti); -#endif -int tapeblock_init (void); -void tapeblock_uninit (void); -#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape.c linux.21rc1-ac2/drivers/s390/char/tape.c --- linux.21rc1/drivers/s390/char/tape.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,1120 +0,0 @@ - -/*********************************************************************** - * drivers/s390/char/tape.c - * tape device driver for S/390 and zSeries tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - *********************************************************************** - */ - -#include "tapedefs.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef MODULE -#include -#endif -#include -#ifdef CONFIG_S390_TAPE_DYNAMIC -#include -#endif -#include "tape.h" -#ifdef CONFIG_S390_TAPE_3490 -#include "tape3490.h" -#endif -#ifdef CONFIG_S390_TAPE_3480 -#include "tape3480.h" -#endif -#ifdef CONFIG_S390_TAPE_BLOCK -#include "tapeblock.h" -#endif -#ifdef CONFIG_S390_TAPE_CHAR -#include "tapechar.h" -#endif -#ifdef CONFIG_PROC_FS -#include -#endif -#define PRINTK_HEADER "T390:" - - -/* state handling routines */ -inline void tapestate_set (tape_info_t * ti, int newstate); -inline int tapestate_get (tape_info_t * ti); -void tapestate_event (tape_info_t * ti, int event); - -/* our globals */ -tape_info_t *first_tape_info = NULL; -tape_discipline_t *first_discipline = NULL; -tape_frontend_t *first_frontend = NULL; -devreg_t* tape_devreg[128]; -int devregct=0; - -#ifdef TAPE_DEBUG -debug_info_t *tape_debug_area = NULL; -#endif - -char* state_verbose[TS_SIZE]={ - "TS_UNUSED", "TS_IDLE", "TS_DONE", "TS_FAILED", - "TS_BLOCK_INIT", - "TS_BSB_INIT", - "TS_BSF_INIT", - "TS_DSE_INIT", - "TS_EGA_INIT", - "TS_FSB_INIT", - "TS_FSF_INIT", - "TS_LDI_INIT", - "TS_LBL_INIT", - "TS_MSE_INIT", - "TS_NOP_INIT", - "TS_RBA_INIT", - "TS_RBI_INIT", - "TS_RBU_INIT", - "TS_RBL_INIT", - "TS_RDC_INIT", - "TS_RFO_INIT", - "TS_RSD_INIT", - "TS_REW_INIT", - "TS_REW_RELEASE_INIT", - "TS_RUN_INIT", - "TS_SEN_INIT", - "TS_SID_INIT", - "TS_SNP_INIT", - "TS_SPG_INIT", - "TS_SWI_INIT", - "TS_SMR_INIT", - "TS_SYN_INIT", - "TS_TIO_INIT", - "TS_UNA_INIT", - "TS_WRI_INIT", - "TS_WTM_INIT", - "TS_NOT_OPER"}; - -char* event_verbose[TE_SIZE]= { - "TE_START", "TE_DONE", "TE_FAILED", "TE_ERROR", "TE_OTHER"}; - -/* our root devfs handle */ -#ifdef CONFIG_DEVFS_FS -devfs_handle_t tape_devfs_root_entry; - -inline void -tape_mkdevfsroots (tape_info_t* ti) -{ - char devno [5]; - sprintf (devno,"%04x",ti->devinfo.devno); - ti->devfs_dir=devfs_mk_dir (tape_devfs_root_entry, devno, ti); -} - -inline void -tape_rmdevfsroots (tape_info_t* ti) -{ - devfs_unregister (ti->devfs_dir); -} -#endif - -#ifdef CONFIG_PROC_FS -/* our proc tapedevices entry */ -static struct proc_dir_entry *tape_devices_entry; - -typedef struct { - char *data; - int len; -} tempinfo_t; - - -static int -tape_devices_open (struct inode *inode, struct file *file) -{ - int size=80; - tape_info_t* ti; - tempinfo_t* tempinfo; - char* data; - int pos=0; - tempinfo = kmalloc (sizeof(tempinfo_t),GFP_KERNEL); - if (!tempinfo) - return -ENOMEM; - for (ti=first_tape_info;ti!=NULL;ti=ti->next) - size+=80; // FIXME: Guess better! - data=vmalloc(size); - if (!data) { - kfree (tempinfo); - return -ENOMEM; - } - pos+=sprintf(data+pos,"TapeNo\tDevNo\tCuType\tCuModel\tDevType\tDevModel\tState\n"); - for (ti=first_tape_info;ti!=NULL;ti=ti->next) { - pos+=sprintf(data+pos,"%d\t%04X\t%04X\t%02X\t%04X\t%02X\t\t%s\n",ti->rew_minor/2, - ti->devinfo.devno,ti->devinfo.sid_data.cu_type, - ti->devinfo.sid_data.cu_model,ti->devinfo.sid_data.dev_type, - ti->devinfo.sid_data.dev_model,((tapestate_get(ti) >= 0) && - (tapestate_get(ti) < TS_SIZE)) ? - state_verbose[tapestate_get (ti)] : "TS UNKNOWN"); - } - tempinfo->len=pos; - tempinfo->data=data; - file->private_data= (void*) tempinfo; -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif - return 0; -} - -static ssize_t -tape_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset) -{ - loff_t len; - tempinfo_t *p_info = (tempinfo_t *) file->private_data; - - if (*offset >= p_info->len) { - return 0; /* EOF */ - } else { - len = user_len<(p_info->len - *offset)?user_len:(p_info->len - *offset); - if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) - return -EFAULT; - (*offset) += len; - return len; /* number of bytes "read" */ - } -} - -static int -tape_devices_release (struct inode *inode, struct file *file) -{ - int rc = 0; - tempinfo_t *p_info = (tempinfo_t *) file->private_data; - if (p_info) { - if (p_info->data) - vfree (p_info->data); - kfree (p_info); - } -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return rc; -} - -static struct file_operations tape_devices_file_ops = -{ - read:tape_devices_read, /* read */ - open:tape_devices_open, /* open */ - release:tape_devices_release, /* close */ -}; - -static struct inode_operations tape_devices_inode_ops = -{ -#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) - default_file_ops:&tape_devices_file_ops /* file ops */ -#endif /* LINUX_IS_24 */ -}; -#endif /* CONFIG_PROC_FS */ - -/* SECTION: Parameters for tape */ -char *tape[256] = { NULL, }; - -#ifndef MODULE -static char tape_parm_string[1024] __initdata = { 0, }; -static void -tape_split_parm_string (char *str) -{ - char *tmp = str; - int count = 0; - while (tmp != NULL && *tmp != '\0') { - char *end; - int len; - end = strchr (tmp, ','); - if (end == NULL) { - len = strlen (tmp) + 1; - } else { - len = (long) end - (long) tmp + 1; - *end = '\0'; - end++; - } - tape[count] = kmalloc (len * sizeof (char), GFP_ATOMIC); - if (tape[count] == NULL) { - printk (KERN_WARNING PRINTK_HEADER - "can't store tape= parameter no %d\n", - count + 1); - break; - } - memset (tape[count], 0, len * sizeof (char)); - memcpy (tape[count], tmp, len * sizeof (char)); - count++; - tmp = end; - }; -} - -void __init -tape_parm_setup (char *str, int *ints) -{ - int len = strlen (tape_parm_string); - if (len != 0) { - strcat (tape_parm_string, ","); - } - strcat (tape_parm_string, str); -} - -int __init -tape_parm_call_setup (char *str) -{ - int dummy; - tape_parm_setup (str, &dummy); - return 1; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,16)) -__setup("tape=", tape_parm_call_setup); -#endif /* kernel <2.2.19 */ -#endif /* not defined MODULE */ - -static inline int -tape_parm_strtoul (char *str, char **stra) -{ - char *temp = str; - int val; - if (*temp == '0') { - temp++; /* strip leading zero */ - if (*temp == 'x') - temp++; /* strip leading x */ - } - val = simple_strtoul (temp, &temp, 16); /* interpret anything as hex */ - *stra = temp; - return val; -} - -static inline devreg_t * -tape_create_devreg (int devno) -{ - devreg_t *devreg = kmalloc (sizeof (devreg_t), GFP_KERNEL); - if (devreg != NULL) { - memset (devreg, 0, sizeof (devreg_t)); - devreg->ci.devno = devno; - devreg->flag = DEVREG_TYPE_DEVNO; - devreg->oper_func = tape_oper_handler; - } - return devreg; -} - -static inline void -tape_parm_parse (char **str) -{ - char *temp; - int from, to,i,irq=0,rc,retries=0,tape_num=0; - s390_dev_info_t dinfo; - tape_info_t* ti,*tempti; - tape_discipline_t* disc; - long lockflags; - if (*str==NULL) { - /* no params present -> leave */ - return; - } - while (*str) { - temp = *str; - from = 0; - to = 0; - - /* turn off autodetect mode, if any range is present */ - from = tape_parm_strtoul (temp, &temp); - to = from; - if (*temp == '-') { - temp++; - to = tape_parm_strtoul (temp, &temp); - } - for (i=from;i<=to;i++) { - retries=0; - // register for attch/detach of a devno - tape_devreg[devregct]=tape_create_devreg(i); - if (tape_devreg[devregct]==NULL) { - PRINT_WARN ("Could not create devreg for devno %04x, dyn. attach for this devno deactivated.\n",i); - } else { - s390_device_register (tape_devreg[devregct++]); - } - // we are activating a device if it is present - for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) { - rc = get_dev_info_by_irq (irq, &dinfo); - - disc = first_discipline; - while ((dinfo.devno == i) && (disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type)) - disc = (tape_discipline_t *) (disc->next); - if ((disc == NULL) || (rc == -ENODEV) || (i!=dinfo.devno)) { - continue; - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"det irq: "); - debug_int_event (tape_debug_area,3,irq); - debug_text_event (tape_debug_area,3,"cu: "); - debug_int_event (tape_debug_area,3,disc->cu_type); -#endif /* TAPE_DEBUG */ - PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2); - /* Allocate tape structure */ - ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC); - if (ti == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"ti:no mem "); -#endif /* TAPE_DEBUG */ - PRINT_INFO ("tape: can't allocate memory for " - "tape info structure\n"); - continue; - } - memset(ti,0,sizeof(tape_info_t)); - ti->discipline = disc; - disc->tape = ti; - rc = tape_setup (ti, irq, tape_num); - if (rc) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"tsetup err"); - debug_int_exception (tape_debug_area,3,rc); -#endif /* TAPE_DEBUG */ - kfree (ti); - } else { - s390irq_spin_lock_irqsave (irq, lockflags); - if (first_tape_info == NULL) { - first_tape_info = ti; - } else { - tempti = first_tape_info; - while (tempti->next != NULL) - tempti = tempti->next; - tempti->next = ti; - } - s390irq_spin_unlock_irqrestore (irq, lockflags); - } - } - tape_num+=2; - } - str++; - } -} - - -/* SECTION: Managing wrappers for ccwcache */ - -#define TAPE_EMERGENCY_REQUESTS 16 - -static ccw_req_t *tape_emergency_req[TAPE_EMERGENCY_REQUESTS] = -{NULL,}; -static spinlock_t tape_emergency_req_lock = SPIN_LOCK_UNLOCKED; - -static void -tape_init_emergency_req (void) -{ - int i; - for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) { - tape_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL); - } -} - -#ifdef MODULE // We only cleanup the emergency requests on module unload. -static void -tape_cleanup_emergency_req (void) -{ - int i; - for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) { - if (tape_emergency_req[i]) - free_page ((long) (tape_emergency_req[i])); - else - printk (KERN_WARNING PRINTK_HEADER "losing one page for 'in-use' emergency request\n"); - } -} -#endif - -ccw_req_t * -tape_alloc_request (char *magic, int cplength, int datasize) -{ - ccw_req_t *rv = NULL; - int i; - if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) { - return rv; - } - if (cplength * sizeof (ccw1_t) + datasize + sizeof (ccw_req_t) > PAGE_SIZE) { - return NULL; - } - spin_lock (&tape_emergency_req_lock); - for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) { - if (tape_emergency_req[i] != NULL) { - rv = tape_emergency_req[i]; - tape_emergency_req[i] = NULL; - } - } - spin_unlock (&tape_emergency_req_lock); - if (rv) { - memset (rv, 0, PAGE_SIZE); - rv->cache = (kmem_cache_t *) (tape_emergency_req + i); - strncpy ((char *) (&rv->magic), magic, 4); - ASCEBC ((char *) (&rv->magic), 4); - rv->cplength = cplength; - rv->datasize = datasize; - rv->data = (void *) ((long) rv + PAGE_SIZE - datasize); - rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t)); - } - return rv; -} - -void -tape_free_request (ccw_req_t * request) -{ - if (request->cache >= (kmem_cache_t *) tape_emergency_req && - request->cache <= (kmem_cache_t *) (tape_emergency_req + TAPE_EMERGENCY_REQUESTS)) { - *((ccw_req_t **) (request->cache)) = request; - } else { - clear_normalized_cda ((ccw1_t *) (request->cpaddr)); // avoid memory leak caused by modeset_byte - ccw_free_request (request); - } -} - -/* - * Allocate a ccw request and reserve it for tape driver - */ -inline - ccw_req_t * -tape_alloc_ccw_req (tape_info_t * ti, int cplength, int datasize) -{ - char tape_magic_id[] = "tape"; - ccw_req_t *cqr = NULL; - - if (!ti) - return NULL; - cqr = tape_alloc_request (tape_magic_id, cplength, datasize); - - if (!cqr) { -#ifdef TAPE_DEBUG - PRINT_WARN ("empty CQR generated\n"); -#endif - } - cqr->magic = TAPE_MAGIC; /* sets an identifier for tape driver */ - cqr->device = ti; /* save pointer to tape info */ - return cqr; -} - -/* - * Find the tape_info_t structure associated with irq - */ -static inline tape_info_t * -tapedev_find_info (int irq) -{ - tape_info_t *ti; - - ti = first_tape_info; - if (ti != NULL) - do { - if (ti->devinfo.irq == irq) - break; - } while ((ti = (tape_info_t *) ti->next) != NULL); - return ti; -} - -#define QUEUE_THRESHOLD 5 - -/* - * Tape interrupt routine, called from Ingo's I/O layer - */ -void -tape_irq (int irq, void *int_parm, struct pt_regs *regs) -{ - tape_info_t *ti = tapedev_find_info (irq); - - /* analyse devstat and fire event */ - if (ti->devstat.dstat & DEV_STAT_UNIT_CHECK) { - tapestate_event (ti, TE_ERROR); - } else if (ti->devstat.dstat & (DEV_STAT_DEV_END)) { - tapestate_event (ti, TE_DONE); - } else - tapestate_event (ti, TE_OTHER); -} - -int -tape_oper_handler ( int irq, struct _devreg *dreg) { - tape_info_t* ti=first_tape_info; - tape_info_t* newtape; - int rc,tape_num,retries=0,i; - s390_dev_info_t dinfo; - tape_discipline_t* disc; -#ifdef CONFIG_DEVFS_FS - tape_frontend_t* frontend; -#endif - long lockflags; - while ((ti!=NULL) && (ti->devinfo.irq!=irq)) - ti=ti->next; - if (ti!=NULL) { - // irq is (still) used by tape. tell ingo to try again later - PRINT_WARN ("Oper handler for irq %d called while irq still (internaly?) used.\n",irq); - return -EAGAIN; - } - // irq is not used by tape - rc = get_dev_info_by_irq (irq, &dinfo); - if (rc == -ENODEV) { - retries++; - rc = get_dev_info_by_irq (irq, &dinfo); - if (retries > 5) { - PRINT_WARN ("No device information for new dev. could be retrieved.\n"); - return -ENODEV; - } - } - disc = first_discipline; - while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type)) - disc = (tape_discipline_t *) (disc->next); - if (disc == NULL) - PRINT_WARN ("No matching discipline for cu_type %x found, ignoring device %04x.\n",dinfo.sid_data.cu_type,dinfo.devno); - if (rc == -ENODEV) - PRINT_WARN ("No device information for new dev. could be retrieved.\n"); - if ((disc == NULL) || (rc == -ENODEV)) - return -ENODEV; - - /* Allocate tape structure */ - ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC); - if (ti == NULL) { - PRINT_INFO ( "tape: can't allocate memory for " - "tape info structure\n"); - return -ENOBUFS; - } - memset(ti,0,sizeof(tape_info_t)); - ti->discipline = disc; - disc->tape = ti; - tape_num=0; - if (*tape) { - // we have static device ranges, so fingure out the tape_num of the attached tape - for (i=0;ici.devno==dinfo.devno) { - tape_num=2*i; - break; - } - } else { - // we are running in autoprobe mode, find a free tape_num - newtape=first_tape_info; - while (newtape!=NULL) { - if (newtape->rew_minor==tape_num) { - // tape num in use. try next one - tape_num+=2; - newtape=first_tape_info; - } else { - // tape num not used by newtape. look at next tape info - newtape=newtape->next; - } - } - } - rc = tape_setup (ti, irq, tape_num); - if (rc) { - kfree (ti); - return -ENOBUFS; - } -#ifdef CONFIG_DEVFS_FS - for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next) - frontend->mkdevfstree(ti); -#endif - s390irq_spin_lock_irqsave (irq,lockflags); - if (first_tape_info == NULL) { - first_tape_info = ti; - } else { - newtape = first_tape_info; - while (newtape->next != NULL) - newtape = newtape->next; - newtape->next = ti; - } - s390irq_spin_unlock_irqrestore (irq, lockflags); - return 0; -} - - -static void -tape_noper_handler ( int irq, int status ) { - tape_info_t *ti=first_tape_info; - tape_info_t *lastti; -#ifdef CONFIG_DEVFS_FS - tape_frontend_t *frontend; -#endif - long lockflags; - s390irq_spin_lock_irqsave(irq,lockflags); - while (ti!=NULL && ti->devinfo.irq!=irq) ti=ti->next; - if (ti==NULL) return; - if (tapestate_get(ti)!=TS_UNUSED) { - // device is in use! - PRINT_WARN ("Tape #%d was detached while it was busy. Expect errors!",ti->blk_minor/2); - tapestate_set(ti,TS_NOT_OPER); - ti->rc=-ENODEV; - ti->wanna_wakeup=1; - switch (tapestate_get(ti)) { - case TS_REW_RELEASE_INIT: - tapestate_set(ti,TS_NOT_OPER); - wake_up (&ti->wq); - break; -#ifdef CONFIG_S390_TAPE_BLOCK - case TS_BLOCK_INIT: - tapestate_set(ti,TS_NOT_OPER); - schedule_tapeblock_exec_IO(ti); - break; -#endif - default: - tapestate_set(ti,TS_NOT_OPER); - wake_up_interruptible (&ti->wq); - } - } else { - // device is unused! - PRINT_WARN ("Tape #%d was detached.\n",ti->blk_minor/2); - if (ti==first_tape_info) { - first_tape_info=ti->next; - } else { - lastti=first_tape_info; - while (lastti->next!=ti) lastti=lastti->next; - lastti->next=ti->next; - } -#ifdef CONFIG_DEVFS_FS - for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next) - frontend->rmdevfstree(ti); - tape_rmdevfsroots(ti); -#endif - kfree(ti); - } - s390irq_spin_unlock_irqrestore(irq,lockflags); - return; -} - - -void -tape_dump_sense (devstat_t * stat) -{ -#ifdef TAPE_DEBUG - int sl; -#endif -#if 0 - - PRINT_WARN ("------------I/O resulted in unit check:-----------\n"); - for (sl = 0; sl < 4; sl++) { - PRINT_WARN ("Sense:"); - for (sct = 0; sct < 8; sct++) { - PRINT_WARN (" %2d:0x%02X", 8 * sl + sct, - stat->ii.sense.data[8 * sl + sct]); - } - PRINT_WARN ("\n"); - } - PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X " - " %02X%02X%02X%02X %02X%02X%02X%02X \n", - stat->ii.sense.data[0], stat->ii.sense.data[1], - stat->ii.sense.data[2], stat->ii.sense.data[3], - stat->ii.sense.data[4], stat->ii.sense.data[5], - stat->ii.sense.data[6], stat->ii.sense.data[7], - stat->ii.sense.data[8], stat->ii.sense.data[9], - stat->ii.sense.data[10], stat->ii.sense.data[11], - stat->ii.sense.data[12], stat->ii.sense.data[13], - stat->ii.sense.data[14], stat->ii.sense.data[15]); - PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X " - " %02X%02X%02X%02X %02X%02X%02X%02X \n", - stat->ii.sense.data[16], stat->ii.sense.data[17], - stat->ii.sense.data[18], stat->ii.sense.data[19], - stat->ii.sense.data[20], stat->ii.sense.data[21], - stat->ii.sense.data[22], stat->ii.sense.data[23], - stat->ii.sense.data[24], stat->ii.sense.data[25], - stat->ii.sense.data[26], stat->ii.sense.data[27], - stat->ii.sense.data[28], stat->ii.sense.data[29], - stat->ii.sense.data[30], stat->ii.sense.data[31]); -#endif -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"SENSE:"); - for (sl=0;sl<31;sl++) { - debug_int_event (tape_debug_area,3,stat->ii.sense.data[sl]); - } - debug_int_exception (tape_debug_area,3,stat->ii.sense.data[31]); -#endif -} - -/* - * Setup tape_info_t structure of a tape device - */ -int -tape_setup (tape_info_t * ti, int irq, int minor) -{ - long lockflags; - int rc = 0; - - if (minor>254) { - PRINT_WARN ("Device id %d on irq %d will not be accessible since this driver is restricted to 128 devices.\n",minor/2,irq); - return -EINVAL; - } - rc = get_dev_info_by_irq (irq, &(ti->devinfo)); - if (rc == -ENODEV) { /* end of device list */ - return rc; - } - ti->rew_minor = minor; - ti->nor_minor = minor + 1; - ti->blk_minor = minor; -#ifdef CONFIG_DEVFS_FS - tape_mkdevfsroots(ti); -#endif - /* Register IRQ */ -#ifdef CONFIG_S390_TAPE_DYNAMIC - rc = s390_request_irq_special (irq, tape_irq, tape_noper_handler,0, "tape", &(ti->devstat)); -#else - rc = s390_request_irq (irq, tape_irq, 0, "tape", &(ti->devstat)); -#endif - s390irq_spin_lock_irqsave (irq, lockflags); - ti->next = NULL; - if (rc) - PRINT_WARN ("Cannot register irq %d, rc=%d\n", irq, rc); - init_waitqueue_head (&ti->wq); - ti->kernbuf = ti->userbuf = ti->discdata = NULL; - tapestate_set (ti, TS_UNUSED); - ti->discdata=NULL; - ti->discipline->setup_assist (ti); - ti->wanna_wakeup=0; - s390irq_spin_unlock_irqrestore (irq, lockflags); - return rc; -} - -/* - * tape_init will register the driver for each tape. - */ -int -tape_init (void) -{ - long lockflags; - s390_dev_info_t dinfo; - tape_discipline_t *disc; - tape_info_t *ti = NULL, *tempti = NULL; - char *opt_char,*opt_block,*opt_3490,*opt_3480; - int irq = 0, rc, retries = 0, tape_num = 0; - static int initialized=0; - - if (initialized) // Only init the devices once - return 0; - initialized=1; - -#ifdef TAPE_DEBUG - tape_debug_area = debug_register ( "tape", 3, 2, 10); - debug_register_view(tape_debug_area,&debug_hex_ascii_view); - debug_text_event (tape_debug_area,3,"begin init"); -#endif /* TAPE_DEBUG */ - - /* print banner */ - PRINT_WARN ("IBM S/390 Tape Device Driver (v1.01).\n"); - PRINT_WARN ("(C) IBM Deutschland Entwicklung GmbH, 2000\n"); - opt_char=opt_block=opt_3480=opt_3490="not present"; -#ifdef CONFIG_S390_TAPE_CHAR - opt_char="built in"; -#endif -#ifdef CONFIG_S390_TAPE_BLOCK - opt_block="built in"; -#endif -#ifdef CONFIG_S390_TAPE_3480 - opt_3480="built in"; -#endif -#ifdef CONFIG_S390_TAPE_3490 - opt_3490="built in"; -#endif - /* print feature info */ - PRINT_WARN ("character device frontend : %s\n",opt_char); - PRINT_WARN ("block device frontend : %s\n",opt_block); - PRINT_WARN ("support for 3480 compatible : %s\n",opt_3480); - PRINT_WARN ("support for 3490 compatible : %s\n",opt_3490); - -#ifndef MODULE - tape_split_parm_string(tape_parm_string); -#endif - if (*tape) - PRINT_INFO ("Using ranges supplied in parameters, disabling autoprobe mode.\n"); - else - PRINT_INFO ("No parameters supplied, enabling autoprobe mode for all supported devices.\n"); -#ifdef CONFIG_S390_TAPE_3490 - if (*tape) - first_discipline = tape3490_init (0); // no autoprobe for devices - else - first_discipline = tape3490_init (1); // do autoprobe since no parm specified - first_discipline->next = NULL; -#endif - -#ifdef CONFIG_S390_TAPE_3480 - if (first_discipline == NULL) { - if (*tape) - first_discipline = tape3480_init (0); // no autoprobe for devices - else - first_discipline = tape3480_init (1); // do autoprobe since no parm specified - first_discipline->next = NULL; - } else { - if (*tape) - first_discipline->next = tape3480_init (0); // no autoprobe for devices - else - first_discipline->next = tape3480_init (1); // do autoprobe since no parm specified - ((tape_discipline_t*) (first_discipline->next))->next=NULL; - } -#endif -#ifdef CONFIG_DEVFS_FS - tape_devfs_root_entry=devfs_mk_dir (NULL, "tape", NULL); -#endif CONFIG_DEVFS_FS - -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"dev detect"); -#endif /* TAPE_DEBUG */ - /* Allocate the tape structures */ - if (*tape!=NULL) { - // we have parameters, continue with parsing the parameters and set the devices online - tape_parm_parse (tape); - } else { - // we are running in autodetect mode, search all devices for compatibles - for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) { - rc = get_dev_info_by_irq (irq, &dinfo); - disc = first_discipline; - while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type)) - disc = (tape_discipline_t *) (disc->next); - if ((disc == NULL) || (rc == -ENODEV)) - continue; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"det irq: "); - debug_int_event (tape_debug_area,3,irq); - debug_text_event (tape_debug_area,3,"cu: "); - debug_int_event (tape_debug_area,3,disc->cu_type); -#endif /* TAPE_DEBUG */ - PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2); - /* Allocate tape structure */ - ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC); - if (ti == NULL) { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"ti:no mem "); -#endif /* TAPE_DEBUG */ - PRINT_INFO ("tape: can't allocate memory for " - "tape info structure\n"); - continue; - } - memset(ti,0,sizeof(tape_info_t)); - ti->discipline = disc; - disc->tape = ti; - rc = tape_setup (ti, irq, tape_num); - if (rc) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"tsetup err"); - debug_int_exception (tape_debug_area,3,rc); -#endif /* TAPE_DEBUG */ - kfree (ti); - } else { - s390irq_spin_lock_irqsave (irq, lockflags); - if (first_tape_info == NULL) { - first_tape_info = ti; - } else { - tempti = first_tape_info; - while (tempti->next != NULL) - tempti = tempti->next; - tempti->next = ti; - } - tape_num += 2; - s390irq_spin_unlock_irqrestore (irq, lockflags); - } - } - } - - /* Allocate local buffer for the ccwcache */ - tape_init_emergency_req (); -#ifdef CONFIG_PROC_FS -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) - tape_devices_entry = create_proc_entry ("tapedevices", - S_IFREG | S_IRUGO | S_IWUSR, - &proc_root); - tape_devices_entry->proc_fops = &tape_devices_file_ops; - tape_devices_entry->proc_iops = &tape_devices_inode_ops; -#else - tape_devices_entry = (struct proc_dir_entry *) kmalloc - (sizeof (struct proc_dir_entry), GFP_ATOMIC); - if (tape_devices_entry) { - memset (tape_devices_entry, 0, sizeof (struct proc_dir_entry)); - tape_devices_entry->name = "tapedevices"; - tape_devices_entry->namelen = strlen ("tapedevices"); - tape_devices_entry->low_ino = 0; - tape_devices_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR); - tape_devices_entry->nlink = 1; - tape_devices_entry->uid = 0; - tape_devices_entry->gid = 0; - tape_devices_entry->size = 0; - tape_devices_entry->get_info = NULL; - tape_devices_entry->ops = &tape_devices_inode_ops; - proc_register (&proc_root, tape_devices_entry); - } -#endif -#endif /* CONFIG_PROC_FS */ - - return 0; -} - -#ifdef MODULE -MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte (cotte@de.ibm.com)"); -MODULE_DESCRIPTION("Linux for S/390 channel attached tape device driver"); -MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s"); - -int -init_module (void) -{ -#ifdef CONFIG_S390_TAPE_CHAR - tapechar_init (); -#endif -#ifdef CONFIG_S390_TAPE_BLOCK - tapeblock_init (); -#endif - return 0; -} - -void -cleanup_module (void) -{ - tape_info_t *ti ,*temp; - tape_frontend_t* frontend, *tempfe; - tape_discipline_t* disc ,*tempdi; - int i; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"cleaup mod"); -#endif /* TAPE_DEBUG */ - - if (*tape) { - // we are running with parameters. we'll now deregister from our devno's - for (i=0;inext; - //cleanup a device -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"free irq:"); - debug_int_event (tape_debug_area,6,temp->devinfo.irq); -#endif /* TAPE_DEBUG */ - free_irq (temp->devinfo.irq, &(temp->devstat)); - if (temp->discdata) kfree (temp->discdata); - if (temp->kernbuf) kfree (temp->kernbuf); - if (temp->cqr) tape_free_request(temp->cqr); -#ifdef CONFIG_DEVFS_FS - for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next) - frontend->rmdevfstree(temp); - tape_rmdevfsroots(temp); -#endif - kfree (temp); - } -#ifdef CONFIG_DEVFS_FS - devfs_unregister (tape_devfs_root_entry); -#endif CONFIG_DEVFS_FS -#ifdef CONFIG_PROC_FS -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) - remove_proc_entry ("tapedevices", &proc_root); -#else - proc_unregister (&proc_root, tape_devices_entry->low_ino); - kfree (tape_devices_entry); -#endif /* LINUX_IS_24 */ -#endif -#ifdef CONFIG_S390_TAPE_CHAR - tapechar_uninit(); -#endif -#ifdef CONFIG_S390_TAPE_BLOCK - tapeblock_uninit(); -#endif - frontend=first_frontend; - while (frontend != NULL) { - tempfe = frontend; - frontend = frontend->next; - kfree (tempfe); - } - disc=first_discipline; - while (disc != NULL) { - if (*tape) - disc->shutdown(0); - else - disc->shutdown(1); - tempdi = disc; - disc = disc->next; - kfree (tempdi); - } - /* Deallocate the local buffer for the ccwcache */ - tape_cleanup_emergency_req (); -#ifdef TAPE_DEBUG - debug_unregister (tape_debug_area); -#endif /* TAPE_DEBUG */ -} -#endif /* MODULE */ - -inline void -tapestate_set (tape_info_t * ti, int newstate) -{ - if (ti->tape_state == TS_NOT_OPER) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"ts_set err"); - debug_text_exception (tape_debug_area,3,"dev n.oper"); -#endif /* TAPE_DEBUG */ - } else { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,4,"ts. dev: "); - debug_int_event (tape_debug_area,4,ti->blk_minor); - debug_text_event (tape_debug_area,4,"old ts: "); - debug_text_event (tape_debug_area,4,(((tapestate_get (ti) < TS_SIZE) && - (tapestate_get (ti) >=0 )) ? - state_verbose[tapestate_get (ti)] : - "UNKNOWN TS")); - debug_text_event (tape_debug_area,4,"new ts: "); - debug_text_event (tape_debug_area,4,(((newstate < TS_SIZE) && - (newstate >= 0)) ? - state_verbose[newstate] : - "UNKNOWN TS")); -#endif /* TAPE_DEBUG */ - ti->tape_state = newstate; - } -} - -inline int -tapestate_get (tape_info_t * ti) -{ - return (ti->tape_state); -} - -void -tapestate_event (tape_info_t * ti, int event) -{ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"te! dev: "); - debug_int_event (tape_debug_area,6,ti->blk_minor); - debug_text_event (tape_debug_area,6,"event:"); - debug_text_event (tape_debug_area,6,((event >=0) && - (event < TE_SIZE)) ? - event_verbose[event] : "TE UNKNOWN"); - debug_text_event (tape_debug_area,6,"state:"); - debug_text_event (tape_debug_area,6,((tapestate_get(ti) >= 0) && - (tapestate_get(ti) < TS_SIZE)) ? - state_verbose[tapestate_get (ti)] : - "TS UNKNOWN"); -#endif /* TAPE_DEBUG */ - if (event == TE_ERROR) { - ti->discipline->error_recovery(ti); - } else { - if ((event >= 0) && - (event < TE_SIZE) && - (tapestate_get (ti) >= 0) && - (tapestate_get (ti) < TS_SIZE) && - ((*(ti->discipline->event_table))[tapestate_get (ti)][event] != NULL)) - ((*(ti->discipline->event_table))[tapestate_get (ti)][event]) (ti); - else { -#ifdef TAPE_DEBUG - debug_text_exception (tape_debug_area,3,"TE UNEXPEC"); -#endif /* TAPE_DEBUG */ - ti->discipline->default_handler (ti); - } - } -} - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 - * End: - */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_char.c linux.21rc1-ac2/drivers/s390/char/tape_char.c --- linux.21rc1/drivers/s390/char/tape_char.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_char.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,513 @@ +/* + * drivers/s390/char/tape_char.c + * character device frontend for tape device driver + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Michael Holzheu + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tape.h" +#include "tape_std.h" + +#define PRINTK_HEADER "TCHAR:" + +#define TAPECHAR_DEVFSMODE 0020644 /* crwxrw-rw- */ +#define TAPECHAR_MAJOR 0 /* get dynamic major */ + +int tapechar_major = TAPECHAR_MAJOR; + +/* + * Prototypes for file operation functions + */ +static ssize_t tapechar_read(struct file *, char *, size_t, loff_t *); +static ssize_t tapechar_write(struct file *, const char *, size_t, loff_t *); +static int tapechar_open(struct inode *,struct file *); +static int tapechar_release(struct inode *,struct file *); +static int tapechar_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); + +/* + * File operation structure for tape character frontend + */ +static struct file_operations tape_fops = +{ + .read = tapechar_read, + .write = tapechar_write, + .ioctl = tapechar_ioctl, + .open = tapechar_open, + .release = tapechar_release, +}; + +#ifdef CONFIG_DEVFS_FS +/* + * Create Char directory with (non)rewinding entries + */ +static int +tapechar_mkdevfstree(struct tape_device *device) +{ + device->char_data.devfs_char_dir = + devfs_mk_dir(device->devfs_dir, "char", device); + if (device->char_data.devfs_char_dir == NULL) + return -ENOENT; + device->char_data.devfs_nonrewinding = + devfs_register(device->char_data.devfs_char_dir, + "nonrewinding", DEVFS_FL_DEFAULT, + tapechar_major, device->first_minor, + TAPECHAR_DEVFSMODE, &tape_fops, device); + if (device->char_data.devfs_nonrewinding == NULL) { + devfs_unregister(device->char_data.devfs_char_dir); + return -ENOENT; + } + device->char_data.devfs_rewinding = + devfs_register(device->char_data.devfs_char_dir, + "rewinding", DEVFS_FL_DEFAULT, + tapechar_major, device->first_minor + 1, + TAPECHAR_DEVFSMODE, &tape_fops, device); + if (device->char_data.devfs_rewinding == NULL) { + devfs_unregister(device->char_data.devfs_nonrewinding); + devfs_unregister(device->char_data.devfs_char_dir); + return -ENOENT; + } + return 0; +} + +/* + * Remove devfs entries + */ +static void +tapechar_rmdevfstree (struct tape_device *device) +{ + if (device->char_data.devfs_nonrewinding) + devfs_unregister(device->char_data.devfs_nonrewinding); + if (device->char_data.devfs_rewinding) + devfs_unregister(device->char_data.devfs_rewinding); + if (device->char_data.devfs_char_dir) + devfs_unregister(device->char_data.devfs_char_dir); +} +#endif + +/* + * This function is called for every new tapedevice + */ +int +tapechar_setup_device(struct tape_device * device) +{ +#ifdef CONFIG_DEVFS_FS + int rc; + + rc = tapechar_mkdevfstree(device); + if (rc) + return rc; +#endif + + tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD); + return 0; + +} + +void +tapechar_cleanup_device(struct tape_device* device) +{ +#ifdef CONFIG_DEVFS_FS + tapechar_rmdevfstree(device); +#endif + tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_REMOVE); +} + +static inline int +tapechar_check_idalbuffer(struct tape_device *device, size_t block_size) +{ + struct idal_buffer *new; + + if (device->char_data.idal_buf != NULL && + device->char_data.idal_buf->size >= block_size) + return 0; + + /* The current idal buffer is not big enough. Allocate a new one. */ + new = idal_buffer_alloc(block_size, 0); + if (new == NULL) + return -ENOMEM; + if (device->char_data.idal_buf != NULL) + idal_buffer_free(device->char_data.idal_buf); + device->char_data.idal_buf = new; + return 0; +} + +/* + * Tape device read function + */ +ssize_t +tapechar_read (struct file *filp, char *data, size_t count, loff_t *ppos) +{ + struct tape_device *device; + struct tape_request *request; + size_t block_size; + int rc; + + DBF_EVENT(6, "TCHAR:read\n"); + device = (struct tape_device *) filp->private_data; + + /* Check position. */ + if (ppos != &filp->f_pos) { + /* + * "A request was outside the capabilities of the device." + * This check uses internal knowledge about how pread and + * read work... + */ + DBF_EVENT(6, "TCHAR:ppos wrong\n"); + return -EOVERFLOW; + } + + /* + * If the tape isn't terminated yet, do it now. And since we then + * are at the end of the tape there wouldn't be anything to read + * anyways. So we return immediatly. + */ + if(device->required_tapemarks) { + return tape_std_terminate_write(device); + } + + /* Find out block size to use */ + if (device->char_data.block_size != 0) { + if (count < device->char_data.block_size) { + DBF_EVENT(3, "TCHAR:read smaller than block " + "size was requested\n"); + return -EINVAL; + } + block_size = device->char_data.block_size; + } else { + block_size = count; + rc = tapechar_check_idalbuffer(device, block_size); + if (rc) + return rc; + } + DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size); + /* Let the discipline build the ccw chain. */ + request = device->discipline->read_block(device, block_size); + if (IS_ERR(request)) + return PTR_ERR(request); + /* Execute it. */ + rc = tape_do_io(device, request); + if (rc == 0) { + rc = block_size - device->devstat.rescnt; + DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc); + filp->f_pos += rc; + /* Copy data from idal buffer to user space. */ + if (idal_buffer_to_user(device->char_data.idal_buf, + data, rc) != 0) + rc = -EFAULT; + } + tape_put_request(request); + return rc; +} + +/* + * Tape device write function + */ +ssize_t +tapechar_write(struct file *filp, const char *data, size_t count, loff_t *ppos) +{ + struct tape_device *device; + struct tape_request *request; + size_t block_size; + size_t written; + int nblocks; + int i, rc; + + DBF_EVENT(6, "TCHAR:write\n"); + device = (struct tape_device *) filp->private_data; + /* Check position */ + if (ppos != &filp->f_pos) { + /* "A request was outside the capabilities of the device." */ + DBF_EVENT(6, "TCHAR:ppos wrong\n"); + return -EOVERFLOW; + } + /* Find out block size and number of blocks */ + if (device->char_data.block_size != 0) { + if (count < device->char_data.block_size) { + DBF_EVENT(3, "TCHAR:write smaller than block " + "size was requested\n"); + return -EINVAL; + } + block_size = device->char_data.block_size; + nblocks = count / block_size; + } else { + block_size = count; + rc = tapechar_check_idalbuffer(device, block_size); + if (rc) + return rc; + nblocks = 1; + } + DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size); + DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks); + /* Let the discipline build the ccw chain. */ + request = device->discipline->write_block(device, block_size); + if (IS_ERR(request)) + return PTR_ERR(request); + rc = 0; + written = 0; + for (i = 0; i < nblocks; i++) { + /* Copy data from user space to idal buffer. */ + if (idal_buffer_from_user(device->char_data.idal_buf, + data, block_size)) { + rc = -EFAULT; + break; + } + rc = tape_do_io(device, request); + if (rc) + break; + DBF_EVENT(6, "TCHAR:wbytes: %lx\n", + block_size - device->devstat.rescnt); + filp->f_pos += block_size - device->devstat.rescnt; + written += block_size - device->devstat.rescnt; + if (device->devstat.rescnt != 0) + break; + data += block_size; + } + tape_put_request(request); + + if (rc == -ENOSPC) { + /* + * Ok, the device has no more space. It has NOT written + * the block. + */ + if (device->discipline->process_eov) + device->discipline->process_eov(device); + if (written > 0) + rc = 0; + } + + /* + * After doing a write we always need two tapemarks to correctly + * terminate the tape (one to terminate the file, the second to + * flag the end of recorded data. + * Since process_eov positions the tape in front of the written + * tapemark it doesn't hurt to write two marks again. + */ + if(!rc) + device->required_tapemarks = 2; + + return rc ? rc : written; +} + +/* + * Character frontend tape device open function. + */ +int +tapechar_open (struct inode *inode, struct file *filp) +{ + struct tape_device *device; + int minor, rc; + + MOD_INC_USE_COUNT; + if (major(filp->f_dentry->d_inode->i_rdev) != tapechar_major) + return -ENODEV; + minor = minor(filp->f_dentry->d_inode->i_rdev); + device = tape_get_device(minor / TAPE_MINORS_PER_DEV); + if (IS_ERR(device)) { + MOD_DEC_USE_COUNT; + return PTR_ERR(device); + } + DBF_EVENT(6, "TCHAR:open: %x\n", minor(inode->i_rdev)); + rc = tape_open(device); + if (rc == 0) { + rc = tape_assign(device, TAPE_STATUS_ASSIGN_A); + if (rc == 0) { + filp->private_data = device; + return 0; + } + tape_release(device); + } + tape_put_device(device); + MOD_DEC_USE_COUNT; + return rc; +} + +/* + * Character frontend tape device release function. + */ + +int +tapechar_release(struct inode *inode, struct file *filp) +{ + struct tape_device *device; + + device = (struct tape_device *) filp->private_data; + DBF_EVENT(6, "TCHAR:release: %x\n", minor(inode->i_rdev)); + + /* + * If this is the rewinding tape minor then rewind. In that case we + * write all required tapemarks. Otherwise only one to terminate the + * file. + */ + if ((minor(inode->i_rdev) & 1) != 0) { + if(device->required_tapemarks) + tape_std_terminate_write(device); + tape_mtop(device, MTREW, 1); + } else { + if(device->required_tapemarks > 1) { + if(tape_mtop(device, MTWEOF, 1) == 0) + device->required_tapemarks--; + } + } + + if (device->char_data.idal_buf != NULL) { + idal_buffer_free(device->char_data.idal_buf); + device->char_data.idal_buf = NULL; + } + tape_unassign(device, TAPE_STATUS_ASSIGN_A); + tape_release(device); + filp->private_data = NULL; tape_put_device(device); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Tape device io controls. + */ +static int +tapechar_ioctl(struct inode *inp, struct file *filp, + unsigned int no, unsigned long data) +{ + struct tape_device *device; + int rc; + + DBF_EVENT(6, "TCHAR:ioct(%x)\n", no); + + device = (struct tape_device *) filp->private_data; + + if (no == MTIOCTOP) { + struct mtop op; + + if (copy_from_user(&op, (char *) data, sizeof(op)) != 0) + return -EFAULT; + if (op.mt_count < 0) + return -EINVAL; + + /* + * Operations that change tape position should write final + * tapemarks + */ + switch(op.mt_op) { + case MTFSF: + case MTBSF: + case MTFSR: + case MTBSR: + case MTREW: + case MTOFFL: + case MTEOM: + case MTRETEN: + case MTBSFM: + case MTFSFM: + case MTSEEK: + if(device->required_tapemarks) + tape_std_terminate_write(device); + default: + ; + } + rc = tape_mtop(device, op.mt_op, op.mt_count); + + if(op.mt_op == MTWEOF && rc == 0) { + if(op.mt_count > device->required_tapemarks) + device->required_tapemarks = 0; + else + device->required_tapemarks -= op.mt_count; + } + return rc; + } + if (no == MTIOCPOS) { + /* MTIOCPOS: query the tape position. */ + struct mtpos pos; + + rc = tape_mtop(device, MTTELL, 1); + if (rc < 0) + return rc; + pos.mt_blkno = rc; + if (copy_to_user((char *) data, &pos, sizeof(pos)) != 0) + return -EFAULT; + return 0; + } + if (no == MTIOCGET) { + /* MTIOCGET: query the tape drive status. */ + struct mtget get; + + memset(&get, 0, sizeof(get)); + get.mt_type = MT_ISUNKNOWN; + get.mt_resid = device->devstat.rescnt; + get.mt_dsreg = device->tape_status; + /* FIXME: mt_erreg, mt_fileno */ + get.mt_gstat = device->tape_generic_status; + + if(device->medium_state == MS_LOADED) { + rc = tape_mtop(device, MTTELL, 1); + + if(rc < 0) + return rc; + + if(rc == 0) + get.mt_gstat |= GMT_BOT(~0); + + get.mt_blkno = rc; + } + get.mt_erreg = 0; + if (copy_to_user((char *) data, &get, sizeof(get)) != 0) + return -EFAULT; + return 0; + } + /* Try the discipline ioctl function. */ + if (device->discipline->ioctl_fn == NULL) + return -EINVAL; + return device->discipline->ioctl_fn(device, no, data); +} + +/* + * Initialize character device frontend. + */ +int +tapechar_init (void) +{ + int rc; + + /* Register the tape major number to the kernel */ +#ifdef CONFIG_DEVFS_FS + if (tapechar_major == 0) + tapechar_major = devfs_alloc_major(DEVFS_SPECIAL_CHR); +#endif + rc = register_chrdev(tapechar_major, "tape", &tape_fops); + if (rc < 0) { + PRINT_ERR("can't get major %d\n", tapechar_major); + DBF_EVENT(3, "TCHAR:initfail\n"); + return rc; + } + if (tapechar_major == 0) + tapechar_major = rc; /* accept dynamic major number */ + PRINT_INFO("Tape gets major %d for char device\n", tapechar_major); + DBF_EVENT(3, "Tape gets major %d for char device\n", rc); + DBF_EVENT(3, "TCHAR:init ok\n"); + return 0; +} + +/* + * cleanup + */ +void +tapechar_exit(void) +{ + unregister_chrdev (tapechar_major, "tape"); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tapechar.c linux.21rc1-ac2/drivers/s390/char/tapechar.c --- linux.21rc1/drivers/s390/char/tapechar.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tapechar.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,759 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tapechar.c - * character device frontend for tape device driver - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - * - **************************************************************************** - */ - -#include "tapedefs.h" -#include -#include -#include -#include -#include /* CCW allocations */ -#include -#include -#include -#include -#include -#ifdef MODULE -#define __NO_VERSION__ -#include -#endif -#include "tape.h" -#include "tapechar.h" - -#define PRINTK_HEADER "TCHAR:" - -/* - * file operation structure for tape devices - */ -static struct file_operations tape_fops = -{ - // owner : THIS_MODULE, - llseek:NULL, /* lseek - default */ - read:tape_read, /* read */ - write:tape_write, /* write */ - readdir:NULL, /* readdir - bad */ - poll:NULL, /* poll */ - ioctl:tape_ioctl, /* ioctl */ - mmap:NULL, /* mmap */ - open:tape_open, /* open */ - flush:NULL, /* flush */ - release:tape_release, /* release */ - fsync:NULL, /* fsync */ - fasync:NULL, /* fasync */ - lock:NULL, -}; - -int tape_major = TAPE_MAJOR; - -#ifdef CONFIG_DEVFS_FS -void -tapechar_mkdevfstree (tape_info_t* ti) { - ti->devfs_char_dir=devfs_mk_dir (ti->devfs_dir, "char", ti); - ti->devfs_nonrewinding=devfs_register(ti->devfs_char_dir, "nonrewinding", - DEVFS_FL_DEFAULT,tape_major, - ti->nor_minor, TAPECHAR_DEFAULTMODE, - &tape_fops, ti); - ti->devfs_rewinding=devfs_register(ti->devfs_char_dir, "rewinding", - DEVFS_FL_DEFAULT, tape_major, ti->rew_minor, - TAPECHAR_DEFAULTMODE, &tape_fops, ti); -} - -void -tapechar_rmdevfstree (tape_info_t* ti) { - devfs_unregister(ti->devfs_nonrewinding); - devfs_unregister(ti->devfs_rewinding); - devfs_unregister(ti->devfs_char_dir); -} -#endif - -void -tapechar_setup (tape_info_t * ti) -{ -#ifdef CONFIG_DEVFS_FS - tapechar_mkdevfstree(ti); -#endif -} - -void -tapechar_init (void) -{ - int result; - tape_frontend_t *charfront,*temp; - tape_info_t* ti; - - tape_init(); - - /* Register the tape major number to the kernel */ -#ifdef CONFIG_DEVFS_FS - result = devfs_register_chrdev (tape_major, "tape", &tape_fops); -#else - result = register_chrdev (tape_major, "tape", &tape_fops); -#endif - - if (result < 0) { - PRINT_WARN (KERN_ERR "tape: can't get major %d\n", tape_major); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"c:initfail"); - debug_text_event (tape_debug_area,3,"regchrfail"); -#endif /* TAPE_DEBUG */ - panic ("no major number available for tape char device"); - } - if (tape_major == 0) - tape_major = result; /* accept dynamic major number */ - PRINT_WARN (KERN_ERR " tape gets major %d for character device\n", result); - charfront = kmalloc (sizeof (tape_frontend_t), GFP_KERNEL); - if (charfront == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"c:initfail"); - debug_text_event (tape_debug_area,3,"no mem"); -#endif /* TAPE_DEBUG */ - panic ("no major number available for tape char device"); - } - charfront->device_setup = tapechar_setup; -#ifdef CONFIG_DEVFS_FS - charfront->mkdevfstree = tapechar_mkdevfstree; - charfront->rmdevfstree = tapechar_rmdevfstree; -#endif -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"c:init ok"); -#endif /* TAPE_DEBUG */ - charfront->next=NULL; - if (first_frontend==NULL) { - first_frontend=charfront; - } else { - temp=first_frontend; - while (temp->next!=NULL) - temp=temp->next; - temp->next=charfront; - } - ti=first_tape_info; - while (ti!=NULL) { - tapechar_setup(ti); - ti=ti->next; - } -} - -void -tapechar_uninit (void) -{ - unregister_chrdev (tape_major, "tape"); -} - -/* - * Tape device read function - */ -ssize_t -tape_read (struct file *filp, char *data, size_t count, loff_t * ppos) -{ - long lockflags; - tape_info_t *ti; - size_t block_size; - ccw_req_t *cqr; - int rc; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:read"); -#endif /* TAPE_DEBUG */ - ti = first_tape_info; - while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp)) - ti = (tape_info_t *) ti->next; - if (ti == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:nodev"); -#endif /* TAPE_DEBUG */ - return -ENODEV; - } - if (ppos != &filp->f_pos) { - /* "A request was outside the capabilities of the device." */ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:ppos wrong"); -#endif /* TAPE_DEBUG */ - return -EOVERFLOW; /* errno=75 Value too large for def. data type */ - } - if (ti->block_size == 0) { - block_size = count; - } else { - block_size = ti->block_size; - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:nbytes:"); - debug_int_event (tape_debug_area,6,block_size); -#endif - cqr = ti->discipline->read_block (data, block_size, ti); - if (!cqr) { - return -ENOBUFS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - if (rc) { - tapestate_set(ti,TS_IDLE); - kfree (cqr); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return rc; - } - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - ti->discipline->free_read_block (cqr, ti); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return ti->rc; - } - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:rbytes:"); - debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt); -#endif /* TAPE_DEBUG */ - filp->f_pos += block_size - ti->devstat.rescnt; - return block_size - ti->devstat.rescnt; -} - -/* - * Tape device write function - */ -ssize_t -tape_write (struct file *filp, const char *data, size_t count, loff_t * ppos) -{ - long lockflags; - tape_info_t *ti; - size_t block_size; - ccw_req_t *cqr; - int nblocks, i, rc; - size_t written = 0; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:write"); -#endif - ti = first_tape_info; - while ((ti != NULL) && (ti->nor_filp != filp) && (ti->rew_filp != filp)) - ti = (tape_info_t *) ti->next; - if (ti == NULL) - return -ENODEV; - if (ppos != &filp->f_pos) { - /* "A request was outside the capabilities of the device." */ -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:ppos wrong"); -#endif - return -EOVERFLOW; /* errno=75 Value too large for def. data type */ - } - if ((ti->block_size != 0) && (count % ti->block_size != 0)) - return -EIO; - if (ti->block_size == 0) { - block_size = count; - nblocks = 1; - } else { - block_size = ti->block_size; - nblocks = count / (ti->block_size); - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:nbytes:"); - debug_int_event (tape_debug_area,6,block_size); - debug_text_event (tape_debug_area,6,"c:nblocks:"); - debug_int_event (tape_debug_area,6,nblocks); -#endif - for (i = 0; i < nblocks; i++) { - cqr = ti->discipline->write_block (data + i * block_size, block_size, ti); - if (!cqr) { - return -ENOBUFS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - ti->discipline->free_write_block (cqr, ti); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if ((ti->rc==-ENOSPC) && (i!=0)) - return i*block_size; - return ti->rc; - } - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:wbytes:"); - debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt); -#endif - filp->f_pos += block_size - ti->devstat.rescnt; - written += block_size - ti->devstat.rescnt; - if (ti->devstat.rescnt > 0) - return written; - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:wtotal:"); - debug_int_event (tape_debug_area,6,written); -#endif - return written; -} - -static int -tape_mtioctop (struct file *filp, short mt_op, int mt_count) -{ - tape_info_t *ti; - ccw_req_t *cqr = NULL; - int rc; - long lockflags; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:mtio"); - debug_text_event (tape_debug_area,6,"c:ioop:"); - debug_int_event (tape_debug_area,6,mt_op); - debug_text_event (tape_debug_area,6,"c:arg:"); - debug_int_event (tape_debug_area,6,mt_count); -#endif - ti = first_tape_info; - while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp)) - ti = (tape_info_t *) ti->next; - if (ti == NULL) - return -ENODEV; - switch (mt_op) { - case MTREW: // rewind - - cqr = ti->discipline->mtrew (ti, mt_count); - break; - case MTOFFL: // put drive offline - - cqr = ti->discipline->mtoffl (ti, mt_count); - break; - case MTUNLOAD: // unload the tape - - cqr = ti->discipline->mtunload (ti, mt_count); - break; - case MTWEOF: // write tapemark - - cqr = ti->discipline->mtweof (ti, mt_count); - break; - case MTFSF: // forward space file - - cqr = ti->discipline->mtfsf (ti, mt_count); - break; - case MTBSF: // backward space file - - cqr = ti->discipline->mtbsf (ti, mt_count); - break; - case MTFSFM: // forward space file, stop at BOT side - - cqr = ti->discipline->mtfsfm (ti, mt_count); - break; - case MTBSFM: // backward space file, stop at BOT side - - cqr = ti->discipline->mtbsfm (ti, mt_count); - break; - case MTFSR: // forward space file - - cqr = ti->discipline->mtfsr (ti, mt_count); - break; - case MTBSR: // backward space file - - cqr = ti->discipline->mtbsr (ti, mt_count); - break; - case MTNOP: - cqr = ti->discipline->mtnop (ti, mt_count); - break; - case MTEOM: // postion at the end of portion - - case MTRETEN: // retension the tape - - cqr = ti->discipline->mteom (ti, mt_count); - break; - case MTERASE: - cqr = ti->discipline->mterase (ti, mt_count); - break; - case MTSETDENSITY: - cqr = ti->discipline->mtsetdensity (ti, mt_count); - break; - case MTSEEK: - cqr = ti->discipline->mtseek (ti, mt_count); - break; - case MTSETDRVBUFFER: - cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count); - break; - case MTLOCK: - cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count); - break; - case MTUNLOCK: - cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count); - break; - case MTLOAD: - cqr = ti->discipline->mtload (ti, mt_count); - if (cqr!=NULL) break; // if backend driver has an load function ->use it - // if no medium is in, wait until it gets inserted - if (ti->medium_is_unloaded) { - wait_event_interruptible (ti->wq,ti->medium_is_unloaded==0); - } - return 0; - case MTCOMPRESSION: - cqr = ti->discipline->mtcompression (ti, mt_count); - break; - case MTSETPART: - cqr = ti->discipline->mtsetpart (ti, mt_count); - break; - case MTMKPART: - cqr = ti->discipline->mtmkpart (ti, mt_count); - break; - case MTTELL: // return number of block relative to current file - - cqr = ti->discipline->mttell (ti, mt_count); - break; - case MTSETBLK: - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->block_size = mt_count; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:setblk:"); - debug_int_event (tape_debug_area,6,mt_count); -#endif - return 0; - case MTRESET: - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->kernbuf = ti->userbuf = NULL; - tapestate_set (ti, TS_IDLE); - ti->block_size = 0; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:devreset:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif - return 0; - default: -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:inv.mtio"); -#endif - return -EINVAL; - } - if (cqr == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:ccwg fail"); -#endif - return -ENOSPC; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - if (ti->kernbuf != NULL) { - kfree (ti->kernbuf); - ti->kernbuf = NULL; - } - tape_free_request (cqr); - // if medium was unloaded, update the corresponding variable. - switch (mt_op) { - case MTOFFL: - case MTUNLOAD: - ti->medium_is_unloaded=1; - } - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (((mt_op == MTEOM) || (mt_op == MTRETEN)) && (tapestate_get (ti) == TS_FAILED)) - tapestate_set (ti, TS_DONE); - if (tapestate_get (ti) == TS_FAILED) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return ti->rc; - } - if (tapestate_get (ti) == TS_NOT_OPER) { - ti->blk_minor=ti->rew_minor=ti->nor_minor=-1; - ti->devinfo.irq=-1; - s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags); - return -ENODEV; - } - if (tapestate_get (ti) != TS_DONE) { - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - return -EIO; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - switch (mt_op) { - case MTRETEN: //need to rewind the tape after moving to eom - - return tape_mtioctop (filp, MTREW, 1); - case MTFSFM: //need to skip back over the filemark - - return tape_mtioctop (filp, MTBSFM, 1); - case MTBSF: //need to skip forward over the filemark - - return tape_mtioctop (filp, MTFSF, 1); - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:mtio done"); -#endif - return 0; -} - -/* - * Tape device io controls. - */ -int -tape_ioctl (struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - long lockflags; - tape_info_t *ti; - ccw_req_t *cqr; - struct mtop op; /* structure for MTIOCTOP */ - struct mtpos pos; /* structure for MTIOCPOS */ - struct mtget get; - - int rc; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:ioct"); -#endif - ti = first_tape_info; - while ((ti != NULL) && - (ti->rew_minor != MINOR (inode->i_rdev)) && - (ti->nor_minor != MINOR (inode->i_rdev))) - ti = (tape_info_t *) ti->next; - if (ti == NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:nodev"); -#endif - return -ENODEV; - } - // check for discipline ioctl overloading - if ((rc = ti->discipline->discipline_ioctl_overload (inode, filp, cmd, arg)) - != -EINVAL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:ioverloa"); -#endif - return rc; - } - - switch (cmd) { - case MTIOCTOP: /* tape op command */ - if (copy_from_user (&op, (char *) arg, sizeof (struct mtop))) { - return -EFAULT; - } - return (tape_mtioctop (filp, op.mt_op, op.mt_count)); - case MTIOCPOS: /* query tape position */ - cqr = ti->discipline->mttell (ti, 0); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - pos.mt_blkno = ti->rc; - ti->cqr = NULL; - if (ti->kernbuf != NULL) { - kfree (ti->kernbuf); - ti->kernbuf = NULL; - } - tape_free_request (cqr); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if (copy_to_user ((char *) arg, &pos, sizeof (struct mtpos))) - return -EFAULT; - return 0; - case MTIOCGET: - get.mt_erreg = ti->rc; - cqr = ti->discipline->mttell (ti, 0); - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - ti->cqr = cqr; - ti->wanna_wakeup=0; - do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event_interruptible (ti->wq,ti->wanna_wakeup); - get.mt_blkno = ti->rc; - get.mt_fileno = 0; - get.mt_type = MT_ISUNKNOWN; - get.mt_resid = ti->devstat.rescnt; - get.mt_dsreg = ti->devstat.ii.sense.data[3]; - get.mt_gstat = 0; - if (ti->devstat.ii.sense.data[1] & 0x08) - get.mt_gstat &= GMT_BOT (1); // BOT - - if (ti->devstat.ii.sense.data[1] & 0x02) - get.mt_gstat &= GMT_WR_PROT (1); // write protected - - if (ti->devstat.ii.sense.data[1] & 0x40) - get.mt_gstat &= GMT_ONLINE (1); //drive online - - ti->cqr = NULL; - if (ti->kernbuf != NULL) { - kfree (ti->kernbuf); - ti->kernbuf = NULL; - } - tape_free_request (cqr); - if (signal_pending (current)) { - tapestate_set (ti, TS_IDLE); - return -ERESTARTSYS; - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - if (copy_to_user ((char *) arg, &get, sizeof (struct mtget))) - return -EFAULT; - return 0; - default: -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,3,"c:ioct inv"); -#endif - return -EINVAL; - } -} - -/* - * Tape device open function. - */ -int -tape_open (struct inode *inode, struct file *filp) -{ - tape_info_t *ti; - kdev_t dev; - long lockflags; - - inode = filp->f_dentry->d_inode; - ti = first_tape_info; - while ((ti != NULL) && - (ti->rew_minor != MINOR (inode->i_rdev)) && - (ti->nor_minor != MINOR (inode->i_rdev))) - ti = (tape_info_t *) ti->next; - if (ti == NULL) - return -ENODEV; -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:open:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (tapestate_get (ti) != TS_UNUSED) { - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:dbusy"); -#endif - return -EBUSY; - } - tapestate_set (ti, TS_IDLE); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - - dev = MKDEV (tape_major, MINOR (inode->i_rdev)); /* Get the device */ - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - if (ti->rew_minor == MINOR (inode->i_rdev)) - ti->rew_filp = filp; /* save for later reference */ - else - ti->nor_filp = filp; - filp->private_data = ti; /* save the dev.info for later reference */ - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif /* MODULE */ - return 0; -} - -/* - * Tape device release function. - */ -int -tape_release (struct inode *inode, struct file *filp) -{ - long lockflags; - tape_info_t *ti,*lastti; - ccw_req_t *cqr = NULL; - int rc = 0; - - ti = first_tape_info; - while ((ti != NULL) && (ti->rew_minor != MINOR (inode->i_rdev)) && (ti->nor_minor != MINOR (inode->i_rdev))) - ti = (tape_info_t *) ti->next; - if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) { - if (ti==first_tape_info) { - first_tape_info=ti->next; - } else { - lastti=first_tape_info; - while (lastti->next!=ti) lastti=lastti->next; - lastti->next=ti->next; - } - kfree(ti); - goto out; - } - if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:notidle!"); -#endif - rc = -ENXIO; /* error in tape_release */ - goto out; - } -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:release:"); - debug_int_event (tape_debug_area,6,ti->blk_minor); -#endif - if (ti->rew_minor == MINOR (inode->i_rdev)) { - cqr = ti->discipline->mtrew (ti, 1); - if (cqr != NULL) { -#ifdef TAPE_DEBUG - debug_text_event (tape_debug_area,6,"c:rewrelea"); -#endif - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_REW_RELEASE_INIT); - ti->cqr = cqr; - ti->wanna_wakeup=0; - rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); - wait_event (ti->wq,ti->wanna_wakeup); - ti->cqr = NULL; - tape_free_request (cqr); - } - } - s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags); - tapestate_set (ti, TS_UNUSED); - s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags); -out: -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif /* MODULE */ - return rc; -} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tapechar.h linux.21rc1-ac2/drivers/s390/char/tapechar.h --- linux.21rc1/drivers/s390/char/tapechar.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tapechar.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,34 +0,0 @@ - -/*************************************************************************** - * - * drivers/s390/char/tapechar.h - * character device frontend for tape device driver - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - * - **************************************************************************** - */ - -#ifndef TAPECHAR_H -#define TAPECHAR_H -#include -#define TAPECHAR_DEFAULTMODE 0020644 -#define TAPE_MAJOR 0 /* get dynamic major since no major officialy defined for tape */ -/* - * Prototypes for tape_fops - */ -ssize_t tape_read(struct file *, char *, size_t, loff_t *); -ssize_t tape_write(struct file *, const char *, size_t, loff_t *); -int tape_ioctl(struct inode *,struct file *,unsigned int,unsigned long); -int tape_open (struct inode *,struct file *); -int tape_release (struct inode *,struct file *); -#ifdef CONFIG_DEVFS_FS -void tapechar_mkdevfstree (tape_info_t* ti); -#endif -void tapechar_init (void); -void tapechar_uninit (void); -#endif /* TAPECHAR_H */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_core.c linux.21rc1-ac2/drivers/s390/char/tape_core.c --- linux.21rc1/drivers/s390/char/tape_core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_core.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,1345 @@ +/* + * drivers/s390/char/tape_core.c + * basic function of the tape device driver + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Michael Holzheu + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + */ + +#include +#include +#include +#include // for kernel parameters +#include // for requesting modules +#include // for locks +#include + +#include // for variable types +#include +#include +#include + +#include "tape.h" +#include "tape_std.h" + +#ifdef CONFIG_S390_TAPE_3590 +#include "tape_3590.h" +#endif + +#define PRINTK_HEADER "T390:" + +/* + * Prototypes for some static functions. + */ +static void __tape_do_irq (int, void *, struct pt_regs *); +static void __tape_remove_request(struct tape_device *, struct tape_request *); +static void tape_timeout_io (unsigned long); + +/* + * List of tape disciplines guarded by tape_discipline_lock. + */ +static struct list_head tape_disciplines = LIST_HEAD_INIT(tape_disciplines); +static spinlock_t tape_discipline_lock = SPIN_LOCK_UNLOCKED; + +/* + * Pointer to debug area. + */ +debug_info_t *tape_dbf_area = NULL; + +const char *tape_op_verbose[TO_SIZE] = +{ + [TO_BLOCK] = "BLK", + [TO_BSB] = "BSB", + [TO_BSF] = "BSF", + [TO_DSE] = "DSE", + [TO_FSB] = "FSB", + [TO_FSF] = "FSF", + [TO_LBL] = "LBL", + [TO_NOP] = "NOP", + [TO_RBA] = "RBA", + [TO_RBI] = "RBI", + [TO_RFO] = "RFO", + [TO_REW] = "REW", + [TO_RUN] = "RUN", + [TO_WRI] = "WRI", + [TO_WTM] = "WTM", + [TO_MSEN] = "MSN", + [TO_LOAD] = "LOA", + [TO_READ_CONFIG] = "RCF", + [TO_READ_ATTMSG] = "RAT", + [TO_DIS] = "DIS", + [TO_ASSIGN] = "ASS", + [TO_UNASSIGN] = "UAS", + [TO_BREAKASS] = "BRK" +}; + +/* + * Inline functions, that have to be defined. + */ + +/* + * I/O helper function. Adds the request to the request queue + * and starts it if the tape is idle. Has to be called with + * the device lock held. + */ +static inline int +__do_IO(struct tape_device *device, struct tape_request *request) +{ + int rc = 0; + + if(request->cpaddr == NULL) + BUG(); + + if(request->timeout.expires > 0) { + /* Init should be done by caller */ + DBF_EVENT(6, "(%04x): starting timed request\n", + device->devstat.devno); + + request->timeout.function = tape_timeout_io; + request->timeout.data = (unsigned long) + tape_clone_request(request); + add_timer(&request->timeout); + } + + rc = do_IO(device->devinfo.irq, request->cpaddr, + (unsigned long) request, 0x00, request->options); + + return rc; +} + +static void +__tape_process_queue(void *data) +{ + struct tape_device *device = (struct tape_device *) data; + struct list_head *l, *n; + struct tape_request *request; + int rc; + + DBF_EVENT(6, "tape_process_queue(%p)\n", device); + + /* + * We were told to be quiet. Do nothing for now. + */ + if (TAPE_NOACCESS(device)) { + return; + } + + /* + * Try to start each request on request queue until one is + * started successful. + */ + list_for_each_safe(l, n, &device->req_queue) { + request = list_entry(l, struct tape_request, list); + + /* Happens when new request arrive while still doing one. */ + if (request->status == TAPE_REQUEST_IN_IO) + break; + +#ifdef CONFIG_S390_TAPE_BLOCK + if (request->op == TO_BLOCK) + device->discipline->check_locate(device, request); +#endif + switch(request->op) { + case TO_MSEN: + case TO_ASSIGN: + case TO_UNASSIGN: + case TO_BREAKASS: + break; + default: + if (TAPE_OPEN(device)) + break; + DBF_EVENT(3, + "TAPE(%04x): REQ in UNUSED state\n", + device->devstat.devno); + } + + rc = __do_IO(device, request); + if (rc == 0) { + DBF_EVENT(6, "tape: do_IO success\n"); + request->status = TAPE_REQUEST_IN_IO; + break; + } + /* Start failed. Remove request and indicate failure. */ + DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc); + + /* Set final status and remove. */ + request->rc = rc; + __tape_remove_request(device, request); + } +} + +static void +tape_process_queue(void *data) +{ + unsigned long flags; + struct tape_device * device; + + device = (struct tape_device *) data; + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + atomic_set(&device->bh_scheduled, 0); + __tape_process_queue(device); + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); +} + +void +tape_schedule_bh(struct tape_device *device) +{ + /* Protect against rescheduling, when already running. */ + if (atomic_compare_and_swap(0, 1, &device->bh_scheduled)) + return; + + INIT_LIST_HEAD(&device->bh_task.list); + device->bh_task.sync = 0; + device->bh_task.routine = tape_process_queue; + device->bh_task.data = device; + + queue_task(&device->bh_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return; +} + +/* + * Stop running ccw. Has to be called with the device lock held. + */ +static inline int +__tape_halt_io(struct tape_device *device, struct tape_request *request) +{ + int retries; + int rc; + + /* SMB: This should never happen */ + if(request->cpaddr == NULL) + BUG(); + + /* Check if interrupt has already been processed */ + if (request->callback == NULL) + return 0; + + /* Stop a possibly running timer */ + if(request->timeout.expires) { + if(del_timer(&request->timeout) > 0) { + tape_put_request(request); + request->timeout.data = 0L; + } + } + + rc = 0; + for (retries = 0; retries < 5; retries++) { + if (retries < 2) + rc = halt_IO(device->devinfo.irq, + (long) request, request->options); + else + rc = clear_IO(device->devinfo.irq, + (long) request, request->options); + if (rc == 0) + break; /* termination successful */ + if (rc == -ENODEV) + DBF_EXCEPTION(2, "device gone, retry\n"); + else if (rc == -EIO) + DBF_EXCEPTION(2, "I/O error, retry\n"); + else if (rc == -EBUSY) + DBF_EXCEPTION(2, "device busy, retry later\n"); + else + BUG(); + } + if (rc == 0) + request->status = TAPE_REQUEST_DONE; + return rc; +} + +static void +__tape_remove_request(struct tape_device *device, struct tape_request *request) +{ + /* First remove the request from the queue. */ + list_del(&request->list); + + /* This request isn't processed any further. */ + request->status = TAPE_REQUEST_DONE; + + /* Finally, if the callback hasn't been called, do it now. */ + if (request->callback != NULL) { + request->callback(request, request->callback_data); + request->callback = NULL; + } +} + +/* + * Tape state functions + */ +/* + * Printable strings for tape enumerations. + */ +const char *tape_state_string(struct tape_device *device) { + char *s = " ???? "; + + if (TAPE_NOT_OPER(device)) { + s = "NOT_OP"; + } else if (TAPE_NOACCESS(device)) { + s = "NO_ACC"; + } else if (TAPE_BOXED(device)) { + s = "BOXED "; + } else if (TAPE_OPEN(device)) { + s = "IN_USE"; + } else if (TAPE_ASSIGNED(device)) { + s = "ASSIGN"; + } else if (TAPE_INIT(device)) { + s = "INIT "; + } else if (TAPE_UNUSED(device)) { + s = "UNUSED"; + } + + return s; +} + +void +tape_state_set(struct tape_device *device, unsigned int status) +{ + const char *str; + + /* Maybe nothing changed. */ + if (device->tape_status == status) + return; + + DBF_EVENT(4, "ts. dev: %x\n", device->first_minor); + str = tape_state_string(device); + DBF_EVENT(4, "old ts: 0x%08x %s\n", device->tape_status, str); + + device->tape_status = status; + + str = tape_state_string(device); + DBF_EVENT(4, "new ts: 0x%08x %s\n", status, str); + + wake_up(&device->state_change_wq); +} + +void +tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate) +{ + if (device->medium_state == newstate) + return; + + switch(newstate){ + case MS_UNLOADED: + device->tape_generic_status |= GMT_DR_OPEN(~0); + PRINT_INFO("(%04x): Tape is unloaded\n", + device->devstat.devno); + break; + case MS_LOADED: + device->tape_generic_status &= ~GMT_DR_OPEN(~0); + PRINT_INFO("(%04x): Tape has been mounted\n", + device->devstat.devno); + break; + default: + // print nothing + break; + } +#ifdef CONFIG_S390_TAPE_BLOCK + tapeblock_medium_change(device); +#endif + device->medium_state = newstate; + wake_up(&device->state_change_wq); +} + +static void +tape_timeout_io(unsigned long data) +{ + struct tape_request *request; + struct tape_device *device; + unsigned long flags; + + request = (struct tape_request *) data; + device = request->device; + + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + if(request->callback != NULL) { + DBF_EVENT(3, "TAPE(%04x): %s timeout\n", + device->devstat.devno, tape_op_verbose[request->op]); + PRINT_ERR("TAPE(%04x): %s timeout\n", + device->devstat.devno, tape_op_verbose[request->op]); + + if(__tape_halt_io(device, request) == 0) + DBF_EVENT(6, "tape_timeout_io: success\n"); + else { + DBF_EVENT(2, "tape_timeout_io: halt_io failed\n"); + PRINT_ERR("tape_timeout_io: halt_io failed\n"); + } + request->rc = -EIO; + + /* Remove from request queue. */ + __tape_remove_request(device, request); + + /* Start next request. */ + if (!list_empty(&device->req_queue)) + tape_schedule_bh(device); + } + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); + tape_put_request(request); +} + +/* + * DEVFS Functions + */ +#ifdef CONFIG_DEVFS_FS +devfs_handle_t tape_devfs_root_entry; + +/* + * Create devfs root entry (devno in hex) for device td + */ +static int +tape_mkdevfsroot (struct tape_device* device) +{ + char devno [5]; + + sprintf(devno, "%04x", device->devinfo.devno); + device->devfs_dir = devfs_mk_dir(tape_devfs_root_entry, devno, device); + return (device->devfs_dir == NULL) ? -ENOMEM : 0; +} + +/* + * Remove devfs root entry for a device + */ +static void +tape_rmdevfsroot (struct tape_device *device) +{ + if (device->devfs_dir) { + devfs_unregister(device->devfs_dir); + device->devfs_dir = NULL; + } +} +#endif + +/* + * Enable tape device + */ +int +tape_enable_device(struct tape_device *device, + struct tape_discipline *discipline) +{ + int rc; + + if (!TAPE_INIT(device)) + return -EINVAL; + + /* Register IRQ. */ + rc = s390_request_irq_special(device->devinfo.irq, __tape_do_irq, + tape_noper_handler, 0, + TAPE_MAGIC, &device->devstat); + if (rc) + return rc; + + s390_set_private_data(device->devinfo.irq, tape_clone_device(device)); + + device->discipline = discipline; + + /* Let the discipline have a go at the device. */ + rc = discipline->setup_device(device); + if (rc) { + s390_set_private_data(device->devinfo.irq, NULL); + tape_put_device(device); + free_irq(device->devinfo.irq, &device->devstat); + return rc; + } + +#ifdef CONFIG_DEVFS_FS + /* Create devfs entries */ + rc = tape_mkdevfsroot(device); + if (rc){ + PRINT_WARN ("Cannot create a devfs directory for " + "device %04x\n", device->devinfo.devno); + device->discipline->cleanup_device(device); + s390_set_private_data(device->devinfo.irq, NULL); + tape_put_device(device); + free_irq(device->devinfo.irq, &device->devstat); + return rc; + } +#endif + rc = tapechar_setup_device(device); + if (rc) { +#ifdef CONFIG_DEVFS_FS + tape_rmdevfsroot(device); +#endif + device->discipline->cleanup_device(device); + s390_set_private_data(device->devinfo.irq, NULL); + tape_put_device(device); + free_irq(device->devinfo.irq, &device->devstat); + return rc; + } +#ifdef CONFIG_S390_TAPE_BLOCK + rc = tapeblock_setup_device(device); + if (rc) { + tapechar_cleanup_device(device); +#ifdef CONFIG_DEVFS_FS + tape_rmdevfsroot(device); +#endif + device->discipline->cleanup_device(device); + s390_set_private_data(device->devinfo.irq, NULL); + tape_put_device(device); + free_irq(device->devinfo.irq, &device->devstat); + return rc; + } +#endif + + TAPE_CLEAR_STATE(device, TAPE_STATUS_INIT); + + return 0; +} + +/* + * Disable tape device. Check if there is a running request and + * terminate it. Post all queued requests with -EIO. + */ +void +tape_disable_device(struct tape_device *device) +{ + struct list_head *l, *n; + struct tape_request *request; + + spin_lock_irq(get_irq_lock(device->devinfo.irq)); + /* Post remaining requests with -EIO */ + list_for_each_safe(l, n, &device->req_queue) { + request = list_entry(l, struct tape_request, list); + if (request->status == TAPE_REQUEST_IN_IO) + __tape_halt_io(device, request); + + request->rc = -EIO; + __tape_remove_request(device, request); + } + + if (TAPE_ASSIGNED(device)) { + spin_unlock(get_irq_lock(device->devinfo.irq)); + if( + tape_unassign( + device, + TAPE_STATUS_ASSIGN_M|TAPE_STATUS_ASSIGN_A + ) == 0 + ) { + printk(KERN_WARNING "%04x: automatically unassigned\n", + device->devinfo.devno); + } + spin_lock_irq(get_irq_lock(device->devinfo.irq)); + } + + TAPE_SET_STATE(device, TAPE_STATUS_NOT_OPER); + spin_unlock_irq(get_irq_lock(device->devinfo.irq)); + + s390_set_private_data(device->devinfo.irq, NULL); + tape_put_device(device); + +#ifdef CONFIG_S390_TAPE_BLOCK + tapeblock_cleanup_device(device); +#endif + tapechar_cleanup_device(device); +#ifdef CONFIG_DEVFS_FS + tape_rmdevfsroot(device); +#endif + device->discipline->cleanup_device(device); + device->discipline = NULL; + free_irq(device->devinfo.irq, &device->devstat); +} + +/* + * Find discipline by cu_type. + */ +struct tape_discipline * +tape_get_discipline(int cu_type) +{ + struct list_head *l; + struct tape_discipline *discipline, *tmp; + + discipline = NULL; + spin_lock(&tape_discipline_lock); + list_for_each(l, &tape_disciplines) { + tmp = list_entry(l, struct tape_discipline, list); + if (tmp->cu_type == cu_type) { + discipline = tmp; + break; + } + } + if (discipline->owner != NULL) { + if (!try_inc_mod_count(discipline->owner)) + /* Discipline is currently unloaded! */ + discipline = NULL; + } + spin_unlock(&tape_discipline_lock); + return discipline; +} + +/* + * Decrement usage count for discipline. + */ +void +tape_put_discipline(struct tape_discipline *discipline) +{ + spin_lock(&tape_discipline_lock); + if (discipline->owner) + __MOD_DEC_USE_COUNT(discipline->owner); + spin_unlock(&tape_discipline_lock); +} + +/* + * Register backend discipline + */ +int +tape_register_discipline(struct tape_discipline *discipline) +{ + if (!try_inc_mod_count(THIS_MODULE)) + /* Tape module is currently unloaded! */ + return -ENOSYS; + spin_lock(&tape_discipline_lock); + list_add_tail(&discipline->list, &tape_disciplines); + spin_unlock(&tape_discipline_lock); + /* Now add the tape devices with matching cu_type. */ + tape_add_devices(discipline); + return 0; +} + +/* + * Unregister backend discipline + */ +void +__tape_unregister_discipline(struct tape_discipline *discipline) +{ + list_del(&discipline->list); + /* Remove tape devices with matching cu_type. */ + tape_remove_devices(discipline); + MOD_DEC_USE_COUNT; +} + +void +tape_unregister_discipline(struct tape_discipline *discipline) +{ + struct list_head *l; + + spin_lock(&tape_discipline_lock); + list_for_each(l, &tape_disciplines) { + if (list_entry(l, struct tape_discipline, list) == discipline){ + __tape_unregister_discipline(discipline); + break; + } + } + spin_unlock(&tape_discipline_lock); +} + +/* + * Allocate a new tape ccw request + */ +struct tape_request * +tape_alloc_request(int cplength, int datasize) +{ + struct tape_request *request; + + if (datasize > PAGE_SIZE || (cplength*sizeof(ccw1_t)) > PAGE_SIZE) + BUG(); + + DBF_EVENT(5, "tape_alloc_request(%d,%d)\n", cplength, datasize); + + request = (struct tape_request *) + kmalloc(sizeof(struct tape_request), GFP_KERNEL); + if (request == NULL) { + DBF_EXCEPTION(1, "cqra nomem\n"); + return ERR_PTR(-ENOMEM); + } + memset(request, 0, sizeof(struct tape_request)); + INIT_LIST_HEAD(&request->list); + atomic_set(&request->ref_count, 1); + + /* allocate channel program */ + if (cplength > 0) { + request->cpaddr = + kmalloc(cplength*sizeof(ccw1_t), GFP_ATOMIC | GFP_DMA); + if (request->cpaddr == NULL) { + DBF_EXCEPTION(1, "cqra nomem\n"); + kfree(request); + return ERR_PTR(-ENOMEM); + } + memset(request->cpaddr, 0, cplength*sizeof(ccw1_t)); + } + /* alloc small kernel buffer */ + if (datasize > 0) { + request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA); + if (request->cpdata == NULL) { + DBF_EXCEPTION(1, "cqra nomem\n"); + if (request->cpaddr != NULL) + kfree(request->cpaddr); + kfree(request); + return ERR_PTR(-ENOMEM); + } + memset(request->cpdata, 0, datasize); + } + + DBF_EVENT(5, "request=%p(%p/%p)\n", request, request->cpaddr, + request->cpdata); + + return request; +} + +/* + * Free tape ccw request + */ +void +tape_free_request (struct tape_request * request) +{ + DBF_EVENT(5, "tape_free_request(%p)\n", request); + + if (request->device != NULL) { + tape_put_device(request->device); + request->device = NULL; + } + if (request->cpdata != NULL) { + kfree(request->cpdata); + } + if (request->cpaddr != NULL) { + kfree(request->cpaddr); + } + kfree(request); +} + +struct tape_request * +tape_clone_request(struct tape_request *request) +{ + DBF_EVENT(5, "tape_clone_request(%p) = %i\n", request, + atomic_inc_return(&request->ref_count)); + return request; +} + +struct tape_request * +tape_put_request(struct tape_request *request) +{ + int remain; + + DBF_EVENT(4, "tape_put_request(%p)\n", request); + if((remain = atomic_dec_return(&request->ref_count)) > 0) { + DBF_EVENT(5, "remaining = %i\n", remain); + } else { + tape_free_request(request); + } + + return NULL; +} + +/* + * Write sense data to console/dbf + */ +void +tape_dump_sense(struct tape_device* device, struct tape_request *request) +{ + devstat_t *stat; + unsigned int *sptr; + + stat = &device->devstat; + PRINT_INFO("-------------------------------------------------\n"); + PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n", + stat->dstat, stat->cstat, stat->cpa); + PRINT_INFO("DEVICE: %04x\n", device->devinfo.devno); + if (request != NULL) + PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]); + + sptr = (unsigned int *) stat->ii.sense.data; + PRINT_INFO("Sense data: %08X %08X %08X %08X \n", + sptr[0], sptr[1], sptr[2], sptr[3]); + PRINT_INFO("Sense data: %08X %08X %08X %08X \n", + sptr[4], sptr[5], sptr[6], sptr[7]); + PRINT_INFO("--------------------------------------------------\n"); +} + +/* + * Write sense data to dbf + */ +void +tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request) +{ + devstat_t *stat = &device->devstat; + unsigned int *sptr; + const char* op; + + if (request != NULL) + op = tape_op_verbose[request->op]; + else + op = "---"; + DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n", stat->dstat,stat->cstat); + DBF_EVENT(3, "DEVICE: %04x OP\t: %s\n", device->devinfo.devno,op); + sptr = (unsigned int *) stat->ii.sense.data; + DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]); + DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]); + DBF_EVENT(3, "%08x %08x\n", sptr[4], sptr[5]); + DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]); +} + +static inline int +__tape_do_io(struct tape_device *device, struct tape_request *request) +{ + /* Some operations may happen even on an unused tape device */ + switch(request->op) { + case TO_MSEN: + case TO_ASSIGN: + case TO_UNASSIGN: + case TO_BREAKASS: + break; + default: + if (!TAPE_OPEN(device)) + return -ENODEV; + } + + /* Add reference to device to the request. This increases the reference + count. */ + request->device = tape_clone_device(device); + request->status = TAPE_REQUEST_QUEUED; + + list_add_tail(&request->list, &device->req_queue); + __tape_process_queue(device); + + return 0; +} + +/* + * Add the request to the request queue, try to start it if the + * tape is idle. Return without waiting for end of i/o. + */ +int +tape_do_io_async(struct tape_device *device, struct tape_request *request) +{ + int rc; + long flags; + + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + /* Add request to request queue and try to start it. */ + rc = __tape_do_io(device, request); + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); + return rc; +} + +/* + * tape_do_io/__tape_wake_up + * Add the request to the request queue, try to start it if the + * tape is idle and wait uninterruptible for its completion. + */ +static void +__tape_wake_up(struct tape_request *request, void *data) +{ + request->callback = NULL; + wake_up((wait_queue_head_t *) data); +} + +int +tape_do_io(struct tape_device *device, struct tape_request *request) +{ + wait_queue_head_t wq; + long flags; + int rc; + + DBF_EVENT(5, "tape: tape_do_io(%p, %p)\n", device, request); + + init_waitqueue_head(&wq); + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + /* Setup callback */ + request->callback = __tape_wake_up; + request->callback_data = &wq; + /* Add request to request queue and try to start it. */ + rc = __tape_do_io(device, request); + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); + if (rc) + return rc; + /* Request added to the queue. Wait for its completion. */ + wait_event(wq, (request->callback == NULL)); + /* Get rc from request */ + return request->rc; +} + +/* + * tape_do_io_interruptible/__tape_wake_up_interruptible + * Add the request to the request queue, try to start it if the + * tape is idle and wait uninterruptible for its completion. + */ +static void +__tape_wake_up_interruptible(struct tape_request *request, void *data) +{ + request->callback = NULL; + wake_up_interruptible((wait_queue_head_t *) data); +} + +int +tape_do_io_interruptible(struct tape_device *device, + struct tape_request *request) +{ + wait_queue_head_t wq; + long flags; + int rc; + + DBF_EVENT(5, "tape: tape_do_io_int(%p, %p)\n", device, request); + + init_waitqueue_head(&wq); + // debug paranoia + if(!device) BUG(); + if(!request) BUG(); + + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + /* Setup callback */ + request->callback = __tape_wake_up_interruptible; + request->callback_data = &wq; + rc = __tape_do_io(device, request); + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); + if (rc) + return rc; + /* Request added to the queue. Wait for its completion. */ + rc = wait_event_interruptible(wq, (request->callback == NULL)); + if (rc != -ERESTARTSYS) + /* Request finished normally. */ + return request->rc; + /* Interrupted by a signal. We have to stop the current request. */ + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + rc = __tape_halt_io(device, request); + if (rc == 0) { + DBF_EVENT(3, "IO stopped on irq %d\n", device->devinfo.irq); + rc = -ERESTARTSYS; + } + spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); + return rc; +} + + +/* + * Tape interrupt routine, called from Ingo's I/O layer + */ +static void +__tape_do_irq (int irq, void *ds, struct pt_regs *regs) +{ + struct tape_device *device; + struct tape_request *request; + devstat_t *devstat; + int final; + int rc; + + devstat = (devstat_t *) ds; + device = (struct tape_device *) s390_get_private_data(irq); + if (device == NULL) { + PRINT_ERR("could not get device structure for irq %d " + "in interrupt\n", irq); + return; + } + request = (struct tape_request *) devstat->intparm; + + DBF_EVENT(5, "tape: __tape_do_irq(%p, %p)\n", device, request); + + if(request && request->timeout.expires) { + /* + * If the timer was not yet startet the reference to the + * request has to be dropped here. Otherwise it will be + * dropped by the timeout handler. + */ + if(del_timer(&request->timeout) > 0) + request->timeout.data = (unsigned long) + tape_put_request(request); + } + + if (device->devstat.cstat & SCHN_STAT_INCORR_LEN) + DBF_EVENT(4, "tape: incorrect blocksize\n"); + + if (device->devstat.dstat != 0x0c){ + /* + * Any request that does not come back with channel end + * and device end is unusual. Log the sense data. + */ + DBF_EVENT(3,"-- Tape Interrupthandler --\n"); + tape_dump_sense_dbf(device, request); + } + if (TAPE_NOT_OPER(device)) { + DBF_EVENT(6, "tape:device is not operational\n"); + return; + } + + /* Some status handling */ + if(devstat && devstat->dstat & DEV_STAT_UNIT_CHECK) { + unsigned char *sense = devstat->ii.sense.data; + + if(!(sense[1] & SENSE_DRIVE_ONLINE)) + device->tape_generic_status &= ~GMT_ONLINE(~0); + } else { + device->tape_generic_status |= GMT_ONLINE(~0); + } + + rc = device->discipline->irq(device, request); + /* + * rc < 0 : request finished unsuccessfully. + * rc == TAPE_IO_SUCCESS: request finished successfully. + * rc == TAPE_IO_PENDING: request is still running. Ignore rc. + * rc == TAPE_IO_RETRY: request finished but needs another go. + * rc == TAPE_IO_STOP: request needs to get terminated. + */ + final = 0; + switch (rc) { + case TAPE_IO_SUCCESS: + final = 1; + break; + case TAPE_IO_PENDING: + break; + case TAPE_IO_RETRY: +#ifdef CONFIG_S390_TAPE_BLOCK + if (request->op == TO_BLOCK) + device->discipline->check_locate(device, request); +#endif + rc = __do_IO(device, request); + if (rc) { + DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc); + final = 1; + } + break; + case TAPE_IO_STOP: + __tape_halt_io(device, request); + rc = -EIO; + final = 1; + break; + default: + if (rc > 0) { + DBF_EVENT(6, "xunknownrc\n"); + PRINT_ERR("Invalid return code from discipline " + "interrupt function.\n"); + rc = -EIO; + } + final = 1; + break; + } + if (final) { + /* This might be an unsolicited interrupt (no request) */ + if(request != NULL) { + /* Set ending status. */ + request->rc = rc; + __tape_remove_request(device, request); + } + /* Start next request. */ + if (!list_empty(&device->req_queue)) + tape_schedule_bh(device); + } +} + +/* + * Lock a shared tape for our exclusive use. + */ +int +tape_assign(struct tape_device *device, int type) +{ + int rc; + + spin_lock_irq(&device->assign_lock); + + /* The device is already assigned */ + rc = 0; + if (!TAPE_ASSIGNED(device)) { + rc = device->discipline->assign(device); + + spin_lock(get_irq_lock(device->devinfo.irq)); + if (rc) { + PRINT_WARN( + "(%04x): assign failed - " + "device might be busy\n", + device->devstat.devno); + DBF_EVENT(3, + "(%04x): assign failed " + "- device might be busy\n", + device->devstat.devno); + TAPE_SET_STATE(device, TAPE_STATUS_BOXED); + } else { + DBF_EVENT(3, "(%04x): assign lpum = %02x\n", + device->devstat.devno, device->devstat.lpum); + tape_state_set( + device, + (device->tape_status | type) & + (~TAPE_STATUS_BOXED) + ); + } + } else { + spin_lock(get_irq_lock(device->devinfo.irq)); + TAPE_SET_STATE(device, type); + } + spin_unlock(get_irq_lock(device->devinfo.irq)); + spin_unlock_irq(&device->assign_lock); + + return rc; +} + +/* + * Unlock a shared tape. + */ +int +tape_unassign(struct tape_device *device, int type) +{ + int rc; + + spin_lock_irq(&device->assign_lock); + + rc = 0; + spin_lock(get_irq_lock(device->devinfo.irq)); + if (!TAPE_ASSIGNED(device)) { + spin_unlock(get_irq_lock(device->devinfo.irq)); + spin_unlock_irq(&device->assign_lock); + return 0; + } + TAPE_CLEAR_STATE(device, type); + spin_unlock(get_irq_lock(device->devinfo.irq)); + + if (!TAPE_ASSIGNED(device)) { + rc = device->discipline->unassign(device); + if (rc) { + PRINT_WARN("(%04x): unassign failed\n", + device->devstat.devno); + DBF_EVENT(3, "(%04x): unassign failed\n", + device->devstat.devno); + } else { + DBF_EVENT(3, "(%04x): unassign lpum = %02x\n", + device->devstat.devno, device->devstat.lpum); + } + } + + spin_unlock_irq(&device->assign_lock); + return rc; +} + +/* + * Tape device open function used by tape_char & tape_block frontends. + */ +int +tape_open(struct tape_device *device) +{ + int rc; + + spin_lock_irq(&tape_discipline_lock); + spin_lock(get_irq_lock(device->devinfo.irq)); + if (TAPE_NOT_OPER(device)) { + DBF_EVENT(6, "TAPE:nodev\n"); + rc = -ENODEV; + } else if (TAPE_OPEN(device)) { + DBF_EVENT(6, "TAPE:dbusy\n"); + rc = -EBUSY; + } else if (device->discipline != NULL && + !try_inc_mod_count(device->discipline->owner)) { + DBF_EVENT(6, "TAPE:nodisc\n"); + rc = -ENODEV; + } else { + TAPE_SET_STATE(device, TAPE_STATUS_OPEN); + rc = 0; + } + spin_unlock(get_irq_lock(device->devinfo.irq)); + spin_unlock_irq(&tape_discipline_lock); + return rc; +} + +/* + * Tape device release function used by tape_char & tape_block frontends. + */ +int +tape_release(struct tape_device *device) +{ + spin_lock_irq(&tape_discipline_lock); + spin_lock(get_irq_lock(device->devinfo.irq)); + + if (TAPE_OPEN(device)) { + TAPE_CLEAR_STATE(device, TAPE_STATUS_OPEN); + + if (device->discipline->owner) + __MOD_DEC_USE_COUNT(device->discipline->owner); + } + spin_unlock(get_irq_lock(device->devinfo.irq)); + spin_unlock_irq(&tape_discipline_lock); + + return 0; +} + +/* + * Execute a magnetic tape command a number of times. + */ +int +tape_mtop(struct tape_device *device, int mt_op, int mt_count) +{ + tape_mtop_fn fn; + int rc; + + DBF_EVENT(6, "TAPE:mtio\n"); + DBF_EVENT(6, "TAPE:ioop: %x\n", mt_op); + DBF_EVENT(6, "TAPE:arg: %x\n", mt_count); + + if (mt_op < 0 || mt_op >= TAPE_NR_MTOPS) + return -EINVAL; + fn = device->discipline->mtop_array[mt_op]; + if(fn == NULL) + return -EINVAL; + + /* We assume that the backends can handle count up to 500. */ + if (mt_op == MTBSR || mt_op == MTFSR || mt_op == MTFSF || + mt_op == MTBSF || mt_op == MTFSFM || mt_op == MTBSFM) { + rc = 0; + for (; mt_count > 500; mt_count -= 500) + if ((rc = fn(device, 500)) != 0) + break; + if (rc == 0) + rc = fn(device, mt_count); + } else + rc = fn(device, mt_count); + return rc; + +} + +void +tape_init_disciplines(void) +{ +#ifdef CONFIG_S390_TAPE_34XX + tape_34xx_init(); +#endif +#ifdef CONFIG_S390_TAPE_34XX_MODULE + request_module("tape_34xx"); +#endif + +#ifdef CONFIG_S390_TAPE_3590 + tape_3590_init(); +#endif +#ifdef CONFIG_S390_TAPE_3590_MODULE + request_module("tape_3590"); +#endif + tape_auto_detect(); +} + +/* + * Tape init function. + */ +static int +tape_init (void) +{ + tape_dbf_area = debug_register ( "tape", 1, 2, 4*sizeof(long)); + debug_register_view(tape_dbf_area, &debug_sprintf_view); + debug_set_level(tape_dbf_area, 6); /* FIXME */ + DBF_EVENT(3, "tape init: ($Revision: 1.6 $)\n"); +#ifdef CONFIG_DEVFS_FS + tape_devfs_root_entry = devfs_mk_dir (NULL, "tape", NULL); +#endif /* CONFIG_DEVFS_FS */ + DBF_EVENT(3, "dev detect\n"); + /* Parse the parameters. */ + tape_devmap_init(); +#ifdef CONFIG_PROC_FS + tape_proc_init(); +#endif /* CONFIG_PROC_FS */ + tapechar_init(); +#ifdef CONFIG_S390_TAPE_BLOCK + tapeblock_init(); +#endif + tape_init_disciplines(); + return 0; +} + +/* + * Tape exit function. + */ +void +tape_exit(void) +{ + struct list_head *l, *n; + struct tape_discipline *discipline; + + DBF_EVENT(6, "tape exit\n"); + + /* Cleanup registered disciplines. */ + spin_lock(&tape_discipline_lock); + list_for_each_safe(l, n, &tape_disciplines) { + discipline = list_entry(l, struct tape_discipline, list); + __tape_unregister_discipline(discipline); + } + spin_unlock(&tape_discipline_lock); + + /* Get rid of the frontends */ + tapechar_exit(); +#ifdef CONFIG_S390_TAPE_BLOCK + tapeblock_exit(); +#endif +#ifdef CONFIG_PROC_FS + tape_proc_cleanup(); +#endif + tape_devmap_exit(); +#ifdef CONFIG_DEVFS_FS + devfs_unregister (tape_devfs_root_entry); /* devfs checks for NULL */ +#endif /* CONFIG_DEVFS_FS */ + debug_unregister (tape_dbf_area); +} + +/* + * Issue an hotplug event + */ +void tape_hotplug_event(struct tape_device *device, int devmaj, int action) { +#ifdef CONFIG_HOTPLUG + char *argv[3]; + char *envp[8]; + char devno[20]; + char major[20]; + char minor[20]; + + sprintf(devno, "DEVNO=%04x", device->devinfo.devno); + sprintf(major, "MAJOR=%d", devmaj); + sprintf(minor, "MINOR=%d", device->first_minor); + + argv[0] = hotplug_path; + argv[1] = "tape"; + argv[2] = NULL; + + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + switch(action) { + case TAPE_HOTPLUG_CHAR_ADD: + case TAPE_HOTPLUG_BLOCK_ADD: + envp[2] = "ACTION=add"; + break; + case TAPE_HOTPLUG_CHAR_REMOVE: + case TAPE_HOTPLUG_BLOCK_REMOVE: + envp[2] = "ACTION=remove"; + break; + default: + BUG(); + } + switch(action) { + case TAPE_HOTPLUG_CHAR_ADD: + case TAPE_HOTPLUG_CHAR_REMOVE: + envp[3] = "INTERFACE=char"; + break; + case TAPE_HOTPLUG_BLOCK_ADD: + case TAPE_HOTPLUG_BLOCK_REMOVE: + envp[3] = "INTERFACE=block"; + break; + } + envp[4] = devno; + envp[5] = major; + envp[6] = minor; + envp[7] = NULL; + + call_usermodehelper(argv[0], argv, envp); +#endif +} + +MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and " + "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)"); +MODULE_DESCRIPTION("Linux on zSeries channel attached " + "tape device driver ($Revision: 1.6 $)"); + +module_init(tape_init); +module_exit(tape_exit); + +EXPORT_SYMBOL(tape_dbf_area); +EXPORT_SYMBOL(tape_state_string); +EXPORT_SYMBOL(tape_op_verbose); +EXPORT_SYMBOL(tape_state_set); +EXPORT_SYMBOL(tape_med_state_set); +EXPORT_SYMBOL(tape_register_discipline); +EXPORT_SYMBOL(tape_unregister_discipline); +EXPORT_SYMBOL(tape_alloc_request); +EXPORT_SYMBOL(tape_put_request); +EXPORT_SYMBOL(tape_clone_request); +EXPORT_SYMBOL(tape_dump_sense); +EXPORT_SYMBOL(tape_dump_sense_dbf); +EXPORT_SYMBOL(tape_do_io); +EXPORT_SYMBOL(tape_do_io_free); +EXPORT_SYMBOL(tape_do_io_async); +EXPORT_SYMBOL(tape_do_io_interruptible); +EXPORT_SYMBOL(tape_mtop); +EXPORT_SYMBOL(tape_hotplug_event); + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tapedefs.h linux.21rc1-ac2/drivers/s390/char/tapedefs.h --- linux.21rc1/drivers/s390/char/tapedefs.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tapedefs.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,76 +0,0 @@ -/*********************************************************************** - * drivers/s390/char/tapedefs.h - * tape device driver for S/390 and zSeries tapes. - * - * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation - * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - * - *********************************************************************** - */ - -/* Kernel Version Compatibility section */ -#include -#include -#include -#include - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17)) -#define TAPE_DEBUG // use s390 debug feature -#else -#undef TAPE_DEBUG // debug feature not supported by our 2.2.16 code -static inline void set_normalized_cda ( ccw1_t * cp, unsigned long address ) { - cp -> cda = address; -} -static inline void clear_normalized_cda ( ccw1_t * ccw ) { - ccw -> cda = 0; -} -#define BUG() PRINT_FATAL("tape390: CRITICAL INTERNAL ERROR OCCURED. REPORT THIS BACK TO LINUX390@DE.IBM.COM\n") -#endif -#define CONFIG_S390_TAPE_DYNAMIC // allow devices to be attached or detached on the fly -#define TAPEBLOCK_RETRIES 20 // number of retries, when a block-dev request fails. - - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) -#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \ -do { \ - blk_dev[d_major].queue = d_queue_fn; \ -} while(0) -static inline struct request * -tape_next_request( request_queue_t *queue ) -{ - return blkdev_entry_next_request(&queue->queue_head); -} -static inline void -tape_dequeue_request( request_queue_t * q, struct request *req ) -{ - blkdev_dequeue_request (req); -} -#else -#define s390_dev_info_t dev_info_t -typedef struct request *request_queue_t; -#ifndef init_waitqueue_head -#define init_waitqueue_head(x) do { *x = NULL; } while(0) -#endif -#define blk_init_queue(x,y) do {} while(0) -#define blk_queue_headactive(x,y) do {} while(0) -#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \ -do { \ - blk_dev[d_major].request_fn = d_request_fn; \ - blk_dev[d_major].queue = d_queue_fn; \ - blk_dev[d_major].current_request = d_current; \ -} while(0) -static inline struct request * -tape_next_request( request_queue_t *queue ) -{ - return *queue; -} -static inline void -tape_dequeue_request( request_queue_t * q, struct request *req ) -{ - *q = req->next; - req->next = NULL; -} -#endif diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_devmap.c linux.21rc1-ac2/drivers/s390/char/tape_devmap.c --- linux.21rc1/drivers/s390/char/tape_devmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_devmap.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,894 @@ +/* + * drivers/s390/char/tape_devmap.c + * device mapping for tape device driver + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Michael Holzheu + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + * + * Device mapping and tape= parameter parsing functions. All devmap + * functions may not be called from interrupt context. In particular + * tape_get_device is a no-no from interrupt context. + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* This is ugly... */ +#define PRINTK_HEADER "tape_devmap:" + +#include "tape.h" + +struct tape_devmap { + struct list_head list; + unsigned int devindex; + unsigned short devno; + devreg_t devreg; + struct tape_device *device; +}; + +struct tape_discmap { + struct list_head list; + devreg_t devreg; + struct tape_discipline *discipline; +}; + +/* + * List of all registered tapes and disciplines. + */ +static struct list_head tape_devreg_list = LIST_HEAD_INIT(tape_devreg_list); +static struct list_head tape_disc_devreg_list = LIST_HEAD_INIT(tape_disc_devreg_list); +int tape_max_devindex = 0; + +/* + * Single spinlock to protect devmap structures and lists. + */ +static spinlock_t tape_devmap_lock = SPIN_LOCK_UNLOCKED; + +/* + * Module/Kernel Parameter Handling. The syntax of tape= is: + * : (0x)?[0-9a-fA-F]+ + * : (-)? + * : (,)* + */ +int tape_autodetect = 0; /* is true, when autodetection is active */ + +/* + * char *tape[] is intended to hold the ranges supplied by the tape= statement + * it is named 'tape' to directly be filled by insmod with the comma separated + * strings when running as a module. + */ +static char *tape[256]; +MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s"); + +#ifndef MODULE +/* + * The parameter parsing functions for builtin-drivers are called + * before kmalloc works. Store the pointers to the parameters strings + * into tape[] for later processing. + */ +static int __init +tape_call_setup (char *str) +{ + static int count = 0; + + if (count < 256) + tape[count++] = str; + return 1; +} + +__setup("tape=", tape_call_setup); +#endif /* not defined MODULE */ + +/* + * Add a range of devices and create the corresponding devreg_t + * structures. The order of the ranges added by this function + * will define the kdevs for the individual devices. + */ +int +tape_add_range(int from, int to) +{ + struct tape_devmap *devmap, *tmp; + struct list_head *l; + int devno; + + if (from > to) { + PRINT_ERR("Invalid device range %04x-%04x", from, to); + return -EINVAL; + } + spin_lock(&tape_devmap_lock); + for (devno = from; devno <= to; devno++) { + devmap = NULL; + list_for_each(l, &tape_devreg_list) { + tmp = list_entry(l, struct tape_devmap, list); + if (tmp->devno == devno) { + devmap = tmp; + break; + } + } + if (devmap == NULL) { + /* This devno is new. */ + devmap = (struct tape_devmap *) + kmalloc(sizeof(struct tape_devmap), + GFP_KERNEL); + if (devmap == NULL) + return -ENOMEM; + memset(devmap, 0, sizeof(struct tape_devmap)); + devmap->devno = devno; + devmap->devindex = tape_max_devindex++; + list_add(&devmap->list, &tape_devreg_list); + devmap->devreg.ci.devno = devno; + devmap->devreg.flag = DEVREG_TYPE_DEVNO; + devmap->devreg.oper_func = tape_oper_handler; + s390_device_register(&devmap->devreg); + } + } + spin_unlock(&tape_devmap_lock); + return 0; +} + +/* + * Read device number from string. The number is always is hex, + * a leading 0x is accepted (and has to be removed for simple_stroul + * to work). + */ +static inline int +tape_devno(char *str, char **endp) +{ + /* remove leading '0x' */ + if (*str == '0') { + str++; + if (*str == 'x') + str++; + } + if (!isxdigit(*str)) + return -EINVAL; + return simple_strtoul(str, endp, 16); /* interpret anything as hex */ +} + +/* + * Parse Kernel/Module Parameters and create devregs for dynamic attach/detach + */ +static int +tape_parm_parse (char *str) +{ + int from, to, rc; + + while (1) { + to = from = tape_devno(str, &str); + if (*str == '-') { + str++; + to = tape_devno(str, &str); + } + /* Negative numbers in from/to indicate errors. */ + if (from >= 0 && to >= 0) { + rc = tape_add_range(from, to); + if (rc) + return rc; + } + if (*str != ',') + break; + str++; + } + if (*str != '\0') { + PRINT_WARN("junk at end of tape parameter string: %s\n", str); + return -EINVAL; + } + return 0; +} + +/* + * Parse parameters stored in tape[]. + */ +static int +tape_parse(void) +{ + int rc, i; + + if (*tape == NULL) { + /* No parameters present */ + PRINT_INFO ("No parameters supplied, enabling auto detect " + "mode for all supported devices.\n"); + tape_autodetect = 1; + return 0; + } + PRINT_INFO("Using ranges supplied in parameters, " + "disabling auto detect mode.\n"); + rc = 0; + for (i = 0; i < 256; i++) { + if (tape[i] == NULL) + break; + rc = tape_parm_parse(tape[i]); + if (rc) { + PRINT_ERR("Invalid tape parameter found.\n"); + break; + } + } + return rc; +} + +/* + * Create a devreg for a discipline. This is only done if no explicit + * tape range is given. The tape_oper_handler will call tape_add_range + * for each device that appears. + */ +static int +tape_add_disc_devreg(struct tape_discipline *discipline) +{ + struct tape_discmap *discmap; + + discmap = (struct tape_discmap *) kmalloc(sizeof(struct tape_discmap), + GFP_KERNEL); + if (discmap == NULL) { + PRINT_WARN("Could not alloc devreg: Out of memory\n" + "Dynamic attach/detach will not work!\n"); + return -ENOMEM; + } + spin_lock(&tape_devmap_lock); + discmap->devreg.ci.hc.ctype = discipline->cu_type; + discmap->devreg.flag = DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS; + discmap->devreg.oper_func = tape_oper_handler; + s390_device_register(&discmap->devreg); + list_add(&discmap->list, &tape_disc_devreg_list); + spin_unlock(&tape_devmap_lock); + return 0; +} + +/* + * Free devregs for a discipline. + */ +static void +tape_del_disc_devreg(struct tape_discipline *discipline) +{ + struct list_head *l; + struct tape_discmap *discmap; + + spin_lock(&tape_devmap_lock); + list_for_each(l, &tape_disc_devreg_list) { + discmap = list_entry(l, struct tape_discmap, list); + if (discmap->discipline == discipline) { + s390_device_unregister(&discmap->devreg); + list_del(&discmap->list); + kfree(discmap); + break; + } + } + spin_unlock(&tape_devmap_lock); +} + + +/* + * Forget all about device numbers and disciplines. + * This may only be called at module unload or system shutdown. + */ +static void +tape_forget_devregs(void) +{ + struct list_head *l, *n; + struct tape_devmap *devmap; + struct tape_discmap *discmap; + + spin_lock(&tape_devmap_lock); + list_for_each_safe(l, n, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + if (devmap->device != NULL) + BUG(); + s390_device_unregister(&devmap->devreg); + list_del(&devmap->list); + kfree(devmap); + } + list_for_each_safe(l, n, &tape_disc_devreg_list) { + discmap = list_entry(l, struct tape_discmap, list); + s390_device_unregister(&discmap->devreg); + list_del(&discmap->list); + kfree(discmap); + } + spin_unlock(&tape_devmap_lock); +} + +/* + * Allocate memory for a new device structure. + */ +static struct tape_device * +tape_alloc_device(void) +{ + struct tape_device *device; + + device = (struct tape_device *) + kmalloc(sizeof(struct tape_device), GFP_KERNEL); + if (device == NULL) { + DBF_EXCEPTION(2, "ti:no mem\n"); + PRINT_INFO ("can't allocate memory for " + "tape info structure\n"); + return ERR_PTR(-ENOMEM); + } + memset(device, 0, sizeof(struct tape_device)); + device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA); + if (device->modeset_byte == NULL) { + DBF_EXCEPTION(2, "ti:no mem\n"); + PRINT_INFO("can't allocate memory for modeset byte\n"); + kfree(device); + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&device->req_queue); + init_waitqueue_head(&device->state_change_wq); + spin_lock_init(&device->assign_lock); + atomic_set(&device->ref_count, 1); + TAPE_SET_STATE(device, TAPE_STATUS_INIT); + device->medium_state = MS_UNKNOWN; + *device->modeset_byte = 0; + + return device; +} + +/* + * Create a device structure. + */ +static struct tape_device * +tape_create_device(int devno) +{ + struct list_head *l; + struct tape_devmap *devmap, *tmp; + struct tape_device *device; + int rc; + + DBF_EVENT(4, "tape_create_device(0x%04x)\n", devno); + + device = tape_alloc_device(); + if (IS_ERR(device)) + return device; + /* Get devinfo from the common io layer. */ + rc = get_dev_info_by_devno(devno, &device->devinfo); + if (rc) { + tape_put_device(device); + return ERR_PTR(rc); + } + spin_lock(&tape_devmap_lock); + devmap = NULL; + list_for_each(l, &tape_devreg_list) { + tmp = list_entry(l, struct tape_devmap, list); + if (tmp->devno == devno) { + devmap = tmp; + break; + } + } + if (devmap != NULL && devmap->device == NULL) { + devmap->device = tape_clone_device(device); + device->first_minor = devmap->devindex * TAPE_MINORS_PER_DEV; + } else if (devmap == NULL) { + /* devno not in tape range. */ + DBF_EVENT(4, "No devmap for entry 0x%04x\n", devno); + tape_put_device(device); + device = ERR_PTR(-ENODEV); + } else { + /* Should not happen. */ + DBF_EVENT(4, "A devmap entry for 0x%04x already exists\n", + devno); + tape_put_device(device); + device = ERR_PTR(-EEXIST); + } + spin_unlock(&tape_devmap_lock); + + return device; +} + +struct tape_device * +tape_clone_device(struct tape_device *device) +{ + DBF_EVENT(4, "tape_clone_device(%p) = %i\n", device, + atomic_inc_return(&device->ref_count)); + return device; +} + +/* + * Find tape device by a device index. + */ +struct tape_device * +tape_get_device(int devindex) +{ + struct list_head *l; + struct tape_devmap *devmap; + struct tape_device *device; + + DBF_EVENT(5, "tape_get_device(%i)\n", devindex); + + device = ERR_PTR(-ENODEV); + spin_lock(&tape_devmap_lock); + /* Find devmap for device with device number devno. */ + list_for_each(l, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + if (devmap->devindex == devindex) { + if (devmap->device != NULL) { + device = tape_clone_device(devmap->device); + } + break; + } + } + spin_unlock(&tape_devmap_lock); + return device; +} + +/* + * Find tape handle by a devno. + */ +struct tape_device * +tape_get_device_by_devno(int devno) +{ + struct list_head *l; + struct tape_devmap *devmap; + struct tape_device *device; + + DBF_EVENT(5, "tape_get_device_by_devno(0x%04x)\n", devno); + + device = ERR_PTR(-ENODEV); + spin_lock(&tape_devmap_lock); + + list_for_each(l, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + if(devmap->device != NULL && devmap->devno == devno) { + device = tape_clone_device(devmap->device); + break; + } + } + spin_unlock(&tape_devmap_lock); + + return device; +} + +/* + * Find tape handle by a device irq. + */ +struct tape_device * +tape_get_device_by_irq(int irq) +{ + struct list_head *l; + struct tape_devmap *devmap; + struct tape_device *device; + + DBF_EVENT(5, "tape_get_device_by_irq(0x%02x)\n", irq); + + device = ERR_PTR(-ENODEV); + spin_lock(&tape_devmap_lock); + /* Find devmap for device with device number devno. */ + list_for_each(l, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + if (devmap->device != NULL && + devmap->device->devinfo.irq == irq) { + device = tape_clone_device(devmap->device); + break; + } + } + spin_unlock(&tape_devmap_lock); + return device; +} + +/* + * Decrease the reference counter of a devices structure. If the + * reference counter reaches zero free the device structure and + * wake up sleepers. + */ +void +tape_put_device(struct tape_device *device) +{ + int remain; + + DBF_EVENT(4, "tape_put_device(%p)\n", device); + + if ((remain = atomic_dec_return(&device->ref_count)) > 0) { + DBF_EVENT(5, "remaining = %i\n", remain); + return; + } + + /* + * Reference counter dropped to zero. This means + * that the device is deleted and the last user + * of the device structure is gone. That is what + * tape_delete_device is waiting for. Do a wake up. + */ + if(remain < 0) { + PRINT_ERR("put device without reference\n"); + return; + } + + /* + * Free memory of a device structure. + */ + kfree(device->modeset_byte); + kfree(device); +} + +/* + * Scan the device range for devices with matching cu_type, create + * their device structures and enable them. + */ +void +tape_add_devices(struct tape_discipline *discipline) +{ + struct list_head *l; + struct tape_devmap *devmap; + struct tape_device *device; + + /* + * Scan tape devices for matching cu type. + */ + list_for_each(l, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + device = tape_create_device(devmap->devno); + if (IS_ERR(device)) + continue; + + if (device->devinfo.sid_data.cu_type == discipline->cu_type) { + DBF_EVENT(4, "tape_add_devices(%p)\n", discipline); + DBF_EVENT(4, "det irq: %x\n", device->devinfo.irq); + DBF_EVENT(4, "cu : %x\n", discipline->cu_type); + tape_enable_device(device, discipline); + } else { + devmap->device = NULL; + tape_put_device(device); + } + tape_put_device(device); + } + if (tape_autodetect) + tape_add_disc_devreg(discipline); +} + +/* + * Scan the device range for devices with matching cu_type, disable them + * and remove their device structures. + */ +void +tape_remove_devices(struct tape_discipline *discipline) +{ + struct list_head *l; + struct tape_devmap *devmap; + struct tape_device *device; + + if (tape_autodetect) + tape_del_disc_devreg(discipline); + /* + * Go through our tape info list and disable, deq and free + * all devices with matching discipline + */ + list_for_each(l, &tape_devreg_list) { + devmap = list_entry(l, struct tape_devmap, list); + device = devmap->device; + if (device == NULL) + continue; + if (device->discipline == discipline) { + tape_disable_device(device); + tape_put_device(device); + devmap->device = NULL; + } + } +} + +/* + * Auto detect tape devices. + */ +void +tape_auto_detect(void) +{ + struct tape_device *device; + struct tape_discipline *discipline; + s390_dev_info_t dinfo; + int irq, devno; + + if (!tape_autodetect) + return; + for (irq = get_irq_first(); irq != -ENODEV; irq = get_irq_next(irq)) { + /* Get device info block. */ + devno = get_devno_by_irq(irq); + if (get_dev_info_by_irq(irq, &dinfo) < 0) + continue; + /* Search discipline with matching cu_type */ + discipline = tape_get_discipline(dinfo.sid_data.cu_type); + if (discipline == NULL) + continue; + DBF_EVENT(4, "tape_auto_detect()\n"); + DBF_EVENT(4, "det irq: %x\n", irq); + DBF_EVENT(4, "cu : %x\n", dinfo.sid_data.cu_type); + if (tape_add_range(dinfo.devno, dinfo.devno) == 0) { + device = tape_create_device(devno); + if (!IS_ERR(device)) { + tape_enable_device(device, discipline); + tape_put_device(device); + } + } + tape_put_discipline(discipline); + } +} + +/* + * Private task queue for oper/noper handling... + */ +static DECLARE_TASK_QUEUE(tape_cio_tasks); + +/* + * Oper Handler is called from Ingo's I/O layer when a new tape device is + * attached. + */ +static void +do_tape_oper_handler(void *data) +{ + struct { + int devno; + int cu_type; + struct tq_struct task; + } *p; + struct tape_device *device; + struct tape_discipline *discipline; + unsigned long flags; + + p = (void *) data; + + /* + * Handling the path revalidation scheme or common IO. Devices that + * were detected before will be reactivated. + */ + if(!IS_ERR(device = tape_get_device_by_devno(p->devno))) { + spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); + if (!TAPE_NOACCESS(device)) { + PRINT_ERR( + "Oper handler for irq %d called, " + "which is (still) internally used.\n", + device->devinfo.irq); + } else { + DBF_EVENT(3, + "T390(%04x): resume processing\n", + p->devno); + TAPE_CLEAR_STATE(device, TAPE_STATUS_NOACCESS); + tape_schedule_bh(device); + } + spin_unlock_irqrestore( + get_irq_lock(device->devinfo.irq), flags); + + tape_put_device(device); + kfree(p); + return; + } + + /* If we get here device is NULL. */ + if (tape_autodetect && tape_add_range(p->devno, p->devno) != 0) { + kfree(p); + return; + } + + /* Find discipline for this device. */ + discipline = tape_get_discipline(p->cu_type); + if (discipline == NULL) { + /* Strange. Should not happen. */ + kfree(p); + return; + } + + device = tape_create_device(p->devno); + if (IS_ERR(device)) { + tape_put_discipline(discipline); + kfree(p); + return; + } + tape_enable_device(device, discipline); + tape_put_device(device); + tape_put_discipline(discipline); + kfree(p); +} + +int +tape_oper_handler(int irq, devreg_t *devreg) +{ + struct { + int devno; + int cu_type; + struct tq_struct task; + } *p; + s390_dev_info_t dinfo; + int rc; + + rc = get_dev_info_by_irq (irq, &dinfo); + if (rc < 0) + return rc; + + /* No memory, we loose. */ + if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + p->devno = dinfo.devno; + p->cu_type = dinfo.sid_data.cu_type; + memset(&p->task, 0, sizeof(struct tq_struct)); + p->task.routine = do_tape_oper_handler; + p->task.data = p; + + /* queue call to do_oper_handler. */ + queue_task(&p->task, &tape_cio_tasks); + run_task_queue(&tape_cio_tasks); + + return 0; +} + + +/* + * Not Oper Handler is called from Ingo's IO layer, when a tape device + * is detached. + */ +static void +do_tape_noper_handler(void *data) +{ + struct { + int irq; + int status; + struct tq_struct task; + } *p; + struct tape_device *device; + struct list_head *l; + struct tape_devmap *devmap; + unsigned long flags; + + p = data; + + /* + * find out devno of leaving device: CIO has already deleted + * this information so we need to find it by irq! + */ + device = tape_get_device_by_irq(p->irq); + if (IS_ERR(device)) { + kfree(p); + return; + } + + /* + * Handle the new path revalidation scheme of the common IO layer. + */ + switch(p->status) { + case DEVSTAT_DEVICE_GONE: + case DEVSTAT_REVALIDATE: /* FIXME: What to do? */ + tape_disable_device(device); + + /* + * Remove the device reference from the device map. + */ + spin_lock(&tape_devmap_lock); + list_for_each(l, &tape_devreg_list) { + devmap = list_entry( + l, struct tape_devmap, list + ); + if (devmap->device == device) { + tape_put_device(device); + devmap->device = NULL; + break; + } + } + spin_unlock(&tape_devmap_lock); + break; + case DEVSTAT_NOT_ACC: + /* + * Device shouldn't be accessed at the moment. The + * currently running request will complete. + */ + spin_lock_irqsave( + get_irq_lock(device->devinfo.irq), flags + ); + DBF_EVENT(3, "T390(%04x): suspend processing\n", + device->devinfo.devno); + TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS); + spin_unlock_irqrestore( + get_irq_lock(device->devinfo.irq), flags + ); + break; + case DEVSTAT_NOT_ACC_ERR: { + struct tape_request *request; + + /* + * Device shouldn't be accessed at the moment. The + * request that was running is lost. + */ + spin_lock_irqsave( + get_irq_lock(device->devinfo.irq), flags + ); + + request = list_entry(device->req_queue.next, + struct tape_request, list); + if( + !list_empty(&device->req_queue) + && + request->status == TAPE_REQUEST_IN_IO + ) { + /* Argh! Might better belong to tape_core.c */ + list_del(&request->list); + request->rc = -EIO; + request->status = TAPE_REQUEST_DONE; + if (request->callback != NULL) { + request->callback( + request, + request->callback_data + ); + request->callback = NULL; + } + } + DBF_EVENT(3, "T390(%04x): suspend processing\n", + device->devinfo.devno); + DBF_EVENT(3, "T390(%04x): request lost\n", + device->devinfo.devno); + TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS); + spin_unlock_irqrestore( + get_irq_lock(device->devinfo.irq), flags + ); + break; + } + default: + PRINT_WARN("T390(%04x): no operation handler called " + "with unknown status(0x%x)\n", + device->devinfo.devno, p->status); + tape_disable_device(device); + + /* + * Remove the device reference from the device map. + */ + spin_lock(&tape_devmap_lock); + list_for_each(l, &tape_devreg_list) { + devmap = list_entry( + l, struct tape_devmap, list + ); + if (devmap->device == device) { + tape_put_device(device); + devmap->device = NULL; + break; + } + } + spin_unlock(&tape_devmap_lock); + } + + tape_put_device(device); + kfree(p); +} + +void +tape_noper_handler(int irq, int status) +{ + struct { + int irq; + int status; + struct tq_struct task; + } *p; + + /* No memory, we loose. */ + if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + return; + + p->irq = irq; + memset(&p->task, 0, sizeof(struct tq_struct)); + p->task.routine = do_tape_noper_handler; + p->task.data = p; + + /* queue call to do_oper_handler. */ + queue_task(&p->task, &tape_cio_tasks); + run_task_queue(&tape_cio_tasks); +} + + +int +tape_devmap_init(void) +{ + return tape_parse(); +} + +void +tape_devmap_exit(void) +{ + tape_forget_devregs(); +} + +EXPORT_SYMBOL(tape_get_device); +EXPORT_SYMBOL(tape_get_device_by_irq); +EXPORT_SYMBOL(tape_get_device_by_devno); +EXPORT_SYMBOL(tape_put_device); +EXPORT_SYMBOL(tape_clone_device); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape.h linux.21rc1-ac2/drivers/s390/char/tape.h --- linux.21rc1/drivers/s390/char/tape.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape.h 2003-04-25 13:53:58.000000000 +0100 @@ -1,203 +1,436 @@ -/*************************************************************************** - * +/* * drivers/s390/char/tape.h - * tape device driver for 3480/3490E tapes. + * tape device driver for 3480/3490E/3590 tapes. * * S390 and zSeries version - * Copyright (C) 2001 IBM Corporation + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Carsten Otte - * Tuan Ngo-Anh - * - **************************************************************************** + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader */ #ifndef _TAPE_H - #define _TAPE_H + #include #include - -#define MAX_TAPES 7 /* Max tapes supported is 7*/ -#define TAPE_MAGIC 0xE3C1D7C5 /* is ebcdic-"TAPE" */ - -typedef enum { - TS_UNUSED=0, TS_IDLE, TS_DONE, TS_FAILED, - TS_BLOCK_INIT, - TS_BSB_INIT, - TS_BSF_INIT, - TS_DSE_INIT, - TS_EGA_INIT, - TS_FSB_INIT, - TS_FSF_INIT, - TS_LDI_INIT, - TS_LBL_INIT, - TS_MSE_INIT, - TS_NOP_INIT, - TS_RBA_INIT, - TS_RBI_INIT, - TS_RBU_INIT, - TS_RBL_INIT, - TS_RDC_INIT, - TS_RFO_INIT, - TS_RSD_INIT, - TS_REW_INIT, - TS_REW_RELEASE_INIT, - TS_RUN_INIT, - TS_SEN_INIT, - TS_SID_INIT, - TS_SNP_INIT, - TS_SPG_INIT, - TS_SWI_INIT, - TS_SMR_INIT, - TS_SYN_INIT, - TS_TIO_INIT, - TS_UNA_INIT, - TS_WRI_INIT, - TS_WTM_INIT, - TS_NOT_OPER, - TS_SIZE } tape_stat; - -struct _tape_info_t; //Forward declaration - -typedef enum { - TE_START=0, TE_DONE, TE_FAILED, TE_ERROR, TE_OTHER, - TE_SIZE } tape_events; - -typedef void (*tape_disc_shutdown_t) (int); -typedef void (*tape_event_handler_t) (struct _tape_info_t*); -typedef ccw_req_t* (*tape_ccwgen_t)(struct _tape_info_t* ti,int count); -typedef ccw_req_t* (*tape_reqgen_t)(struct request* req,struct _tape_info_t* ti,int tapeblock_major); -typedef ccw_req_t* (*tape_rwblock_t)(const char* data,size_t count,struct _tape_info_t* ti); -typedef void (*tape_freeblock_t)(ccw_req_t* cqr,struct _tape_info_t* ti); -typedef void (*tape_setup_assist_t) (struct _tape_info_t*); +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef CONFIG_DEVFS_FS -typedef void (*tape_devfs_handler_t) (struct _tape_info_t*); +#include #endif -typedef tape_event_handler_t tape_event_table_t[TS_SIZE][TE_SIZE]; -typedef struct _tape_discipline_t { - unsigned int cu_type; - tape_setup_assist_t setup_assist; - tape_event_handler_t error_recovery; - tape_reqgen_t bread; - tape_freeblock_t free_bread; - tape_rwblock_t write_block; - tape_freeblock_t free_write_block; - tape_rwblock_t read_block; - tape_freeblock_t free_read_block; - tape_ccwgen_t mtfsf; - tape_ccwgen_t mtbsf; - tape_ccwgen_t mtfsr; - tape_ccwgen_t mtbsr; - tape_ccwgen_t mtweof; - tape_ccwgen_t mtrew; - tape_ccwgen_t mtoffl; - tape_ccwgen_t mtnop; - tape_ccwgen_t mtbsfm; - tape_ccwgen_t mtfsfm; - tape_ccwgen_t mteom; - tape_ccwgen_t mterase; - tape_ccwgen_t mtsetdensity; - tape_ccwgen_t mtseek; - tape_ccwgen_t mttell; - tape_ccwgen_t mtsetdrvbuffer; - tape_ccwgen_t mtlock; - tape_ccwgen_t mtunlock; - tape_ccwgen_t mtload; - tape_ccwgen_t mtunload; - tape_ccwgen_t mtcompression; - tape_ccwgen_t mtsetpart; - tape_ccwgen_t mtmkpart; - tape_ccwgen_t mtiocget; - tape_ccwgen_t mtiocpos; - tape_disc_shutdown_t shutdown; - int (*discipline_ioctl_overload)(struct inode *,struct file*, unsigned int,unsigned long); - tape_event_table_t* event_table; - tape_event_handler_t default_handler; - struct _tape_info_t* tape; /* pointer for backreference */ - void* next; -} tape_discipline_t __attribute__ ((aligned(8))); -typedef struct _tape_frontend_t { - tape_setup_assist_t device_setup; +/* + * macros s390 debug feature (dbf) + */ +#define DBF_EVENT(d_level, d_str...) \ +do { \ + debug_sprintf_event(tape_dbf_area, d_level, d_str); \ +} while (0) + +#define DBF_EXCEPTION(d_level, d_str...) \ +do { \ + debug_sprintf_exception(tape_dbf_area, d_level, d_str); \ +} while (0) + +#define TAPE_VERSION_MAJOR 2 +#define TAPE_VERSION_MINOR 0 +#define TAPE_MAGIC "tape" + +#define TAPE_MINORS_PER_DEV 2 /* two minors per device */ +#define TAPEBLOCK_HSEC_SIZE 2048 +#define TAPEBLOCK_HSEC_S2B 2 +#define TAPEBLOCK_RETRIES 5 + +/* Event types for hotplug */ +#define TAPE_HOTPLUG_CHAR_ADD 1 +#define TAPE_HOTPLUG_BLOCK_ADD 2 +#define TAPE_HOTPLUG_CHAR_REMOVE 3 +#define TAPE_HOTPLUG_BLOCK_REMOVE 4 + +enum tape_medium_state { + MS_UNKNOWN, + MS_LOADED, + MS_UNLOADED, + MS_SIZE +}; + +enum tape_op { + TO_BLOCK, /* Block read */ + TO_BSB, /* Backward space block */ + TO_BSF, /* Backward space filemark */ + TO_DSE, /* Data security erase */ + TO_FSB, /* Forward space block */ + TO_FSF, /* Forward space filemark */ + TO_LBL, /* Locate block label */ + TO_NOP, /* No operation */ + TO_RBA, /* Read backward */ + TO_RBI, /* Read block information */ + TO_RFO, /* Read forward */ + TO_REW, /* Rewind tape */ + TO_RUN, /* Rewind and unload tape */ + TO_WRI, /* Write block */ + TO_WTM, /* Write tape mark */ + TO_MSEN, /* Medium sense */ + TO_LOAD, /* Load tape */ + TO_READ_CONFIG, /* Read configuration data */ + TO_READ_ATTMSG, /* Read attention message */ + TO_DIS, /* Tape display */ + TO_ASSIGN, /* Assign tape to channel path */ + TO_UNASSIGN, /* Unassign tape from channel path */ + TO_BREAKASS, /* Break the assignment of another host */ + TO_SIZE /* #entries in tape_op_t */ +}; + +/* Forward declaration */ +struct tape_device; + +/* The tape device list lock */ +extern rwlock_t tape_dev_lock; + +/* Tape CCW request */ +struct tape_request { + struct list_head list; /* list head for request queueing. */ + struct tape_device *device; /* tape device of this request */ + ccw1_t *cpaddr; /* address of the channel program. */ + void *cpdata; /* pointer to ccw data. */ + char status; /* status of this request */ + int options; /* options for execution. */ + int retries; /* retry counter for error recovery. */ + + /* + * This timer can be used to automatically cancel a request after + * some time. Specifically the assign request seems to lockup under + * certain circumstances. + */ + struct timer_list timeout; + + enum tape_op op; + int rc; + atomic_t ref_count; + + /* Callback for delivering final status. */ + void (*callback)(struct tape_request *, void *); + void *callback_data; +}; + +/* tape_request->status can be: */ +#define TAPE_REQUEST_INIT 0x00 /* request is ready to be processed */ +#define TAPE_REQUEST_QUEUED 0x01 /* request is queued to be processed */ +#define TAPE_REQUEST_IN_IO 0x02 /* request is currently in IO */ +#define TAPE_REQUEST_DONE 0x03 /* request is completed. */ + +/* Function type for magnetic tape commands */ +typedef int (*tape_mtop_fn)(struct tape_device *, int); + +/* Size of the array containing the mtops for a discipline */ +#define TAPE_NR_MTOPS (MTMKPART+1) + +/* Tape Discipline */ +struct tape_discipline { + struct list_head list; + struct module *owner; + unsigned int cu_type; + int (*setup_device)(struct tape_device *); + void (*cleanup_device)(struct tape_device *); + int (*assign)(struct tape_device *); + int (*unassign)(struct tape_device *); + int (*force_unassign)(struct tape_device *); + int (*irq)(struct tape_device *, struct tape_request *); + struct tape_request *(*read_block)(struct tape_device *, size_t); + struct tape_request *(*write_block)(struct tape_device *, size_t); + void (*process_eov)(struct tape_device*); + /* Block device stuff. */ + struct tape_request *(*bread)(struct tape_device *, struct request *); + void (*check_locate)(struct tape_device *, struct tape_request *); + void (*free_bread)(struct tape_request *); + /* ioctl function for additional ioctls. */ + int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long); + /* Array of tape commands with TAPE_NR_MTOPS entries */ + tape_mtop_fn *mtop_array; +}; + +/* + * The discipline irq function either returns an error code (<0) which + * means that the request has failed with an error or one of the following: + */ +#define TAPE_IO_SUCCESS 0 /* request successful */ +#define TAPE_IO_PENDING 1 /* request still running */ +#define TAPE_IO_RETRY 2 /* retry to current request */ +#define TAPE_IO_STOP 3 /* stop the running request */ + +/* Char Frontend Data */ +struct tape_char_data { + /* Idal buffer to temporaily store character data */ + struct idal_buffer * idal_buf; + /* Block size (in bytes) of the character device (0=auto) */ + int block_size; +#ifdef CONFIG_DEVFS_FS + /* tape//char subdirectory in devfs */ + devfs_handle_t devfs_char_dir; + /* tape//char/nonrewinding entry in devfs */ + devfs_handle_t devfs_nonrewinding; + /* tape//char/rewinding entry in devfs */ + devfs_handle_t devfs_rewinding; +#endif /* CONFIG_DEVFS_FS */ +}; + +#ifdef CONFIG_S390_TAPE_BLOCK +/* Block Frontend Data */ +struct tape_blk_data +{ + /* Block device request queue. */ + request_queue_t request_queue; + /* Block frontend tasklet */ + struct tasklet_struct tasklet; + /* Current position on the tape. */ + unsigned int block_position; + /* The start of the block device image file */ + unsigned int start_block_id; #ifdef CONFIG_DEVFS_FS - tape_devfs_handler_t mkdevfstree; - tape_devfs_handler_t rmdevfstree; + /* tape//block subdirectory in devfs */ + devfs_handle_t devfs_block_dir; + /* tape//block/disc entry in devfs */ + devfs_handle_t devfs_disc; +#endif /* CONFIG_DEVFS_FS */ +}; #endif - void* next; -} tape_frontend_t __attribute__ ((aligned(8))); +#define TAPE_STATUS_INIT 0x00000001 +#define TAPE_STATUS_ASSIGN_M 0x00000002 +#define TAPE_STATUS_ASSIGN_A 0x00000004 +#define TAPE_STATUS_OPEN 0x00000008 +#define TAPE_STATUS_BLOCKDEV 0x00000010 +#define TAPE_STATUS_BOXED 0x20000000 +#define TAPE_STATUS_NOACCESS 0x40000000 +#define TAPE_STATUS_NOT_OPER 0x80000000 + +#define TAPE_SET_STATE(td,st) \ + do { \ + tape_state_set(td, td->tape_status | (st)); \ + } while(0) +#define TAPE_CLEAR_STATE(td,st) \ + do { \ + tape_state_set(td, td->tape_status & ~(st)); \ + } while(0) + +#define TAPE_UNUSED(td) (!TAPE_OPEN(td)) +#define TAPE_INIT(td) (td->tape_status & TAPE_STATUS_INIT) +#define TAPE_ASSIGNED(td) ( \ + td->tape_status & ( \ + TAPE_STATUS_ASSIGN_M | \ + TAPE_STATUS_ASSIGN_A \ + ) \ + ) +#define TAPE_OPEN(td) (td->tape_status & TAPE_STATUS_OPEN) +#define TAPE_BLOCKDEV(td) (td->tape_status & TAPE_STATUS_BLOCKDEV) +#define TAPE_BOXED(td) (td->tape_status & TAPE_STATUS_BOXED) +#define TAPE_NOACCESS(td) (td->tape_status & TAPE_STATUS_NOACCESS) +#define TAPE_NOT_OPER(td) (td->tape_status & TAPE_STATUS_NOT_OPER) + +/* Tape Info */ +struct tape_device { + /* Device discipline information. */ + struct tape_discipline *discipline; + void * discdata; + + /* Generic status bits */ + long tape_generic_status; + unsigned int tape_status; + enum tape_medium_state medium_state; + + /* Number of tapemarks required for correct termination */ + int required_tapemarks; + + /* Waitqueue for state changes and device flags */ + wait_queue_head_t state_change_wq; + unsigned char * modeset_byte; + + /* Reference count. */ + atomic_t ref_count; + + /* For persistent assign */ + spinlock_t assign_lock; + + /* Request queue. */ + struct list_head req_queue; + atomic_t bh_scheduled; + struct tq_struct bh_task; + + /* Common i/o stuff. */ + s390_dev_info_t devinfo; + devstat_t devstat; + + /* each tape device has two minors */ + int first_minor; -typedef struct _tape_info_t { - wait_queue_head_t wq; - s390_dev_info_t devinfo; /* device info from Common I/O */ - int wanna_wakeup; - int rew_minor; /* minor number for the rewinding tape */ - int nor_minor; /* minor number for the nonrewinding tape */ - int blk_minor; /* minor number for the block device */ - devstat_t devstat; /* contains irq, devno, status */ - size_t block_size; /* block size of tape */ - int drive_type; /* Code indicating type of drive */ - struct file *rew_filp; /* backpointer to file structure */ - struct file *nor_filp; - struct file *blk_filp; - int tape_state; /* State of the device. See tape_stat */ - int rc; /* Return code. */ - tape_discipline_t* discipline; - request_queue_t request_queue; - struct request* current_request; - int blk_retries; - long position; - int medium_is_unloaded; // Becomes true when a unload-type operation was issued, false again when medium-insert was detected - ccw_req_t* cqr; - atomic_t bh_scheduled; - struct tq_struct bh_tq; #ifdef CONFIG_DEVFS_FS - devfs_handle_t devfs_dir; /* devfs handle for tape/DEVNO directory */ - devfs_handle_t devfs_char_dir; /* devfs handle for tape/DEVNO/char directory */ - devfs_handle_t devfs_block_dir; /* devfs handle for tape/DEVNO/block directory */ - devfs_handle_t devfs_nonrewinding; /* devfs handle for tape/DEVNO/char/nonrewinding device */ - devfs_handle_t devfs_rewinding; /* devfs handle for tape/DEVNO/char/rewinding device */ - devfs_handle_t devfs_disc; /* devfs handle for tape/DEVNO/block/disc device */ + /* Toplevel devfs directory. */ + devfs_handle_t devfs_dir; +#endif /* CONFIG_DEVFS_FS */ + /* Character device frontend data */ + struct tape_char_data char_data; +#ifdef CONFIG_S390_TAPE_BLOCK + /* Block dev frontend data */ + struct tape_blk_data blk_data; #endif - void* discdata; - void* kernbuf; - void* userbuf; - void* next; -} tape_info_t __attribute__ ((aligned(8))); +}; -/* tape initialisation functions */ -int tape_init(void); -int tape_setup (tape_info_t * ti, int irq, int minor); +/* Externals from tape_core.c */ +struct tape_request *tape_alloc_request(int cplength, int datasize); +struct tape_request *tape_put_request(struct tape_request *); +struct tape_request *tape_clone_request(struct tape_request *); +int tape_do_io(struct tape_device *, struct tape_request *); +int tape_do_io_async(struct tape_device *, struct tape_request *); +int tape_do_io_interruptible(struct tape_device *, struct tape_request *); +void tape_schedule_bh(struct tape_device *); +void tape_hotplug_event(struct tape_device *, int, int); + +static inline int +tape_do_io_free(struct tape_device *device, struct tape_request *request) +{ + int rc; + + rc = tape_do_io(device, request); + tape_put_request(request); + return rc; +} + +int tape_oper_handler(int irq, devreg_t *devreg); +void tape_noper_handler(int irq, int status); +int tape_open(struct tape_device *); +int tape_release(struct tape_device *); +int tape_assign(struct tape_device *, int type); +int tape_unassign(struct tape_device *, int type); +int tape_mtop(struct tape_device *, int, int); + +/* Externals from tape_devmap.c */ +int tape_devmap_init(void); +void tape_devmap_exit(void); + +struct tape_device *tape_get_device(int devindex); +struct tape_device *tape_get_device_by_devno(int devno); +struct tape_device *tape_clone_device(struct tape_device *); +void tape_put_device(struct tape_device *); + +void tape_auto_detect(void); +void tape_add_devices(struct tape_discipline *); +void tape_remove_devices(struct tape_discipline *); + +extern int tape_max_devindex; + +/* Externals from tape_char.c */ +int tapechar_init(void); +void tapechar_exit(void); +int tapechar_setup_device(struct tape_device *); +void tapechar_cleanup_device(struct tape_device *); + +/* Externals from tape_block.c */ +int tapeblock_init (void); +void tapeblock_exit(void); +int tapeblock_setup_device(struct tape_device *); +void tapeblock_cleanup_device(struct tape_device *); +void tapeblock_medium_change(struct tape_device *); + +/* Discipline functions */ +int tape_register_discipline(struct tape_discipline *); +void tape_unregister_discipline(struct tape_discipline *); +struct tape_discipline *tape_get_discipline(int cu_type); +void tape_put_discipline(struct tape_discipline *); +int tape_enable_device(struct tape_device *, struct tape_discipline *); +void tape_disable_device(struct tape_device *device); -/* functoins for alloc'ing ccw stuff */ -inline ccw_req_t * tape_alloc_ccw_req (tape_info_t* ti, int cplength, int datasize); -void tape_free_request (ccw_req_t * request); +/* tape initialisation functions */ +void tape_proc_init (void); +void tape_proc_cleanup (void); /* a function for dumping device sense info */ -void tape_dump_sense (devstat_t * stat); - -#ifdef CONFIG_S390_TAPE_DYNAMIC -/* functions for dyn. dev. attach/detach */ -int tape_oper_handler ( int irq, struct _devreg *dreg); -#endif +void tape_dump_sense(struct tape_device *, struct tape_request *); +void tape_dump_sense_dbf(struct tape_device *, struct tape_request *); /* functions for handling the status of a device */ -inline void tapestate_set (tape_info_t * ti, int newstate); -inline int tapestate_get (tape_info_t * ti); -void tapestate_event (tape_info_t * ti, int event); -extern char* state_verbose[TS_SIZE]; -extern char* event_verbose[TE_SIZE]; - -/****************************************************************************/ - -/* Some linked lists for storing plugins and devices */ -extern tape_info_t *first_tape_info; -extern tape_discipline_t *first_discipline; -extern tape_frontend_t *first_frontend; +inline void tape_state_set (struct tape_device *, unsigned int status); +inline void tape_med_state_set(struct tape_device *, enum tape_medium_state); +const char *tape_state_string(struct tape_device *); + +/* Tape 3480/3490 init/exit functions. */ +int tape_34xx_init(void); +void tape_34xx_exit(void); /* The debug area */ -#ifdef TAPE_DEBUG -extern debug_info_t *tape_debug_area; -#endif +extern debug_info_t *tape_dbf_area; + +/* functions for building ccws */ +static inline ccw1_t * +tape_ccw_cc(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda) +{ + ccw->cmd_code = cmd_code; + ccw->flags = CCW_FLAG_CC; + ccw->count = memsize; + ccw->cda = (__u32)(addr_t) cda; + return ccw + 1; +} + +static inline ccw1_t * +tape_ccw_end(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda) +{ + ccw->cmd_code = cmd_code; + ccw->flags = 0; + ccw->count = memsize; + ccw->cda = (__u32)(addr_t) cda; + return ccw + 1; +} + +static inline ccw1_t * +tape_ccw_cmd(ccw1_t *ccw, __u8 cmd_code) +{ + ccw->cmd_code = cmd_code; + ccw->flags = 0; + ccw->count = 0; + ccw->cda = (__u32)(addr_t) &ccw->cmd_code; + return ccw + 1; +} + +static inline ccw1_t * +tape_ccw_repeat(ccw1_t *ccw, __u8 cmd_code, int count) +{ + while (count-- > 0) { + ccw->cmd_code = cmd_code; + ccw->flags = CCW_FLAG_CC; + ccw->count = 0; + ccw->cda = (__u32)(addr_t) &ccw->cmd_code; + ccw++; + } + return ccw; +} + +extern inline ccw1_t* +tape_ccw_cc_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal) +{ + ccw->cmd_code = cmd_code; + ccw->flags = CCW_FLAG_CC; + idal_buffer_set_cda(idal, ccw); + return ccw++; +} + +extern inline ccw1_t* +tape_ccw_end_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal) +{ + ccw->cmd_code = cmd_code; + ccw->flags = 0; + idal_buffer_set_cda(idal, ccw); + return ccw++; +} + +/* Global vars */ +extern const char *tape_op_verbose[]; #endif /* for ifdef tape.h */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_proc.c linux.21rc1-ac2/drivers/s390/char/tape_proc.c --- linux.21rc1/drivers/s390/char/tape_proc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_proc.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,383 @@ +/* + * drivers/s390/char/tape.c + * tape device driver for S/390 and zSeries tapes. + * + * S390 and zSeries version + * Copyright (C) 2001 IBM Corporation + * Author(s): Carsten Otte + * Michael Holzheu + * Tuan Ngo-Anh + * + * PROCFS Functions + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tape.h" + +#define PRINTK_HEADER "T390:" + +static const char *tape_med_st_verbose[MS_SIZE] = +{ + [MS_UNKNOWN] = "UNKNOWN ", + [MS_LOADED] = "LOADED ", + [MS_UNLOADED] = "UNLOADED" +}; + +/* our proc tapedevices entry */ +static struct proc_dir_entry *tape_proc_devices; + +static int tape_proc_show(struct seq_file *m, void *v) { + struct tape_device *device; + struct tape_request *request; + unsigned long n; + + n = ((unsigned long) v - 1); + + if(n == 0) { + seq_printf(m, + "TapeNo\tDevNo\tCuType\tCuModel\tDevType\t" + "DevMod\tBlkSize\tState\tOp\tMedState\n" + ); + } + + device = tape_get_device(n); + if(IS_ERR(device)) + return 0; + + spin_lock_irq(get_irq_lock(device->devinfo.irq)); + + seq_printf(m, + "%d\t%04X\t%04X\t%02X\t%04X\t%02X\t", + device->first_minor/TAPE_MINORS_PER_DEV, + device->devinfo.devno, + device->devinfo.sid_data.cu_type, + device->devinfo.sid_data.cu_model, + device->devinfo.sid_data.dev_type, + device->devinfo.sid_data.dev_model + ); + + /* + * the blocksize is either 'auto' or the blocksize as a decimal number + */ + if(device->char_data.block_size == 0) + seq_printf(m, "auto\t"); + else + seq_printf(m, "%i\t", device->char_data.block_size); + + seq_printf(m, "%s\t", tape_state_string(device)); + + /* + * verbose desciption of current tape operation + */ + if(!list_empty(&device->req_queue)) { + request = list_entry( + device->req_queue.next, struct tape_request, list + ); + + seq_printf(m, "%s\t", tape_op_verbose[request->op]); + } else { + seq_printf(m, "---\t"); + } + + seq_printf(m, "%s\n", tape_med_st_verbose[device->medium_state]); + + spin_unlock_irq(get_irq_lock(device->devinfo.irq)); + tape_put_device(device); + + return 0; +} + +static void *tape_proc_start(struct seq_file *m, loff_t *pos) { + if(*pos < tape_max_devindex) + return (void *) ((unsigned long) (*pos) + 1); + return NULL; +} + +static void tape_proc_stop(struct seq_file *m, void *v) { +} + +static void *tape_proc_next(struct seq_file *m, void *v, loff_t *pos) { + (*pos)++; + return tape_proc_start(m, pos); +} + +static struct seq_operations tape_proc_seq = { + .start = tape_proc_start, + .next = tape_proc_next, + .stop = tape_proc_stop, + .show = tape_proc_show, +}; + +static int tape_proc_open(struct inode *inode, struct file *file) { + return seq_open(file, &tape_proc_seq); +} + +static int +tape_proc_assign(int devno) +{ + int rc; + struct tape_device *device; + + if(IS_ERR(device = tape_get_device_by_devno(devno))) { + DBF_EVENT(3, "TAPE(%04x): assign invalid device\n", devno); + PRINT_ERR("TAPE(%04x): assign invalid device\n", devno); + return PTR_ERR(device); + } + + rc = tape_assign(device, TAPE_STATUS_ASSIGN_M); + + tape_put_device(device); + + return rc; +} + +static int +tape_proc_unassign(int devno) +{ + int rc; + struct tape_device *device; + + if(IS_ERR(device = tape_get_device_by_devno(devno))) { + DBF_EVENT(3, "TAPE(%04x): unassign invalid device\n", devno); + PRINT_ERR("TAPE(%04x): unassign invalid device\n", devno); + return PTR_ERR(device); + } + + rc = tape_unassign(device, TAPE_STATUS_ASSIGN_M); + + tape_put_device(device); + + return rc; +} + +#ifdef SMB_DEBUG_BOX +static int +tape_proc_put_into_box(int devno) +{ + struct tape_device *device; + + if(IS_ERR(device = tape_get_device_by_devno(devno))) { + DBF_EVENT(3, "TAPE(%04x): invalid device\n", devno); + PRINT_ERR("TAPE(%04x): invalid device\n", devno); + return PTR_ERR(device); + } + + TAPE_SET_STATE(device, TAPE_STATUS_BOXED); + + tape_put_device(device); + + return 0; +} +#endif + +#ifdef TAPE390_FORCE_UNASSIGN +static int +tape_proc_force_unassign(int devno) +{ + int rc; + struct tape_device *device; + + if(IS_ERR(device = tape_get_device_by_devno(devno))) { + DBF_EVENT(3, "TAPE(%04x): force unassign invalid device\n", + devno); + PRINT_ERR("TAPE(%04x): force unassign invalid device\n", + devno); + return PTR_ERR(device); + } + + if (!TAPE_BOXED(device)) { + DBF_EVENT(3, "TAPE(%04x): forced unassignment only allowed for" + " boxed device\n", devno); + PRINT_ERR("TAPE(%04x): forced unassignment only allowed for" + " boxed device\n", devno); + rc = -EPERM; + } else if(device->discipline->force_unassign == NULL) { + DBF_EVENT(3, "TAPE(%04x: force unassign is not supported on" + " this device\n", devno); + PRINT_ERR("TAPE(%04x: force unassign is not supported on" + " this device\n", devno); + rc = -EPERM; + } else { + rc = device->discipline->force_unassign(device); + if(rc == 0) + spin_lock_irq(get_irq_lock(device->devinfo.irq)); + TAPE_CLEAR_STATE( + device, + TAPE_STATUS_BOXED + | TAPE_STATUS_ASSIGN_A + | TAPE_STATUS_ASSIGN_M + ); + spin_unlock_irq(get_irq_lock(device->devinfo.irq)); + } + + tape_put_device(device); + return rc; +} +#endif + +/* + * Skips over all characters to the position after a newline or beyond the + * last character of the string. + * Returns the number of characters skiped. + */ +static size_t +tape_proc_skip_eol(const char *buf, size_t len, loff_t *off) +{ + loff_t start = *off; + + while((*off - start) < len) { + if(*(buf+*off) == '\n') { + *off += 1; + break; + } + *off += 1; + } + + return (size_t) (*off - start); +} + +/* + * Skips over whitespace characters and returns the number of characters + * that where skiped. + */ +static size_t +tape_proc_skip_ws(const char *buf, size_t len, loff_t *off) +{ + loff_t start = *off; + + while((*off - start) < len) { + if(*(buf + *off) != ' ' && *(buf + *off) != '\t') + break; + *off += 1; + } + + return (size_t) (*off - start); +} + +static size_t +tape_proc_get_hexvalue(char *buf, size_t len, loff_t *off, unsigned int *hex) +{ + int hexdigit; + loff_t start = *off; + + /* Skip possible space characters */ + tape_proc_skip_ws(buf, len, off); + + /* The hexvalue might start with '0x' or '0X' */ + if((*off - start)+1 < len && *(buf + *off) == '0') + if(*(buf + *off + 1) == 'x' || *(buf + *off + 1) == 'X') + *off += 2; + + *hex = 0; + while((*off - start) < len) { + if(*(buf + *off) >= '0' && *(buf + *off) <= '9') { + hexdigit = *(buf + *off) - '0'; + } else if(*(buf + *off) >= 'a' && *(buf + *off) <= 'f') { + hexdigit = *(buf + *off) - 'a' + 10; + } else if(*(buf + *off) >= 'A' && *(buf + *off) <= 'F') { + hexdigit = *(buf + *off) - 'A' + 10; + } else { + break; + } + *hex = (*hex << 4) + hexdigit; + *off += 1; + } + + return (size_t) (*off - start); +} + +static ssize_t tape_proc_write( + struct file *file, + const char *buf, + size_t len, + loff_t *off +) { + loff_t start = *off; + int devno; + char *s; + + if(PAGE_SIZE < len) + return -EINVAL; + + if((s = kmalloc(len, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if(copy_from_user(s, buf, len) != 0) { + kfree(s); + return -EFAULT; + } + + if(strncmp(s+*off, "assign", 6) == 0) { + (*off) += 6; + tape_proc_get_hexvalue(s, len - 6, off, &devno); + if(devno > 0) + tape_proc_assign(devno); + } else if(strncmp(s+*off, "unassign", 8) == 0) { + (*off) += 8; + tape_proc_get_hexvalue(s, len - (*off - start), off, &devno); + if(devno > 0) + tape_proc_unassign(devno); +#ifdef TAPE390_FORCE_UNASSIGN + } else if(strncmp(s+*off, "forceunassign", 13) == 0) { + (*off) += 13; + tape_proc_get_hexvalue(s, len - (*off - start), off, &devno); + if(devno > 0) + tape_proc_force_unassign(devno); +#endif +#ifdef SMB_DEBUG_BOX + } else if(strncmp(s+*off, "putintobox", 10) == 0) { + (*off) += 10; + tape_proc_get_hexvalue(s, len - (*off - start), off, &devno); + if(devno > 0) + tape_proc_put_into_box(devno); +#endif + } else { + DBF_EVENT(3, "tape_proc_write() parse error\n"); + PRINT_ERR("Invalid /proc/tapedevices command.\n"); + } + tape_proc_skip_eol(s, len - (*off - start), off); + + kfree(s); + + /* Just pretend to have processed all the stuff */ + return len; +} + +static struct file_operations tape_proc_ops = +{ + .open = tape_proc_open, + .read = seq_read, + .write = tape_proc_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* + * Initialize procfs stuff on startup + */ +void tape_proc_init(void) { + tape_proc_devices = create_proc_entry( + "tapedevices", S_IFREG | S_IRUGO | S_IWUSR, &proc_root); + + if (tape_proc_devices == NULL) { + PRINT_WARN("tape: Cannot register procfs entry tapedevices\n"); + return; + } + tape_proc_devices->proc_fops = &tape_proc_ops; + tape_proc_devices->owner = THIS_MODULE; +} + +/* + * Cleanup all stuff registered to the procfs + */ +void tape_proc_cleanup(void) { + if(tape_proc_devices != NULL) + remove_proc_entry ("tapedevices", &proc_root); +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_std.c linux.21rc1-ac2/drivers/s390/char/tape_std.c --- linux.21rc1/drivers/s390/char/tape_std.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_std.c 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,777 @@ +/* + * drivers/s390/char/tape_std.c + * standard tape device functions for ibm tapes. + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Michael Holzheu + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_S390_TAPE_BLOCK +#include +#endif + +#include +#include +#include +#include + +#include "tape.h" +#include "tape_std.h" + +#define PRINTK_HEADER "T3xxx:" +#define ZLINUX_PASSWD "zLinux PWD" + +/* + * tape_std_assign + */ +int +tape_std_assign(struct tape_device *device) +{ + struct tape_request *request; + struct tape_ca_data *ca_data; + + request = tape_alloc_request(3, 23); + if (IS_ERR(request)) + return PTR_ERR(request); + + request->op = TO_ASSIGN; + ca_data = (struct tape_ca_data *) (((char *) request->cpdata) + 12); + + /* Password may be 11 characters long (including '\0'). */ + ca_data->function = 0x00; /* Set Password */ + strcpy(ca_data->password, ZLINUX_PASSWD); + ASCEBC(ca_data->password, 11); + + /* + * From the documentation assign requests should fail with the + * 'assigned elsewhere' bit set if the tape is already assigned + * to another host. However, it seems, in reality the request + * hangs forever. Therfor we just set a timeout for this request. + */ + init_timer(&request->timeout); + request->timeout.expires = jiffies + 1 * HZ; + + /* Setup the CCWs */ + tape_ccw_cc(request->cpaddr, ASSIGN, 11, request->cpdata); + // tape_ccw_cc(request->cpaddr + 1, CONTROL_ACCESS, 12, ca_data); + tape_ccw_cc(request->cpaddr + 1, NOP, 0, NULL); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + + return tape_do_io_free(device, request); +} + +/* + * tape_std_unassign + */ +int +tape_std_unassign (struct tape_device *device) +{ + struct tape_request *request; + + request = tape_alloc_request(2, 11); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_UNASSIGN; + tape_ccw_cc(request->cpaddr, UNASSIGN, 11, request->cpdata); + tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); + return tape_do_io_free(device, request); +} + +#ifdef TAPE390_FORCE_UNASSIGN +/* + * tape_std_force_unassign: forces assignment from another host. + * (Since we need a password this works only with other zLinux hosts!) + */ +int +tape_std_force_unassign(struct tape_device *device) +{ + struct tape_request *request; + struct tape_ca_data *ca_data1; + struct tape_ca_data *ca_data2; + + request = tape_alloc_request(2, 24); + if (IS_ERR(request)) + return PTR_ERR(request); + + request->op = TO_BREAKASS; + ca_data1 = (struct tape_ca_data *) + (((char *) request->cpdata)); + ca_data2 = (struct tape_ca_data *) + (((char *) request->cpdata) + 12); + + ca_data1->function = 0x80; /* Conditional enable */ + strcpy(ca_data1->password, ZLINUX_PASSWD); + ASCEBC(ca_data1->password, 11); + ca_data2->function = 0x40; /* Conditional disable */ + memcpy(ca_data2->password, ca_data1->password, 11); + + tape_ccw_cc(request->cpaddr, CONTROL_ACCESS, 12, ca_data1); + tape_ccw_end(request->cpaddr + 1, CONTROL_ACCESS, 12, ca_data2); + + return tape_do_io_free(device, request); +} +#endif + +/* + * TAPE390_DISPLAY: Show a string on the tape display. + */ +int +tape_std_display(struct tape_device *device, struct display_struct *disp) +{ + struct tape_request *request; + int rc; + + request = tape_alloc_request(2, 17); + if (IS_ERR(request)) { + DBF_EVENT(3, "TAPE: load display failed\n"); + return PTR_ERR(request); + } + + request->op = TO_DIS; + *(unsigned char *) request->cpdata = disp->cntrl; + DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl); + memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8); + memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8); + ASCEBC(((unsigned char*) request->cpdata) + 1, 16); + + tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata); + tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); + + rc = tape_do_io_interruptible(device, request); + tape_put_request(request); + return rc; +} + +/* + * Read block id. + */ +int +tape_std_read_block_id(struct tape_device *device, unsigned int *bid) +{ + struct tape_request *request; + struct { + unsigned int channel_block_id; + unsigned int device_block_id; + } __attribute__ ((packed)) *rbi_data; + int rc; + + request = tape_alloc_request(3, 8); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_RBI; + + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, READ_BLOCK_ID, 8, request->cpdata); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + + /* execute it */ + rc = tape_do_io(device, request); + if (rc == 0) { + /* Get result from read buffer. */ + DBF_EVENT(6, "rbi_data = 0x%08x%08x\n", + *((unsigned int *) request->cpdata), + *(((unsigned int *) request->cpdata)+1)); + rbi_data = (void *) request->cpdata; + *bid = rbi_data->channel_block_id; + } + tape_put_request(request); + return rc; +} + +/* Seek block id */ +int +tape_std_seek_block_id(struct tape_device *device, unsigned int bid) +{ + struct tape_request *request; + + request = tape_alloc_request(3, 4); + if (IS_ERR(request)) + return PTR_ERR(request); + + request->op = TO_LBL; + *(__u32 *) request->cpdata = bid; + + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + + /* execute it */ + return tape_do_io_free(device, request); +} + +int +tape_std_terminate_write(struct tape_device *device) +{ + int rc; + + if(device->required_tapemarks == 0) + return 0; + + DBF_EVENT(5, "(%04x): terminate_write %ixEOF\n", + device->devstat.devno, device->required_tapemarks); + + rc = tape_mtop(device, MTWEOF, device->required_tapemarks); + if (rc) + return rc; + + device->required_tapemarks = 0; + return tape_mtop(device, MTBSR, 1); +} + +/* + * MTLOAD: Loads the tape. + * The default implementation just wait until the tape medium state changes + * to MS_LOADED. + */ +int +tape_std_mtload(struct tape_device *device, int count) +{ + return wait_event_interruptible(device->state_change_wq, + (device->medium_state == MS_LOADED)); +} + +/* + * MTSETBLK: Set block size. + */ +int +tape_std_mtsetblk(struct tape_device *device, int count) +{ + struct idal_buffer *new; + + DBF_EVENT(6, "tape_std_mtsetblk(%d)\n", count); + if (count <= 0) { + /* + * Just set block_size to 0. tapechar_read/tapechar_write + * will realloc the idal buffer if a bigger one than the + * current is needed. + */ + device->char_data.block_size = 0; + return 0; + } + if (device->char_data.idal_buf != NULL && + device->char_data.idal_buf->size == count) + /* We already have a idal buffer of that size. */ + return 0; + /* Allocate a new idal buffer. */ + new = idal_buffer_alloc(count, 0); + if (new == NULL) + return -ENOMEM; + if (device->char_data.idal_buf != NULL) + idal_buffer_free(device->char_data.idal_buf); + + device->char_data.idal_buf = new; + device->char_data.block_size = count; + DBF_EVENT(6, "new blocksize is %d\n", device->char_data.block_size); + return 0; +} + +/* + * MTRESET: Set block size to 0. + */ +int +tape_std_mtreset(struct tape_device *device, int count) +{ + DBF_EVENT(6, "TCHAR:devreset:\n"); + device->char_data.block_size = 0; + return 0; +} + +/* + * MTFSF: Forward space over 'count' file marks. The tape is positioned + * at the EOT (End of Tape) side of the file mark. + */ +int +tape_std_mtfsf(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_FSF; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTFSR: Forward space over 'count' tape blocks (blocksize is set + * via MTSETBLK. + */ +int +tape_std_mtfsr(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_FSB; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, FORSPACEBLOCK, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTBSR: Backward space over 'count' tape blocks. + * (blocksize is set via MTSETBLK. + */ +int +tape_std_mtbsr(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_BSB; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, BACKSPACEBLOCK, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTWEOF: Write 'count' file marks at the current position. + */ +int +tape_std_mtweof(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_WTM; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, WRITETAPEMARK, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTBSFM: Backward space over 'count' file marks. + * The tape is positioned at the BOT (Begin Of Tape) side of the + * last skipped file mark. + */ +int +tape_std_mtbsfm(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_BSF; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTBSF: Backward space over 'count' file marks. The tape is positioned at + * the EOT (End of Tape) side of the last skipped file mark. + */ +int +tape_std_mtbsf(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + int rc; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_BSF; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + rc = tape_do_io(device, request); + if (rc == 0) { + request->op = TO_FSF; + /* need to skip forward over the filemark. */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, FORSPACEFILE, 0, NULL); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + /* execute it */ + rc = tape_do_io(device, request); + } + tape_put_request(request); + return rc; +} + +/* + * MTFSFM: Forward space over 'count' file marks. + * The tape is positioned at the BOT (Begin Of Tape) side + * of the last skipped file mark. + */ +int +tape_std_mtfsfm(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + ccw1_t *ccw; + int rc; + + request = tape_alloc_request(mt_count + 2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_FSF; + /* setup ccws */ + ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count); + ccw = tape_ccw_end(ccw, NOP, 0, NULL); + /* execute it */ + rc = tape_do_io(device, request); + if (rc == 0) { + request->op = TO_BSF; + /* need to skip forward over the filemark. */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, BACKSPACEFILE, 0, NULL); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + /* execute it */ + rc = tape_do_io(device, request); + } + tape_put_request(request); + return rc; +} + +/* + * MTREW: Rewind the tape. + */ +int +tape_std_mtrew(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + request = tape_alloc_request(3, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_REW; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, + device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTOFFL: Rewind the tape and put the drive off-line. + * Implement 'rewind unload' + */ +int +tape_std_mtoffl(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + request = tape_alloc_request(3, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_RUN; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL); + tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTNOP: 'No operation'. + */ +int +tape_std_mtnop(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + request = tape_alloc_request(2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_NOP; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTEOM: positions at the end of the portion of the tape already used + * for recordind data. MTEOM positions after the last file mark, ready for + * appending another file. + */ +int +tape_std_mteom(struct tape_device *device, int mt_count) +{ + int rc; + + /* + * Since there is currently no other way to seek, return to the + * BOT and start from there. + */ + if((rc = tape_mtop(device, MTREW, 1)) < 0) + return rc; + + do { + if((rc = tape_mtop(device, MTFSF, 1)) < 0) + return rc; + if((rc = tape_mtop(device, MTFSR, 1)) < 0) + return rc; + } while((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) == 0); + + return tape_mtop(device, MTBSR, 1); +} + +/* + * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind. + */ +int +tape_std_mtreten(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + int rc; + + request = tape_alloc_request(4, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_FSF; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1,FORSPACEFILE, 0, NULL); + tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL); + tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr); + /* execute it, MTRETEN rc gets ignored */ + rc = tape_do_io_interruptible(device, request); + tape_put_request(request); + return tape_std_mtrew(device, 1); +} + +/* + * MTERASE: erases the tape. + */ +int +tape_std_mterase(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + request = tape_alloc_request(5, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_DSE; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL); + tape_ccw_cc(request->cpaddr + 2, ERASE_GAP, 0, NULL); + tape_ccw_cc(request->cpaddr + 3, DATA_SEC_ERASE, 0, NULL); + tape_ccw_end(request->cpaddr + 4, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTUNLOAD: Rewind the tape and unload it. + */ +int +tape_std_mtunload(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + request = tape_alloc_request(3, 32); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_RUN; + /* setup ccws */ + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL); + tape_ccw_end(request->cpaddr + 2, SENSE, 32, request->cpdata); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * MTCOMPRESSION: used to enable compression. + * Sets the IDRC on/off. + */ +int +tape_std_mtcompression(struct tape_device *device, int mt_count) +{ + struct tape_request *request; + + if (mt_count < 0 || mt_count > 1) { + DBF_EXCEPTION(6, "xcom parm\n"); + if (*device->modeset_byte & 0x08) + PRINT_INFO("(%x) Compression is currently on\n", + device->devstat.devno); + else + PRINT_INFO("(%x) Compression is currently off\n", + device->devstat.devno); + PRINT_INFO("Use 1 to switch compression on, 0 to " + "switch it off\n"); + return -EINVAL; + } + request = tape_alloc_request(2, 0); + if (IS_ERR(request)) + return PTR_ERR(request); + request->op = TO_NOP; + /* setup ccws */ + *device->modeset_byte = (mt_count == 0) ? 0x00 : 0x08; + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); + /* execute it */ + return tape_do_io_free(device, request); +} + +/* + * Read Block + */ +struct tape_request * +tape_std_read_block(struct tape_device *device, size_t count) +{ + struct tape_request *request; + + /* + * We have to alloc 4 ccws in order to be able to transform request + * into a read backward request in error case. + */ + request = tape_alloc_request(4, 0); + if (IS_ERR(request)) { + DBF_EXCEPTION(6, "xrbl fail"); + return request; + } + request->op = TO_RFO; + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_end_idal(request->cpaddr + 1, READ_FORWARD, + device->char_data.idal_buf); + DBF_EVENT(6, "xrbl ccwg\n"); + return request; +} + +/* + * Read Block backward transformation function. + */ +void +tape_std_read_backward(struct tape_device *device, struct tape_request *request) +{ + /* + * We have allocated 4 ccws in tape_std_read, so we can now + * transform the request to a read backward, followed by a + * forward space block. + */ + request->op = TO_RBA; + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_cc_idal(request->cpaddr + 1, READ_BACKWARD, + device->char_data.idal_buf); + tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL); + tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL); + DBF_EVENT(6, "xrop ccwg");} + +/* + * Write Block + */ +struct tape_request * +tape_std_write_block(struct tape_device *device, size_t count) +{ + struct tape_request *request; + + request = tape_alloc_request(2, 0); + if (IS_ERR(request)) { + DBF_EXCEPTION(6, "xwbl fail\n"); + return request; + } + request->op = TO_WRI; + tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); + tape_ccw_end_idal(request->cpaddr + 1, WRITE_CMD, + device->char_data.idal_buf); + DBF_EVENT(6, "xwbl ccwg\n"); + return request; +} + +/* + * This routine is called by frontend after an ENOSP on write + */ +void +tape_std_process_eov(struct tape_device *device) +{ + /* + * End of volume: We have to backspace the last written record, then + * we TRY to write a tapemark and then backspace over the written TM + */ + if (tape_mtop(device, MTBSR, 1) < 0) + return; + if (tape_mtop(device, MTWEOF, 1) < 0) + return; + tape_mtop(device, MTBSR, 1); +} + +EXPORT_SYMBOL(tape_std_assign); +EXPORT_SYMBOL(tape_std_unassign); +#ifdef TAPE390_FORCE_UNASSIGN +EXPORT_SYMBOL(tape_std_force_unassign); +#endif +EXPORT_SYMBOL(tape_std_display); +EXPORT_SYMBOL(tape_std_read_block_id); +EXPORT_SYMBOL(tape_std_seek_block_id); +EXPORT_SYMBOL(tape_std_mtload); +EXPORT_SYMBOL(tape_std_mtsetblk); +EXPORT_SYMBOL(tape_std_mtreset); +EXPORT_SYMBOL(tape_std_mtfsf); +EXPORT_SYMBOL(tape_std_mtfsr); +EXPORT_SYMBOL(tape_std_mtbsr); +EXPORT_SYMBOL(tape_std_mtweof); +EXPORT_SYMBOL(tape_std_mtbsfm); +EXPORT_SYMBOL(tape_std_mtbsf); +EXPORT_SYMBOL(tape_std_mtfsfm); +EXPORT_SYMBOL(tape_std_mtrew); +EXPORT_SYMBOL(tape_std_mtoffl); +EXPORT_SYMBOL(tape_std_mtnop); +EXPORT_SYMBOL(tape_std_mteom); +EXPORT_SYMBOL(tape_std_mtreten); +EXPORT_SYMBOL(tape_std_mterase); +EXPORT_SYMBOL(tape_std_mtunload); +EXPORT_SYMBOL(tape_std_mtcompression); +EXPORT_SYMBOL(tape_std_read_block); +EXPORT_SYMBOL(tape_std_read_backward); +EXPORT_SYMBOL(tape_std_write_block); +EXPORT_SYMBOL(tape_std_process_eov); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/char/tape_std.h linux.21rc1-ac2/drivers/s390/char/tape_std.h --- linux.21rc1/drivers/s390/char/tape_std.h 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/char/tape_std.h 2003-04-25 13:53:58.000000000 +0100 @@ -0,0 +1,155 @@ +/* + * drivers/s390/char/tape_std.h + * standard tape device functions for ibm tapes. + * + * S390 and zSeries version + * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Carsten Otte + * Tuan Ngo-Anh + * Martin Schwidefsky + * Stefan Bader + */ + +#ifndef _TAPE_STD_H +#define _TAPE_STD_H + +#include + +/* + * The CCW commands for the Tape type of command. + */ +#define INVALID_00 0x00 /* Invalid cmd */ +#define BACKSPACEBLOCK 0x27 /* Back Space block */ +#define BACKSPACEFILE 0x2f /* Back Space file */ +#define DATA_SEC_ERASE 0x97 /* Data security erase */ +#define ERASE_GAP 0x17 /* Erase Gap */ +#define FORSPACEBLOCK 0x37 /* Forward space block */ +#define FORSPACEFILE 0x3F /* Forward Space file */ +#define FORCE_STREAM_CNT 0xEB /* Forced streaming count # */ +#define NOP 0x03 /* No operation */ +#define READ_FORWARD 0x02 /* Read forward */ +#define REWIND 0x07 /* Rewind */ +#define REWIND_UNLOAD 0x0F /* Rewind and Unload */ +#define SENSE 0x04 /* Sense */ +#define NEW_MODE_SET 0xEB /* Guess it is Mode set */ +#define WRITE_CMD 0x01 /* Write */ +#define WRITETAPEMARK 0x1F /* Write Tape Mark */ + +#define ASSIGN 0xB7 /* 3420 REJECT,3480 OK */ +#define CONTROL_ACCESS 0xE3 /* Set high speed */ +#define DIAG_MODE_SET 0x0B /* 3420 NOP, 3480 REJECT */ +#define LOAD_DISPLAY 0x9F /* 3420 REJECT,3480 OK */ +#define LOCATE 0x4F /* 3420 REJ, 3480 NOP */ +#define LOOP_WRITE_TO_READ 0x8B /* 3480 REJECT */ +#define MODE_SET_DB 0xDB /* 3420 REJECT,3480 OK */ +#define MODE_SET_C3 0xC3 /* for 3420 */ +#define MODE_SET_CB 0xCB /* for 3420 */ +#define MODE_SET_D3 0xD3 /* for 3420 */ +#define READ_BACKWARD 0x0C /* */ +#define READ_BLOCK_ID 0x22 /* 3420 REJECT,3480 OK */ +#define READ_BUFFER 0x12 /* 3420 REJECT,3480 OK */ +#define READ_BUFF_LOG 0x24 /* 3420 REJECT,3480 OK */ +#define RELEASE 0xD4 /* 3420 NOP, 3480 REJECT */ +#define REQ_TRK_IN_ERROR 0x1B /* 3420 NOP, 3480 REJECT */ +#define RESERVE 0xF4 /* 3420 NOP, 3480 REJECT */ +#define SENSE_GROUP_ID 0x34 /* 3420 REJECT,3480 OK */ +#define SENSE_ID 0xE4 /* 3420 REJECT,3480 OK */ +#define READ_DEV_CHAR 0x64 /* Read device characteristics */ +#define SET_DIAGNOSE 0x4B /* 3420 NOP, 3480 REJECT */ +#define SET_GROUP_ID 0xAF /* 3420 REJECT,3480 OK */ +#define SET_TAPE_WRITE_IMMED 0xC3 /* for 3480 */ +#define SUSPEND 0x5B /* 3420 REJ, 3480 NOP */ +#define SYNC 0x43 /* Synchronize (flush buffer) */ +#define UNASSIGN 0xC7 /* 3420 REJECT,3480 OK */ +#define PERF_SUBSYS_FUNC 0x77 /* 3490 CMD */ +#define READ_CONFIG_DATA 0xFA /* 3490 CMD */ +#define READ_MESSAGE_ID 0x4E /* 3490 CMD */ +#define READ_SUBSYS_DATA 0x3E /* 3490 CMD */ +#define SET_INTERFACE_ID 0x73 /* 3490 CMD */ + +#define SENSE_COMMAND_REJECT 0x80 +#define SENSE_INTERVENTION_REQUIRED 0x40 +#define SENSE_BUS_OUT_CHECK 0x20 +#define SENSE_EQUIPMENT_CHECK 0x10 +#define SENSE_DATA_CHECK 0x08 +#define SENSE_OVERRUN 0x04 +#define SENSE_DEFERRED_UNIT_CHECK 0x02 +#define SENSE_ASSIGNED_ELSEWHERE 0x01 + +#define SENSE_LOCATE_FAILURE 0x80 +#define SENSE_DRIVE_ONLINE 0x40 +#define SENSE_RESERVED 0x20 +#define SENSE_RECORD_SEQUENCE_ERR 0x10 +#define SENSE_BEGINNING_OF_TAPE 0x08 +#define SENSE_WRITE_MODE 0x04 +#define SENSE_WRITE_PROTECT 0x02 +#define SENSE_NOT_CAPABLE 0x01 + +#define SENSE_CHANNEL_ADAPTER_CODE 0xE0 +#define SENSE_CHANNEL_ADAPTER_LOC 0x10 +#define SENSE_REPORTING_CU 0x08 +#define SENSE_AUTOMATIC_LOADER 0x04 +#define SENSE_TAPE_SYNC_MODE 0x02 +#define SENSE_TAPE_POSITIONING 0x01 + +/* Data structure for the CONTROL_ACCESS call */ +struct tape_ca_data { + unsigned char function; + char password[11]; +} __attribute__ ((packed)); + +/* discipline functions */ +struct tape_request *tape_std_read_block(struct tape_device *, size_t); +void tape_std_read_backward(struct tape_device *device, + struct tape_request *request); +struct tape_request *tape_std_write_block(struct tape_device *, size_t); +struct tape_request *tape_std_bread(struct tape_device *, struct request *); +void tape_std_free_bread(struct tape_request *); +void tape_std_check_locate(struct tape_device *, struct tape_request *); +struct tape_request *tape_std_bwrite(struct request *, + struct tape_device *, int); + +/* Some non-mtop commands. */ +int tape_std_assign(struct tape_device *); +int tape_std_unassign(struct tape_device *); +int tape_std_force_unassign(struct tape_device *); +int tape_std_read_block_id(struct tape_device *, unsigned int *); +int tape_std_seek_block_id(struct tape_device *, unsigned int); +int tape_std_display(struct tape_device *, struct display_struct *); +int tape_std_terminate_write(struct tape_device *); + +/* Standard magnetic tape commands. */ +int tape_std_mtbsf(struct tape_device *, int); +int tape_std_mtbsfm(struct tape_device *, int); +int tape_std_mtbsr(struct tape_device *, int); +int tape_std_mtcompression(struct tape_device *, int); +int tape_std_mteom(struct tape_device *, int); +int tape_std_mterase(struct tape_device *, int); +int tape_std_mtfsf(struct tape_device *, int); +int tape_std_mtfsfm(struct tape_device *, int); +int tape_std_mtfsr(struct tape_device *, int); +int tape_std_mtload(struct tape_device *, int); +int tape_std_mtnop(struct tape_device *, int); +int tape_std_mtoffl(struct tape_device *, int); +int tape_std_mtreset(struct tape_device *, int); +int tape_std_mtreten(struct tape_device *, int); +int tape_std_mtrew(struct tape_device *, int); +int tape_std_mtsetblk(struct tape_device *, int); +int tape_std_mtunload(struct tape_device *, int); +int tape_std_mtweof(struct tape_device *, int); + +/* Event handlers */ +void tape_std_default_handler(struct tape_device *); +void tape_std_unexpect_uchk_handler(struct tape_device *); +void tape_std_irq(struct tape_device *); +void tape_std_process_eov(struct tape_device *); + +// the error recovery stuff: +void tape_std_error_recovery(struct tape_device *); +void tape_std_error_recovery_has_failed(struct tape_device *,int error_id); +void tape_std_error_recovery_succeded(struct tape_device *); +void tape_std_error_recovery_do_retry(struct tape_device *); +void tape_std_error_recovery_read_opposite(struct tape_device *); +void tape_std_error_recovery_HWBUG(struct tape_device *, int condno); + +#endif // _TAPE_STD_H diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/Config.in linux.21rc1-ac2/drivers/s390/Config.in --- linux.21rc1/drivers/s390/Config.in 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/Config.in 2003-04-25 13:53:58.000000000 +0100 @@ -52,19 +52,20 @@ if [ "$CONFIG_TN3215" = "y" ]; then bool 'Support for console on 3215 line mode terminal' CONFIG_TN3215_CONSOLE fi -bool 'Support for HWC line mode terminal' CONFIG_HWC -if [ "$CONFIG_HWC" = "y" ]; then - bool ' console on HWC line mode terminal' CONFIG_HWC_CONSOLE - tristate ' Control-Program Identification' CONFIG_HWC_CPI +bool 'Support for SCLP' CONFIG_SCLP +if [ "$CONFIG_SCLP" = "y" ]; then + bool ' Support for SCLP line mode terminal' CONFIG_SCLP_TTY + if [ "$CONFIG_SCLP_TTY" = "y" ]; then + bool ' Support for console on SCLP line mode terminal' CONFIG_SCLP_CONSOLE + fi + tristate ' Control-Program Identification' CONFIG_SCLP_CPI fi tristate 'S/390 tape device support' CONFIG_S390_TAPE if [ "$CONFIG_S390_TAPE" != "n" ]; then comment 'S/390 tape interface support' - bool ' Support for tape character devices' CONFIG_S390_TAPE_CHAR bool ' Support for tape block devices' CONFIG_S390_TAPE_BLOCK comment 'S/390 tape hardware support' - bool ' Support for 3490 tape hardware' CONFIG_S390_TAPE_3490 - bool ' Support for 3480 tape hardware' CONFIG_S390_TAPE_3480 + dep_tristate ' Support for 3480/3490 tape hardware' CONFIG_S390_TAPE_34XX $CONFIG_S390_TAPE fi endmenu @@ -80,6 +81,7 @@ tristate 'Universal TUN/TAP device driver support' CONFIG_TUN bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET bool 'Token Ring driver support' CONFIG_TR + dep_tristate 'Cisco 7000 support' CONFIG_C7000 m bool 'FDDI driver support' CONFIG_FDDI comment 'S/390 network device drivers' bool 'Channel Device Configuration' CONFIG_CHANDEV diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/c7000.c linux.21rc1-ac2/drivers/s390/net/c7000.c --- linux.21rc1/drivers/s390/net/c7000.c 1970-01-01 01:00:00.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/c7000.c 2003-04-22 16:44:37.000000000 +0100 @@ -0,0 +1,3301 @@ +/* + Cisco/7000 driver -- Copyright (C) 2000 UTS Global LLC. + Author: Bob Scardapane (UTS Global LLC). + Version: 3. + + To use this driver, run the LINUX command: + + insmod c7000 base0=0xYYYY lhost0=s1 uhost0=s2 lappl0=s3 uappl0=s4 dbg=x + + base0=0xYYYY defines the base unit address of the interface. + lhost0=s1 defines the local host name. + uhost0=s2 defines the unit host name. + lappl0=s3 defines the local application name. + uappl0=s4 defines the unit application name. + dbg=x defines the message level. Higher values will result in + additional diagnostic messages. + + Additional interfaces are defined on insmod by using the variable + name groups "base1,lhost1,lappl1,uhost1,uappl1", etc... up to three + additional groups. + + In addition, the module will automatically detect the unit base + addresses by scanning all active irq's for a control unit type + of 0x3088 and a model of 0x61 (CLAW mode). The noauto parameter + can be used to suppress automatic detection. + + The values of lhostx, lapplx, uhostx and uapplx default to: + + lapplx - TCPIP + lhostx - UTS + uapplx - TCPIP + uhostx - C7011 + + Note that the values passed in the insmod command will always + override the automatic detection of the unit base addreeses and + the default values of lapplx, lhostx, uapplx and uhostx. + + The parameter noauto can be used to disable automatic detection of + devices: + + noauto=1 (disable automatic detection) + noauto=0 (Enable automatic detectio. This is the default value.) + + The values in base0 - base3 will be copied to the bases array when + the module is loaded. + + To configure the interface(s), run the LINUX command(s): + + ifconfig ci0 ... + ifconfig ci1 ... + ifconfig ci2 ... + ifconfig ci3 ... + + There is one device structure for each controller in the c7000_devices + array. The base address of each controller is in the bases array. + These arrays parallel each other. There is also one c7000_controller + structure for each controller. This structure is pointed to by field + priv in the individual device structure. + + In each c7000_controller, there are embedded c7000_unit structures. + There is one c7000_unit structure per device number that makes up + a controller (currently 2 units per controller). +*/ + +#include +#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + Global defines +*/ + +/* + Maximum number of controllers. +*/ + +#define MAX_C7000 4 + +/* + Number of units per controller. +*/ + +#define NUNITS 2 + +/* + Define indexes of read and write units in the cunits array. +*/ + +#define C7000_RD 0 +#define C7000_WR 1 + +/* + Number of device buffers. +*/ + +#define C7000_MAXBUF 40 + +/* + Transmission queue length. +*/ + +#define C7000_TXQUEUE_LEN 100 +/* + Size of the IP packet data. +*/ + +#define C7000_DATAL 4096 + +/* + Size of the read header data. +*/ + +#define C7000_READHDRL 4 + +/* + Size of read flag byte. +*/ + +#define C7000_READFFL 1 + +/* + Size of a device buffer. This is how it is arranged in memory: + 4096 (IP packet data) + 4 (read header) + 1 (read flag) = 4101. +*/ + +#define C7000_BUFSIZE C7000_DATAL + C7000_READHDRL + C7000_READFFL + +/* + Size of sigsmod data. +*/ + +#define C7000_SIGSMODL 1 + +/* + Flag value that indicates a read was completed in the flag + field of a c7000_rd_header. +*/ + +#define FLAG_FF 0xff +/* + Size of C7000 sense id data. +*/ + +#define SIDL 32 + +/* + Maximum number of read and write retries. +*/ + +#define C7000_MAX_RETRIES 3 + +/* + Define sense byte0 value for a box reset. +*/ + +#define C7000_BOX_RESET 0x41 + +/* + CCW commands. +*/ + +#define C7000_WRITE_CCW 0x01 /* normal write */ +#define C7000_READ_CCW 0x02 /* normal read */ +#define C7000_NOOP_CCW 0x03 /* no operation */ +#define C7000_SIGSMOD_CCW 0x05 /* signal status modifier */ +#define C7000_TIC_CCW 0x08 /* transfer in channel */ +#define C7000_READHDR_CCW 0x12 /* read header */ +#define C7000_READFF_CCW 0x22 /* read FF flag */ +#define C7000_SID_CCW 0xe4 /* sense identification */ + +/* + Control commands. +*/ + +#define C7000_SYS_VALIDATE 1 +#define C7000_SYS_VALIDATE_RESP 2 +#define C7000_CONN_REQ 33 +#define C7000_CONN_RESP 34 +#define C7000_CONN_CONFRM 35 +#define C7000_DISCONN 36 +#define C7000_BOXERROR 65 + +/* + State machine values. +*/ + +#define C7000_INIT 1 +#define C7000_HALT 2 +#define C7000_SID 3 +#define C7000_SYSVAL 4 +#define C7000_CONNECT 5 +#define C7000_READY 6 +#define C7000_READ 7 +#define C7000_WRITE 8 +#define C7000_DISC 9 +#define C7000_STOP 10 +#define C7000_STOPPED 11 +#define C7000_ERROR 12 + +/* + The lower subchannel is used for read operations and the one that is + one higher is used for write operations. + + Both subchannels are initially in state C7000_INIT. A transition to + state C7000_HALT occurs when halt_IO is issued on each. When the + halts completes a transition to state C7000_SID occurs and a channel + program is issued to do a sense identification on both subchannels. + + When the sense identification completes, the state C7000_SYSVAL is + entered on the read subchannel. A read channel program is issued. + + When the sense identification completes, the write subchannel enters + state C7000_SYSVAL and a system validation request is written. The + read subchannel is also put into this state. + + When both the system validation response is read and an inbound system + validation request is read, the inbound system validation request is + responded to and both subchannels enter the C7000_CONNECT state. + + A read channel program is posted to look for the inbound connect + request. When that is received a connection confirmation is written. + The state of both subchannels is then changed to C7000_READY. A + read channel program is then posted and the state is changed to + C7000_READ. When a read completes, the packet is sent to the higher + layers and the read channel program is restarted. + + When there is a packet to be written, state C7000_WRITE is entered + and a channel program is issued to write the data. The subchannel + is in state C7000_READY when there is nothing to be written. + + When the stop method is executed, a disconnect message is sent and + the state is changed to C7000_DISC in both subchannels. A halt_IO + will be issued to both subchannels and state C7000_STOP will be entered. + When the halt IO completes, state C7000_STOPPED will be set. + + State C7000_ERROR is set when an error occurs in the interrupt + routine. Recycle the interface (ifconfig down / ifconfig up) + to reset this state. +*/ + +/* + Results from c7000_check_csw. +*/ + +enum c7000_rupt { + C7000_NORMAL, + C7000_CHANERR, + C7000_UCK, + C7000_UCK_RESET, + C7000_UE, + C7000_ATTN, + C7000_BUSY, + C7000_OTHER +}; + +/* + Bits set in device structure tbusy field. +*/ + +#define TB_TX 0 /* sk buffer handling in progress */ +#define TB_STOP 1 /* network device stop in progress */ +#define TB_RETRY 2 /* retry in progress */ +#define TB_NOBUFFER 3 /* no buffer on free queue */ + +/* + Bit in c7000_unit.flag_a that indicates the bh routine is busy. +*/ + +#define C7000_BH_ACTIVE 0 + +#define CPrintk(level, args...) \ + if (level <= dbg) \ + printk(args) + +/* + Maximum length of a system validation string. +*/ + +#define NAMLEN 8 + +#define Err_Conn_Confirm 1 +#define Err_Names_not_Matched 166 +#define Err_C7000_NOT_READY 167 +#define Err_Duplicate 170 +#define Err_Closing 171 +#define Err_No_Such_App 172 +#define Err_Host_Not_Ready 173 +#define Err_CLOSING 174 +#define Err_Dup_Link 175 +#define Err_Wrong_Version 179 +#define Err_Wrong_Frame_Size 180 + +/* + Define a macro to extract the logical link identifier from + the c7000 read header command field. +*/ + +#define C7000_LINKID(cmd) ((unsigned char)cmd >> 3) + +/* + Define the control unit type for a Cisco 7000. +*/ + +#define C7000_CU_TYPE 0x3088 + +/* + Define the control unit model for a Cisco 7000. +*/ + +#define C7000_CU_MODEL 0x61 + +/* + Define the default system validate parameters (lapplx, + lhostx, uapplx, uhostx). +*/ + +#define C7000_DFLT_LAPPL "TCPIP" +#define C7000_DFLT_LHOST "UTS" +#define C7000_DFLT_UAPPL "TCPIP" +#define C7000_DFLT_UHOST "C7011" + +/* + Global variables. +*/ + +/* + Base device addresses of the controllers. +*/ + +static int base0 = -1; +static int base1 = -1; +static int base2 = -1; +static int base3 = -1; + +static int bases[MAX_C7000]; + +/* + Local application names. +*/ + +static char *lappl0; +static char *lappl1; +static char *lappl2; +static char *lappl3; + +/* + Local host names. +*/ + +static char *lhost0; +static char *lhost1; +static char *lhost2; +static char *lhost3; + +/* + Unit application names. +*/ + +static char *uappl0; +static char *uappl1; +static char *uappl2; +static char *uappl3; + +/* + Unit hosts names. +*/ + +static char *uhost0; +static char *uhost1; +static char *uhost2; +static char *uhost3; + +/* + Debugging level (higher numbers emit lower priority messages). +*/ + +static unsigned int dbg = 0; + +/* + Parameter that controls auto detection. +*/ + +static int noauto = 0; + +/* + Interface names. +*/ + +static char ifnames[MAX_C7000][8] = {"ci0", "ci1", "ci2", "ci3"}; + +/* + One device structure per controller. +*/ + +/* RBH Try out the new code for 2.4.0 */ +#define NEWSTUFF + +#ifdef NEWSTUFF +#define STRUCT_NET_DEVICE struct net_device +#else +#define STRUCT_NET_DEVICE struct device +#endif + +STRUCT_NET_DEVICE c7000_devices[MAX_C7000]; + +/* + Scratch variable filled in with controller name. +*/ + +static char *controller; + +/* + Identify parameters that can be passed on the LINUX insmod command. +*/ + +MODULE_AUTHOR("Robert Scardapane (UTS Global)"); +MODULE_DESCRIPTION("Network module for Cisco 7000 box."); + +MODULE_PARM(base0, "1i"); +MODULE_PARM_DESC(base0, "Base unit address for 1st C7000 box."); +MODULE_PARM(base1, "1i"); +MODULE_PARM_DESC(base1, "Base unit address for 2nd C7000 box."); +MODULE_PARM(base2, "1i"); +MODULE_PARM_DESC(base2, "Base unit address for 3rd C7000 box."); +MODULE_PARM(base3, "1i"); +MODULE_PARM_DESC(base3, "Base unit address for 4th C7000 box."); + +MODULE_PARM(lappl0, "s"); +MODULE_PARM_DESC(lappl0, "Application name for 1st C7000 box."); +MODULE_PARM(lappl1, "s"); +MODULE_PARM_DESC(lappl1, "Application name for 2nd C7000 box."); +MODULE_PARM(lappl2, "s"); +MODULE_PARM_DESC(lappl2, "Application name for 3rd C7000 box."); +MODULE_PARM(lappl3, "s"); +MODULE_PARM_DESC(lappl3, "Application name for 4th C7000 box."); + +MODULE_PARM(lhost0, "s"); +MODULE_PARM_DESC(lhost0, "Host name for 1st C7000 box."); +MODULE_PARM(lhost1, "s"); +MODULE_PARM_DESC(lhost1, "Host name for 2nd C7000 box."); +MODULE_PARM(lhost2, "s"); +MODULE_PARM_DESC(lhost2, "Host name for 3rd C7000 box."); +MODULE_PARM(lhost3, "s"); +MODULE_PARM_DESC(lhost3, "Host name for 4th C7000 box."); + +MODULE_PARM(uhost0, "s"); +MODULE_PARM_DESC(uhost0, "Unit name for 1st C7000 box."); +MODULE_PARM(uhost1, "s"); +MODULE_PARM_DESC(uhost1, "Unit name for 2nd C7000 box."); +MODULE_PARM(uhost2, "s"); +MODULE_PARM_DESC(uhost2, "Unit name for 3rd C7000 box."); +MODULE_PARM(uhost3, "s"); +MODULE_PARM_DESC(uhost3, "Unit name for 4th C7000 box."); + +MODULE_PARM(uappl0, "s"); +MODULE_PARM_DESC(uappl0, "Unit application name for 1st C7000 box."); +MODULE_PARM(uappl1, "s"); +MODULE_PARM_DESC(uappl1, "Unit application name for 2nd C7000 box."); +MODULE_PARM(uappl2, "s"); +MODULE_PARM_DESC(uappl2, "Unit application name for 3rd C7000 box."); +MODULE_PARM(uappl3, "s"); +MODULE_PARM_DESC(uappl3, "Unit application name for 4th C7000 box."); + +MODULE_PARM(dbg, "1i"); +MODULE_PARM_DESC(dbg, "Message level for debugging."); + +MODULE_PARM(noauto, "1i"); +MODULE_PARM_DESC(noauto, "Control automatic detection of unit base addresses."); + +/* + Structure used to manage unit buffers. +*/ + +struct c7000_buffer { + ccw1_t ccws[7]; /* channel program */ + struct c7000_buffer *next; /* list pointer */ + char *data; /* pointer to actual data */ + int len; /* length of the data */ +}; + +/* + C7000 Control Block. + */ + +struct c7000_control_blk { + unsigned char cmd; + unsigned char ver; + unsigned char link_id; + unsigned char correlator; + unsigned char ret_code; + unsigned char resvd1[3]; + unsigned char unitname[NAMLEN]; + unsigned char hostname[NAMLEN]; + unsigned short rdsize; /* read frame size */ + unsigned short wrtsize; /* write frame size */ + unsigned char resvd2[4]; +}; + +/* + Per unit structure contained within the c7000_controller structure. +*/ + +struct c7000_unit { + ccw1_t ccws[5]; /* control ccws */ + int devno; /* device number */ + int irq; /* subchannel number */ + int IO_active; /* IO activity flag */ + int state; /* fsm state */ + int retries; /* retry counter */ + unsigned long flag_a; /* bh activity flag */ + devstat_t devstat; /* device status */ +#ifdef NEWSTUFF + wait_queue_head_t wait; /* sleep q head */ +#else + struct wait_queue *wait; /* sleep q pointer */ +#endif + struct c7000_controller *cntlp; /* controller pointer */ + struct c7000_buffer *free; /* free buffer anchor */ + struct c7000_buffer *proc_head; /* proc head */ + struct c7000_buffer *proc_tail; /* proc tail */ + struct c7000_buffer *bh_head; /* bh head */ + struct c7000_buffer *bh_tail; /* bh tail */ + struct tq_struct tq; /* bh scheduling */ + char senseid[SIDL]; /* sense id data */ + struct c7000_control_blk control_blk; /* control block */ + unsigned char sigsmod; /* sigsmod flag */ + unsigned char readhdr[4]; /* read header */ + unsigned char readff; /* readff flag */ +}; + +/* + Private structure pointed to by dev->priv. +*/ + +struct c7000_controller { + struct net_device_stats stats; /* statistics */ + STRUCT_NET_DEVICE *dev; /* -> device struct */ + unsigned int base_addr; /* base address */ + char lappl[NAMLEN]; /* local appl */ + char lhost[NAMLEN]; /* local host */ + char uappl[NAMLEN]; /* unit appl */ + char uhost[NAMLEN]; /* unit host */ + unsigned char version; /* version = 2 */ + unsigned char linkid; /* link id */ + struct c7000_unit cunits[NUNITS]; /* embedded units */ +#ifdef NEWSTUFF + int tbusy; +#endif +}; + +/* + This is the structure returned by the C7000_READHDR_CCW. +*/ + +struct c7000_rd_header { + unsigned short len; /* packet length */ + unsigned char cmd; /* command code */ + unsigned char flag; /* flag */ +}; + +/* + Set the device structure transmission busy flag. +*/ + +#ifdef NEWSTUFF +#define c7000_set_busy(dev) netif_stop_queue(dev) +#else +static __inline__ void +c7000_set_busy(STRUCT_NET_DEVICE *dev) +{ + dev->tbusy = 1; + eieio(); + return; +} +#endif + +/* + Clear the device structure transmission busy flag. +*/ + +#ifdef NEWSTUFF +#define c7000_clear_busy(dev) netif_wake_queue(dev) +#else +static __inline__ void +c7000_clear_busy(STRUCT_NET_DEVICE *dev) +{ + dev->tbusy = 0; + eieio(); + return; +} +#endif + +/* + Extract the device structure transmission busy flag. +*/ + +#ifdef NEWSTUFF +#define c7000_check_busy(dev) netif_queue_stopped(dev) +#else +static __inline__ int +c7000_check_busy(STRUCT_NET_DEVICE *dev) +{ + eieio(); + return(dev->tbusy); +} +#endif + +/* + Set a bit in the device structure transmission busy flag. +*/ + +static __inline__ void +c7000_setbit_busy(int nr, STRUCT_NET_DEVICE *dev) +{ +#ifdef NEWSTUFF + netif_stop_queue(dev); + test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy); +#else + set_bit(nr, (void *)&dev->tbusy); +#endif + return; +} + +/* + Clear a bit in the device structure transmission busy flag. +*/ + +static __inline__ void +c7000_clearbit_busy(int nr, STRUCT_NET_DEVICE *dev) +{ +#ifdef NEWSTUFF + clear_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy); + netif_wake_queue(dev); +#else + clear_bit(nr, (void *)&dev->tbusy); +#endif + return; +} + +/* + Test and set a bit in the device structure transmission busy flag. +*/ + +static __inline__ int +c7000_ts_busy(int nr, STRUCT_NET_DEVICE *dev) +{ +#ifdef NEWSTUFF + netif_stop_queue(dev); + return test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy); +#else + return(test_and_set_bit(nr, (void *)&dev->tbusy)); +#endif +} + +/* + Set the C7000 controller in the error state. +*/ + +static void +c7000_error(struct c7000_controller *ccp) +{ + int i; + struct c7000_unit *cup; + STRUCT_NET_DEVICE *dev = ccp->dev; + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + cup->state = C7000_ERROR; + } + + if (dev != NULL) +#ifdef NEWSTUFF + /* RBH XXX Should we be doing this? */ + dev->state &= ~__LINK_STATE_START; +#else + dev->flags &= ~IFF_RUNNING; +#endif + + CPrintk(0, "c7000: c7000_error: base unit 0x%x is down\n", ccp->base_addr); + return; +} + +/* + Based on the SENSE ID information, fill in the + controller name. Note that this is the SENSE ID + information saved by LINUX/390 at boot time. +*/ + +static int +c7000_check_type(senseid_t *id) +{ + + switch (id->cu_type) { + + case C7000_CU_TYPE: + + if (id->cu_model == C7000_CU_MODEL) { + controller = "C7000 "; + return(0); + } + + break; + + default: + break; + } + + return(-1); +} + +/* + Check the device information for the controller. +*/ + +static int +c7000_check_devices(int devno) +{ + int i; + s390_dev_info_t temp; + + /* + Get the SENSE ID information for each device. + */ + + for (i = devno; i < (devno + NUNITS); i++) { + + if (get_dev_info_by_devno(devno, &temp) != 0) + return(-1); + + if (c7000_check_type(&temp.sid_data) == -1) + return(-1); + } + + CPrintk(1, "c7000: c7000_check_devices: device type is %s\n", controller); + return(0); +} + +/* + Issue a halt I/O to device pointed to by cup. +*/ + +static int +c7000_haltio(struct c7000_unit *cup) +{ + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + DECLARE_WAITQUEUE(wait, current); + int rc; + + s390irq_spin_lock_irqsave(cup->irq, saveflags); + parm = (unsigned long)cup; + + if ((rc = halt_IO(cup->irq, parm, flags)) != 0) { + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(rc); + } + + /* + Wait for the halt I/O to finish. + */ + + add_wait_queue(&cup->wait, &wait); + current->state = TASK_UNINTERRUPTIBLE; + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + schedule(); + remove_wait_queue(&cup->wait, &wait); + return(0); +} + +/* + Issue a start I/O to device pointed to by cup. +*/ + +static int +c7000_doio(struct c7000_unit *cup) +{ + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + DECLARE_WAITQUEUE(wait, current); + int rc; + + /* + Do no further I/O while the device is in the ERROR, STOP + or STOPPED state. + */ + + if (cup->state == C7000_ERROR || cup->state == C7000_STOP || cup->state == C7000_STOPPED) + return(-1); + + s390irq_spin_lock_irqsave(cup->irq, saveflags); + parm = (unsigned long)cup; + + if ((rc = do_IO(cup->irq, &cup->ccws[0], parm, 0xff, flags)) != 0) { + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(rc); + } + + /* + Wait for the I/O to complete. + */ + + add_wait_queue(&cup->wait, &wait); + current->state = TASK_UNINTERRUPTIBLE; + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + schedule(); + remove_wait_queue(&cup->wait, &wait); + + /* + Interrupt handling may have marked the device in ERROR. + */ + + if (cup->state == C7000_ERROR) + return(-1); + + return(0); +} + +/* + Build a channel program to do a sense id channel program. +*/ + +static void +c7000_bld_senseid_chpgm(struct c7000_unit *cup) +{ + ccw1_t *ccwp; + + ccwp = &cup->ccws[0]; + ccwp->cmd_code = C7000_SID_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->senseid); + ccwp->count = SIDL; + ccwp++; + ccwp->cmd_code = C7000_NOOP_CCW; + ccwp->flags = CCW_FLAG_SLI; + ccwp->cda = (__u32)NULL; + ccwp->count = 1; + return; +} + +/* + Build a channel program to write a control message. +*/ + +static void +c7000_bld_wrtctl_chpgm(struct c7000_unit *cup) +{ + ccw1_t *ccwp; + + ccwp = &cup->ccws[0]; + ccwp->cmd_code = C7000_WRITE_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->control_blk); + ccwp->count = sizeof(struct c7000_control_blk); + ccwp++; + ccwp->cmd_code = C7000_READFF_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->readff); + ccwp->count = C7000_READFFL; + ccwp++; + ccwp->cmd_code = C7000_TIC_CCW; + ccwp->flags = 0; + ccwp->cda = (__u32)virt_to_phys(ccwp + 1); + ccwp->count = 0; + ccwp++; + ccwp->cmd_code = C7000_NOOP_CCW; + ccwp->flags = CCW_FLAG_SLI; + ccwp->cda = (__u32)NULL; + ccwp->count = 1; + return; +} + +/* + Build a write channel program to write the indicated buffer. +*/ + +static void +c7000_bld_wrt_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf) +{ + ccw1_t *ccwp; + struct c7000_controller *ccp = cup->cntlp; + + ccwp = &buf->ccws[0]; + ccwp->cmd_code = C7000_WRITE_CCW | (ccp->linkid << 3); + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(buf->data); + ccwp->count = buf->len; + ccwp++; + ccwp->cmd_code = C7000_READFF_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL + C7000_READHDRL); + ccwp->count = C7000_READFFL; + ccwp++; + ccwp->cmd_code = C7000_TIC_CCW; + ccwp->flags = 0; + ccwp->cda = (__u32)virt_to_phys(ccwp + 1); + ccwp->count = 0; + ccwp++; + ccwp->cmd_code = C7000_NOOP_CCW; + ccwp->flags = (CCW_FLAG_SLI); + ccwp->cda = (__u32)NULL; + ccwp->count = 1; + return; +} + +/* + Build a channel program to read a control message. +*/ + +static void +c7000_bld_readctl_chpgm(struct c7000_unit *cup) +{ + ccw1_t *ccwp; + + ccwp = &cup->ccws[0]; + ccwp->cmd_code = C7000_READ_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->control_blk); + ccwp->count = sizeof(struct c7000_control_blk); + ccwp++; + ccwp->cmd_code = C7000_READHDR_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->readhdr); + ccwp->count = C7000_READHDRL; + ccwp++; + ccwp->cmd_code = C7000_SIGSMOD_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod); + ccwp->count = C7000_SIGSMODL; + ccwp++; + ccwp->cmd_code = C7000_TIC_CCW; + ccwp->flags = 0; + ccwp->cda = (__u32)virt_to_phys(ccwp + 1); + ccwp->count = 0; + ccwp++; + ccwp->cmd_code = C7000_NOOP_CCW; + ccwp->flags = (CCW_FLAG_SLI); + ccwp->cda = (__u32)NULL; + ccwp->count = 1; + return; +} + +/* + Build a channel program to read the indicated buffer. +*/ + +static void +c7000_bld_read_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf) +{ + ccw1_t *ccwp; + + ccwp = &buf->ccws[0]; + ccwp->cmd_code = C7000_READ_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(buf->data); + ccwp->count = C7000_DATAL; + ccwp++; + ccwp->cmd_code = C7000_READHDR_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL); + ccwp->count = C7000_READHDRL; + ccwp++; + ccwp->cmd_code = C7000_SIGSMOD_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC); + ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod); + ccwp->count = C7000_SIGSMODL; + ccwp++; + ccwp->cmd_code = C7000_TIC_CCW; + ccwp->flags = 0; + ccwp->cda = (__u32)virt_to_phys(ccwp + 3); + ccwp->count = 0; + ccwp++; + ccwp->cmd_code = C7000_READFF_CCW; + ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC | CCW_FLAG_PCI); + ccwp->cda = (__u32)virt_to_phys(&cup->readff); + ccwp->count = C7000_READFFL; + ccwp++; + ccwp->cmd_code = C7000_TIC_CCW; + ccwp->flags = 0; + ccwp->cda = (__u32)virt_to_phys(ccwp + 1); + ccwp->count = 0; + ccwp++; + ccwp->cmd_code = C7000_NOOP_CCW; + ccwp->flags = (CCW_FLAG_SLI); + ccwp->cda = (__u32)NULL; + ccwp->count = 1; + return; +} + +/* + Allocate buffer structure headers and buffers for all units + A return value of 0 means that all allocations worked. A -1 + means that an allocation failed. It is expected that the caller + will call c7000_free_buffers when -1 is returned. +*/ + +static int +c7000_alloc_buffers(STRUCT_NET_DEVICE *dev) +{ + int i; + int j; + char *data; + struct c7000_buffer *bufptr; + struct c7000_controller *ccp = (struct c7000_controller *) dev->priv; + struct c7000_unit *cup; + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + cup->free = NULL; + + for (j = 0; j < C7000_MAXBUF; j++) { + bufptr = kmalloc(sizeof(struct c7000_buffer), GFP_KERNEL); + data = kmalloc(C7000_BUFSIZE, GFP_KERNEL); + + if (bufptr == NULL) + { + if(data) + kfree(data); + return(-1); + } + + /* + Place filled in buffer header on free anchor. + */ + + bufptr->next = cup->free; + bufptr->data = data; + bufptr->len = 0; + cup->free = bufptr; + + if (data == NULL) + return(-1); + + memset(data, '\0', C7000_BUFSIZE); + } + + } + + CPrintk(1, "c7000: c7000_alloc_buffers: allocated buffers for base unit 0x%lx\n", dev->base_addr); + return(0); +} + +/* + Free buffers on a chain. +*/ + +static void +c7000_free_chain(struct c7000_buffer *buf) +{ + char *data; + struct c7000_buffer *bufptr = buf; + struct c7000_buffer *tmp; + + while (bufptr != NULL) { + data = bufptr->data; + + if (data != NULL) + kfree(data); + + tmp = bufptr; + bufptr = bufptr->next; + kfree(tmp); + } + + return; +} + +/* + Free buffers on all possible chains for all units. +*/ + +static void +c7000_free_buffers(STRUCT_NET_DEVICE *dev) +{ + int i; + struct c7000_controller *ccp = (struct c7000_controller *) dev->priv; + struct c7000_unit *cup; + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + c7000_free_chain(cup->free); + cup->free = NULL; + c7000_free_chain(cup->proc_head); + cup->proc_head = cup->proc_tail = NULL; + c7000_free_chain(cup->bh_head); + cup->bh_head = cup->bh_tail = NULL; + } + + CPrintk(1, "c7000: c7000_free_buffers: freed buffers for base unit 0x%lx\n", dev->base_addr); + return; +} + +/* + Obtain a free buffer. Return a pointer to the c7000_buffer + structure OR NULL. +*/ + +struct c7000_buffer * +c7000_get_buffer(struct c7000_unit *cup) +{ + struct c7000_buffer *buf; + + buf = cup->free; + + if (buf == NULL) + return(NULL); + + cup->free = buf->next; + buf->next = NULL; + return(buf); +} + +/* + Release a buffer to the free list. +*/ + +void +c7000_release_buffer(struct c7000_unit *cup, struct c7000_buffer *buf) +{ + struct c7000_buffer *tmp; + + tmp = cup->free; + cup->free = buf; + buf->next = tmp; + return; +} + +/* + Queue a buffer on the end of the processing (proc) chain. +*/ + +void +c7000_queue_buffer(struct c7000_unit *cup, struct c7000_buffer *buf) +{ + buf->next = NULL; + + if (cup->proc_head == NULL) { + cup->proc_head = cup->proc_tail = buf; + return; + } + + cup->proc_tail->next = buf; + cup->proc_tail = buf; + return; +} + +/* + Dequeue a buffer from the start of the processing (proc) chain. +*/ + +struct c7000_buffer * +c7000_dequeue_buffer(struct c7000_unit *cup) +{ + struct c7000_buffer *buf = cup->proc_head; + + if (buf == NULL) + return(NULL); + + cup->proc_head = buf->next; + + if (cup->proc_head == NULL) + cup->proc_tail = NULL; + + buf->next = NULL; + return(buf); +} + +/* + Queue a buffer on the end of the bh routine chain. +*/ + +void +c7000_queue_bh_buffer(struct c7000_unit *cup, struct c7000_buffer *buf) +{ + buf->next = NULL; + + if (cup->bh_head == NULL) { + cup->bh_head = cup->bh_tail = buf; + return; + } + + cup->bh_tail->next = buf; + cup->bh_tail = buf; + return; +} + +/* + Dequeue a buffer from the start of the bh routine chain. +*/ + +struct c7000_buffer * +c7000_dequeue_bh_buffer(struct c7000_unit *cup) +{ + struct c7000_buffer *buf = cup->bh_head; + + if (buf == NULL) + return(NULL); + + cup->bh_head = buf->next; + + if (cup->bh_head == NULL) + cup->bh_tail = NULL; + + buf->next = NULL; + return(buf); +} + +/* + Build up a list of buffers to read. Each buffer is described + by one c7000_buffer structure. The c7000_buffer structure + contains a channel segment that will read that one buffer. + The channel program segments are chained together via TIC + CCWS. +*/ + +static int +c7000_bld_read_chain(struct c7000_unit *cup) +{ + struct c7000_buffer *buf, *pbuf = NULL; + struct c7000_rd_header *head; + int num = 0; + + while (cup->free != NULL) { + + /* + Obtain a buffer for a read channel segment. + */ + + if ((buf = c7000_get_buffer(cup)) == NULL) { + CPrintk(0, "c7000: c7000_bld_read_chain: can not obtain a read buffer for unit 0x%x\n", cup->devno); + return(-ENOMEM); + } + + num++; + buf->len = 0; + + /* + Clear out the read header flag. + */ + + head = (struct c7000_rd_header *)(buf->data + C7000_DATAL); + head->flag = 0x00; + c7000_queue_buffer(cup, buf); + + /* + Build the read channel program segment. + */ + + c7000_bld_read_chpgm(cup, buf); + + /* + Chain the prior (if any) channel program segment to + this one. + */ + + if (pbuf != NULL) + pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]); + + pbuf = buf; + } + + CPrintk(1, "c7000: c7000_bld_read_chain: chained %d buffers for unit 0x%x\n", num, cup->devno); + return(0); +} + +/* + Build up a list of buffers to write. Each buffer is described + by one c7000_buffer structure. The c7000_buffer structure + contains a channel segment that will write that one buffer. + The channel program segments are chained together via TIC + CCWS. +*/ + +static void +c7000_bld_wrt_chain(struct c7000_unit *cup) +{ + struct c7000_buffer *buf = cup->proc_head, *pbuf = NULL; + int num = 0; + + while (buf != NULL) { + c7000_bld_wrt_chpgm(cup, buf); + + /* + Chain the channel program segments together. + */ + + if (pbuf != NULL) + pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]); + + pbuf = buf; + buf = buf->next; + num++; + } + + CPrintk(1, "c7000: c7000_bld_wrt_chain: chained %d buffers for unit 0x%x\n", num, cup->devno); + return; +} + +/* + Interrupt handler bottom half (bh) routine. + Process all of the buffers on the c7000_unit bh chain. + The bh chain is populated by the interrupt routine when + a READ channel program completes on a buffer. +*/ + +static void +c7000_irq_bh(struct c7000_unit *cup) +{ + struct c7000_buffer *buf, *pbuf; + struct c7000_rd_header *head; + struct sk_buff *skb; + struct c7000_controller *ccp; + STRUCT_NET_DEVICE *dev; + int rc; + __u16 data_length; + __u32 parm; + __u8 flags = 0x00; + __u32 saveflags; + + ccp = cup->cntlp; + dev = ccp->dev; + + s390irq_spin_lock_irqsave(cup->irq, saveflags); + + /* + Process all buffers sent to bh by the interrupt routine. + */ + + while (cup->bh_head != NULL) { + buf = c7000_dequeue_bh_buffer(cup); + + /* + Deference the data as a c7000 header. + */ + + head = (struct c7000_rd_header *)(buf->data + C7000_DATAL); + + /* + If it is a control message, release the buffer and + continue the loop. + */ + + if (C7000_LINKID(head->cmd) == 0) { + CPrintk(0, "c7000: c7000_irq_bh: unexpected control command %d on unit 0x%x\n", head->cmd, cup->devno); + c7000_release_buffer(cup, buf); + continue; + } + + /* + Allocate a socket buffer. + */ + + data_length = head->len; + skb = dev_alloc_skb(data_length); + + /* + Copy the data to the skb. + Send it to the upper layers. + */ + + if (skb != NULL) { + memcpy(skb_put(skb, data_length), buf->data, data_length); + skb->dev = dev; + skb->protocol = htons(ETH_P_IP); + skb->pkt_type = PACKET_HOST; + skb->mac.raw = skb->data; + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + ccp->stats.rx_packets++; + } else { + CPrintk(0, "c7000: c7000_irq_bh: can not allocate a skb for unit 0x%x\n", cup->devno); + ccp->stats.rx_dropped++; + } + + /* + Rechain the buffer on the processing list. + */ + + head->flag = 0x00; + buf->len = 0; + pbuf = cup->proc_tail; + c7000_queue_buffer(cup, buf); + + /* + Rechain the buffer on the running channel program. + */ + + if (pbuf != NULL) + pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]); + + } + + /* + Restart the READ channel program if IO_active is 0. + */ + + if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) { + + if ((rc = c7000_bld_read_chain(cup)) != 0) { + CPrintk(0, "c7000: c7000_irq_bh: can not build read chain for unit 0x%x, return code %d\n", cup->devno, rc); + c7000_error(cup->cntlp); + clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return; + } + + parm = (__u32)cup; + cup->state = C7000_READ; + + if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) { + CPrintk(0, "c7000: c7000_irq_bh: can not start READ IO to unit 0x%x, return code %d\n", cup->devno, rc); + c7000_error(cup->cntlp); + clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return; + } + + CPrintk(1, "c7000: c7000_irq_bh: started READ IO to unit 0x%x\n", cup->devno); + } + + /* + Clear the bh active indication. + */ + + clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return; +} + +/* + Send a system validate control command to a unit. +*/ + +static int +c7000_send_sysval(struct c7000_unit *cup) +{ + int rc; + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &(cup->control_blk); + + CPrintk(1, "c7000: c7000_send_sysval: send sysval for device 0x%x\n", cup->devno); + + /* + Build the system validate control message. + */ + + memset(ctlblkp, '\0', sizeof(struct c7000_control_blk)); + ctlblkp->cmd = C7000_SYS_VALIDATE; + ctlblkp->correlator = 0; + ctlblkp->link_id = ccp->linkid; + ctlblkp->ver = ccp->version; + memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN); + memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN); + ctlblkp->rdsize = C7000_DATAL; + ctlblkp->wrtsize = C7000_DATAL; + + /* + Build the channel program. + */ + + c7000_bld_wrtctl_chpgm(cup); + + /* + Do the IO and wait for write to complete. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_send_sysval failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + return(0); +} + +/* + Send a system validate response control command to a unit. +*/ + +static int +c7000_send_sysval_resp(struct c7000_unit *cup, unsigned char correlator, int ret_code) +{ + int rc; + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &(cup->control_blk); + + CPrintk(1, "c7000: c7000_send_sysval_resp: send sysval response for device 0x%x\n", cup->devno); + + /* + Build the system validate response control message. + */ + + memset(ctlblkp, '\0', sizeof(struct c7000_control_blk)); + ctlblkp->cmd = C7000_SYS_VALIDATE_RESP; + ctlblkp->correlator = correlator; + ctlblkp->ret_code = ret_code; + ctlblkp->link_id = ccp->linkid; + ctlblkp->ver = ccp->version; + memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN); + memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN); + ctlblkp->rdsize = C7000_DATAL; + ctlblkp->wrtsize = C7000_DATAL; + + /* + Build the channel program. + */ + + c7000_bld_wrtctl_chpgm(cup); + + /* + Do the IO and wait for write to complete. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_send_sysval_resp failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + return(0); +} + +/* + Check the information read in a SYS_VALIDATE control message. +*/ + +static int +c7000_checkinfo(struct c7000_unit *cup) +{ + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &cup->control_blk; + int ret_code = 0; + + if (memcmp(ccp->lhost, ctlblkp->hostname, NAMLEN) || + memcmp(ccp->uhost, ctlblkp->unitname, NAMLEN)) + ret_code = Err_Names_not_Matched; + + if (ctlblkp->ver != ccp->version) + ret_code = Err_Wrong_Version; + + if ((ctlblkp->rdsize < C7000_DATAL) || (ctlblkp->wrtsize < C7000_DATAL)) + ret_code = Err_Wrong_Frame_Size; + + if (ret_code != 0) + CPrintk(0, "c7000: c7000_checkinfo: ret_code %d for device 0x%x\n", ret_code, cup->devno); + + return(ret_code); +} + +/* + Keep reading until a sysval response comes in or an error. +*/ + +static int +c7000_get_sysval_resp(struct c7000_unit *cup) +{ + struct c7000_controller *ccp = cup->cntlp; + int resp = 1; + int req = 1; + int rc; + int ret_code = 0; + + CPrintk(1, "c7000: c7000_get_sysval_resp: get sysval response for unit 0x%x\n", cup->devno); + + /* + Wait for the response to C7000_SYS_VALIDATE and for an + inbound C7000_SYS_VALIDATE. + */ + + while (resp || req) { + + /* + Build the read channel program. + */ + + c7000_bld_readctl_chpgm(cup); + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_get_sysval_resp: failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + /* + Process the control message. + */ + + switch (cup->control_blk.cmd) { + + /* + Check that response is positive and return + with success. Otherwise, return with an + error. + */ + + case C7000_SYS_VALIDATE_RESP: + + if (cup->control_blk.ret_code == 0) + resp = 0; + else { + CPrintk(0, "c7000: c7000_get_sysval_resp: receive sysval response for device 0x%x, return code %d\n", + cup->devno, + cup->control_blk.ret_code); + return(-1); + } + + break; + + /* + Check that the request is reasonable and + send a SYS_VALIDATE_RESP. Otherwise, + return with an error. + */ + + case C7000_SYS_VALIDATE: + CPrintk(1, "c7000: c7000_get_sysval_resp: receive sysval for device 0x%x\n", cup->devno); + req = 0; + ret_code = c7000_checkinfo(cup); + + if (c7000_send_sysval_resp(&ccp->cunits[C7000_WR], cup->control_blk.correlator, ret_code) != 0) + return(-1); + + if (ret_code != 0) + return(-1); + + break; + + /* + Anything else is unexpected and will result + in a return with an error. + */ + + default: + CPrintk(0, "c7000: c7000_get_sysval_resp: receive unexpected command for device 0x%x, command %d\n", cup->devno, cup->control_blk.cmd); + return(-1); + break; + } + + } + + return(0); +} + +/* + Send a connection confirm control message. +*/ + +static int +c7000_conn_confrm(struct c7000_unit *cup, unsigned char correlator, int linkid) +{ + int rc; + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &(cup->control_blk); + + CPrintk(1, "c7000: c7000_conn_confrm: send the connection confirmation message for unit 0x%x\n", cup->devno); + + /* + Build the connection confirm control message. + */ + + memset(ctlblkp, '\0', sizeof(struct c7000_control_blk)); + ctlblkp->cmd = C7000_CONN_CONFRM; + ctlblkp->ver = ccp->version; + ctlblkp->link_id = linkid; + ctlblkp->correlator = correlator; + ctlblkp->rdsize = 0; + ctlblkp->wrtsize = 0; + memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN); + memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN); + + /* + Build the channel program. + */ + + c7000_bld_wrtctl_chpgm(cup); + + /* + Do the IO and wait for write to complete. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_conn_confrm: failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + return(0); +} + +/* + Send a connection request control message. +*/ + +static int +c7000_send_conn(struct c7000_unit *cup) +{ + int rc; + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &(cup->control_blk); + + CPrintk(1, "c7000: c7000_send_conn: send the connection request message for unit 0x%x\n", cup->devno); + + /* + Build the connection request control message. + */ + + memset(ctlblkp, '\0', sizeof(struct c7000_control_blk)); + ctlblkp->cmd = C7000_CONN_REQ; + ctlblkp->ver = ccp->version; + ctlblkp->link_id = 0; + ctlblkp->correlator = 0; + ctlblkp->rdsize = C7000_DATAL; + ctlblkp->wrtsize = C7000_DATAL; + memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN); + memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN); + + /* + Build the channel program. + */ + + c7000_bld_wrtctl_chpgm(cup); + + /* + Do the IO and wait for write to complete. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_send_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + return(0); +} + +/* + Send a disconnect control message to the link with the value of + linkid. +*/ + +static int +c7000_send_disc(struct c7000_unit *cup, int linkid) +{ + int rc; + struct c7000_controller *ccp = cup->cntlp; + struct c7000_control_blk *ctlblkp = &(cup->control_blk); + + CPrintk(1, "c7000: c7000_send_disc: send disconnect message for unit 0x%x\n", cup->devno); + + /* + Build the disconnect control message. + */ + + memset(ctlblkp, '\0', sizeof(struct c7000_control_blk)); + ctlblkp->cmd = C7000_DISCONN; + ctlblkp->ver = ccp->version; + ctlblkp->link_id = linkid; + ctlblkp->correlator = 0; + ctlblkp->rdsize = C7000_DATAL; + ctlblkp->wrtsize = C7000_DATAL; + memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN); + memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN); + + /* + Build the channel program. + */ + + c7000_bld_wrtctl_chpgm(cup); + + /* + Do the IO and wait for write to complete. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_send_disc: failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + return(0); +} + +/* + Resolve the race condition based on the link identifier value. + The adapter microcode assigns the identifiers. A higher value implies + that the race was lost. A side effect of this function is that + ccp->linkid is set to the link identifier to be used for this + connection (provided that 0 is returned). +*/ + +static int +c7000_resolve_race(struct c7000_unit *cup, int local_linkid, int remote_linkid) +{ + struct c7000_controller *ccp = cup->cntlp; + + CPrintk(1, "c7000: c7000_resolve_race: for unit 0x%x, local linkid %d, remote linkid %d\n", cup->devno, local_linkid, remote_linkid); + + /* + This local link identifier should not be zero.. + */ + + if (local_linkid == 0) { + CPrintk(0, "c7000: c7000_resolve_race: error for unit 0x%x, local linkid is null\n", cup->devno); + return(-1); + } + + /* + This indicates that there is no race. Just use our + local link identifier. + */ + + if (remote_linkid == 0) { + ccp->linkid = local_linkid; + return(0); + } + + /* + Send a connection confirm message if we lost the race to + the winning link identifier. + + Either way, save the winning link identifier. + */ + + if (local_linkid > remote_linkid) { + + if (c7000_conn_confrm(&ccp->cunits[C7000_WR], cup->control_blk.correlator, remote_linkid) != 0) { + CPrintk(0, "c7000: c7000_resolve_race: failed for unit 0x%x\n", cup->devno); + return(-1); + } + + ccp->linkid = remote_linkid; + } else { + ccp->linkid = local_linkid; + } + + return(0); +} + +/* + Get connected by processing the connection request/response/confirm + control messages. A connection request has already been sent by + calling function c7000_send_conn. +*/ + +static int +c7000_get_conn(struct c7000_unit *cup) +{ + struct c7000_controller *ccp = cup->cntlp; + int rc; + int cont = 1; + int remote_linkid = 0; + int local_linkid = 0; + + CPrintk(1, "c7000: c7000_get_conn: read the connected message for unit 0x%x\n", cup->devno); + ccp->linkid = 0; + + while (cont == 1) { + + /* + Build the read channel program. + */ + + c7000_bld_readctl_chpgm(cup); + + /* + Start the channel program to read a control message. + */ + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_get_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno); + return(-1); + } + + /* + Process the control message that was received based + on the command code. + */ + + CPrintk(1, "c7000: c7000_get_conn: received command %d for unit 0x%x\n", cup->control_blk.cmd, cup->devno); + + switch(cup->control_blk.cmd) { + + /* + Save the remote link_id in the message for + a check in c7000_resolve_race. + */ + + case C7000_CONN_REQ: + remote_linkid = cup->control_blk.link_id; + break; + + /* + A connection response received. Resolve + the network race condition (if any) by + comparing the link identifier values. + */ + + case C7000_CONN_RESP: + + if (cup->control_blk.ret_code != 0) { + CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x , connection response return code %d\n", + cup->devno, cup->control_blk.ret_code); + return(-1); + } + + local_linkid = cup->control_blk.link_id; + + if (c7000_resolve_race(cup, local_linkid, remote_linkid) != 0) + return(-1); + + break; + + /* + Got a confirmation to our connection request. + Disconnect the remote link identifier (if any). + Break out of the loop. + */ + + case C7000_CONN_CONFRM: + + if (remote_linkid != 0) { + + if (c7000_send_disc(&ccp->cunits[C7000_WR], remote_linkid) != 0) { + CPrintk(0, "c7000: c7000_get_conn: send disconnect failed for unit 0x%x\n", cup->devno); + return(-1); + } + + } + + cont = 0; + break; + + /* + Got a disconnect to our connection request. + Break out of the loop. + */ + + case C7000_DISCONN: + cont = 0; + break; + + /* + Anything else must be an error. + Return with an error immediately. + */ + + default: + CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x unexpected command %d\n", + cup->devno, cup->control_blk.cmd); + return(-1); + } + + } + + /* + Be sure that we now have a link identifier. + */ + + if (ccp->linkid == 0) + return(-1); + + return(0); +} + +/* + Get statistics method. +*/ + +struct net_device_stats * +c7000_stats(STRUCT_NET_DEVICE *dev) +{ + struct c7000_controller *ccp = (struct c7000_controller *)dev->priv; + + return(&ccp->stats); +} + +/* + Open method. +*/ + +static int +c7000_open(STRUCT_NET_DEVICE *dev) +{ + int i; + struct c7000_controller *ccp = (struct c7000_controller *)dev->priv; + struct c7000_unit *cup; + int rc; + __u32 parm; + __u8 flags = 0x00; + + c7000_set_busy(dev); + + /* + Allocate space for the unit buffers. + */ + + if (c7000_alloc_buffers(dev) == -1) { + CPrintk(0, "c7000: c7000_open: can not allocate buffer space for base unit 0x%lx\n", dev->base_addr); + c7000_free_buffers(dev); /* free partially allocated buffers */ + c7000_clear_busy(dev); + return(-ENOMEM); + } + + /* + Perform the initialization for all units. + */ + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + + /* + Initialize task queue structure used for the bottom + half routine. + */ + +#ifndef NEWSTUFF + cup->tq.next = NULL; +#endif + cup->tq.sync = 0; + cup->tq.routine = (void *)(void *)c7000_irq_bh; + cup->tq.data = cup; + cup->state = C7000_HALT; +#ifdef NEWSTUFF + init_waitqueue_head(&cup->wait); +#endif + CPrintk(1, "c7000: c7000_open: issuing halt to unit 0x%x\n", cup->devno); + + /* + Issue a halt I/O to the unit + */ + + if ((rc = c7000_haltio(cup)) != 0) { + CPrintk(0, "c7000: c7000_open: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno); + continue; + } + + cup->IO_active = 0; + cup->flag_a = 0; + cup->sigsmod = 0x00; + + CPrintk(1, "c7000: c7000_open: halt complete for unit 0x%x\n", cup->devno); + } + + /* + On each subchannel send a sense id. + */ + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + + /* + Build SENSE ID channel program. + */ + + c7000_bld_senseid_chpgm(cup); + + /* + Issue the start I/O for SENSE ID channel program. + */ + + CPrintk(1, "c7000: c7000_open: issuing SENSEID to unit 0x%x\n", cup->devno); + + if ((rc = c7000_doio(cup)) != 0) { + CPrintk(0, "c7000: c7000_open: SENSEID failed with rc = %d for unit 0x%x\n", rc, cup->devno); + c7000_clear_busy(dev); + return(-EIO); + } + + CPrintk(1, "c7000: c7000_open: SENSEID complete for unit 0x%x\n", cup->devno); + } + + /* + Send the system validation control message. + */ + + cup = &ccp->cunits[C7000_WR]; + + if (c7000_send_sysval(cup) != 0) { + CPrintk(0, "c7000: c7000_open: can not send sysval for unit 0x%x\n", cup->devno); + c7000_clear_busy(dev); + return(-EIO); + } + + CPrintk(1, "c7000: c7000_open: successfully sent sysval for unit 0x%x\n", cup->devno); + /* + Get the system validation response message. + */ + + cup = &ccp->cunits[C7000_RD]; + + if (c7000_get_sysval_resp(cup) != 0) { + CPrintk(0, "c7000: c7000_open: can not read sysval response for unit 0x%x\n", cup->devno); + c7000_clear_busy(dev); + return(-EIO); + } + + CPrintk(1, "c7000: c7000_open: successfully received sysval reply for unit 0x%x\n", cup->devno); + ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_CONNECT; + + cup = &ccp->cunits[C7000_WR]; + + /* + Send a connection request. + */ + + if (c7000_send_conn(cup) != 0) { + CPrintk(0, "c7000: c7000_open: connection failed for unit 0x%x\n", cup->devno); + c7000_clear_busy(dev); + return(-EIO); + } + + cup = &ccp->cunits[C7000_RD]; + + /* + Get the response to our connection request Note that a + network race may occur. This is handled in c7000_get_conn. + */ + + if (c7000_get_conn(cup) != 0) { + CPrintk(0, "c7000: c7000_open: unit 0x%x has connected\n", cup->devno); + c7000_clear_busy(dev); + return(-EIO); + } + + CPrintk(1, "c7000: c7000_open: successfully received connection request for unit 0x%x\n", cup->devno); + ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_READY; + + /* + Clear the interface statistics. + */ + + memset(&ccp->stats, '\0', sizeof(struct net_device_stats)); + + if ((rc = c7000_bld_read_chain(cup)) != 0) { + c7000_clear_busy(dev); + return(rc); + } + + /* + Start the C7000_READ channel program but do not wait for it's + completion. + */ + + cup->state = C7000_READ; + parm = (__u32) cup; + set_bit(0, (void *)&cup->IO_active); + + if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) { + CPrintk(0, "c7000: c7000_open: READ failed with return code %d for unit 0x%x\n", rc, cup->devno); + c7000_error(cup->cntlp); + clear_bit(0, (void *)&cup->IO_active); + c7000_clear_busy(dev); + return(-EIO); + } + +#ifdef NEWSTUFF + netif_start_queue(dev); +#else + dev->start = 1; +#endif + CPrintk(0, "c7000: c7000_open: base unit 0x%lx is opened\n", dev->base_addr); + c7000_clear_busy(dev); + MOD_INC_USE_COUNT; /* increment module usage count */ + return(0); +} + +/* + Stop method. +*/ + +static int +c7000_stop(STRUCT_NET_DEVICE *dev) +{ + int i; + struct c7000_controller *ccp = (struct c7000_controller *)dev->priv; + struct c7000_unit *cup; + int rc; + +#ifdef NEWSTUFF +/* nothing? */ +#else + dev->start = 0; +#endif + c7000_set_busy(dev); + + /* + Send a disconnect message. + */ + + ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_DISC; + cup = &ccp->cunits[C7000_WR]; + + if (c7000_send_disc(cup, ccp->linkid) != 0) { + CPrintk(0, "c7000: c7000_stop: send of disconnect message failed for unit 0x%x\n", cup->devno); + } + + CPrintk(1, "c7000: c7000_stop: successfully sent disconnect message to unit 0x%x\n", cup->devno); + + /* + Issue a halt I/O to all units. + */ + + for (i = 0; i < NUNITS; i++) { + cup = &ccp->cunits[i]; + cup->state = C7000_STOP; + CPrintk(1, "c7000: c7000_stop: issuing halt to unit 0x%x\n", cup->devno); + + if ((rc = c7000_haltio(cup)) != 0) { + CPrintk(0, "c7000: c7000_stop: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno); + continue; + } + + CPrintk(1, "c7000: c7000_stop: halt complete for unit 0x%x\n", cup->devno); + } + + c7000_free_buffers(dev); + CPrintk(0, "c7000: c7000_stop: base unit 0x%lx is stopped\n", dev->base_addr); + MOD_DEC_USE_COUNT; /* Decrement module usage count */ + return(0); +} + +/* + Configure the interface. +*/ + +static int +c7000_config(STRUCT_NET_DEVICE *dev, struct ifmap *map) +{ + CPrintk(1, "c7000: c7000_config: entered for base unit 0x%lx\n", dev->base_addr); + return(0); +} + +/* + Transmit a packet. +*/ + +static int +c7000_xmit(struct sk_buff *skb, STRUCT_NET_DEVICE *dev) +{ + struct c7000_controller *ccp = (struct c7000_controller *)dev->priv; + struct c7000_unit *cup; + __u32 saveflags; + __u32 parm; + __u8 flags = 0x00; + struct c7000_buffer *buf, *pbuf; + int rc; + + CPrintk(1, "c7000: c7000_xmit: entered for base unit 0x%lx\n", dev->base_addr); + + /* + When the skb pointer is NULL return. + */ + + if (skb == NULL) { + CPrintk(0, "c7000: c7000_xmit: skb pointer is null for base unit 0x%lx\n", dev->base_addr); + ccp->stats.tx_dropped++; + return(-EIO); + } + + cup = &ccp->cunits[C7000_WR]; + + /* + Lock the irq. + */ + + s390irq_spin_lock_irqsave(cup->irq, saveflags); + + /* + When the device transmission busy flag is on , no data + can be sent. Unlock the irq and return EBUSY. + */ + + if (c7000_check_busy(dev)) { + CPrintk(1, "c7000: c7000_xmit: c7000_check_busy returns true for base unit 0x%lx\n", dev->base_addr); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(-EBUSY); + } + + /* + Set the device transmission busy flag on atomically. + */ + + if (c7000_ts_busy(TB_TX, dev)) { + CPrintk(1, "c7000: c7000_xmit: c7000_ts_busy returns true for base unit 0x%lx\n", dev->base_addr); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(-EBUSY); + } + + CPrintk(1, "c7000: c7000_xmit: set TB_TX for unit 0x%x\n", cup->devno); + + /* + Obtain a free buffer. If none are free then mark tbusy + with TB_NOBUFFER and return EBUSY. + */ + + if ((buf = c7000_get_buffer(cup)) == NULL) { + CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr); + c7000_setbit_busy(TB_NOBUFFER, dev); + c7000_clearbit_busy(TB_TX, dev); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(-EBUSY); + } + + CPrintk(1, "c7000: c7000_xmit: Got buffer for unit 0x%x\n", cup->devno); + + /* + Save the length of the skb data and then copy it to the + buffer. Queue the buffer on the processing list. + */ + + buf->len = skb->len; + memcpy(buf->data, skb->data, skb->len); + memset(buf->data + C7000_DATAL + C7000_READHDRL, '\0', C7000_READFFL); + pbuf = cup->proc_tail; + c7000_queue_buffer(cup, buf); + + /* + Chain the buffer to the running channel program. + */ + + if (test_bit(0, (void *)&cup->IO_active) && pbuf != NULL) { + c7000_bld_wrt_chpgm(cup, buf); + pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]); + } + + /* + Free the socket buffer. + */ + + dev_kfree_skb(skb); + + /* + If the unit is not currently doing IO, build a channel + program and start the IO for the buffers on the processing + chain. + */ + + if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) { + CPrintk(1, "c7000: c7000_xmit: start IO for unit 0x%x\n", cup->devno); + c7000_bld_wrt_chain(cup); + parm = (__u32) cup; + cup->state = C7000_WRITE; + + if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) { + CPrintk(0, "c7000: c7000_xmit: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno); + c7000_error(cup->cntlp); + c7000_clearbit_busy(TB_TX, dev); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + return(-EIO); + } + + dev->trans_start = jiffies; + CPrintk(1, "c7000: c7000_xmit: do_IO succeeds for unit 0x%x\n", cup->devno); + } + + /* + If the free chain is now NULL, set the TB_NOBUFFER flag. + */ + + if (cup->free == NULL) { + CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr); + c7000_setbit_busy(TB_NOBUFFER, dev); + } + + c7000_clearbit_busy(TB_TX, dev); + s390irq_spin_unlock_irqrestore(cup->irq, saveflags); + CPrintk(1, "c7000: c7000_xmit: exits for unit 0x%x\n", cup->devno); + return(0); +} + +/* + Handle an ioctl from a user process. +*/ + +static int +c7000_ioctl(STRUCT_NET_DEVICE *dev, struct ifreq *ifr, int cmd) +{ + CPrintk(1, "c7000: c7000_ioctl: entered for base unit 0x%lx with cmd %d\n", dev->base_addr, cmd); + return(0); +} + +/* + Analyze the interrupt status and return a value + that identifies the type. +*/ + +static enum c7000_rupt +c7000_check_csw(devstat_t *devstat) +{ + + /* + Check for channel detected conditions (except PCI). + */ + + if ((devstat->cstat & ~SCHN_STAT_PCI) != 0) { + CPrintk(0, "c7000: c7000_check_csw: channel status 0x%x for unit 0x%x\n", devstat->cstat, devstat->devno); + return(C7000_CHANERR); + } + + /* + Fast path the normal cases. + */ + + if (devstat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return(C7000_NORMAL); + + if (devstat->cstat == SCHN_STAT_PCI) + return(C7000_NORMAL); + + /* + Check for exceptions. + */ + + if (devstat->dstat & DEV_STAT_UNIT_CHECK) { + CPrintk(0, "c7000: c7000_check_csw: unit check for unit 0x%x, sense byte0 0x%2.2x\n", devstat->devno, devstat->ii.sense.data[0]); + + if (devstat->ii.sense.data[0] == C7000_BOX_RESET) + return(C7000_UCK_RESET); + else + return(C7000_UCK); + + } else if (devstat->dstat & DEV_STAT_UNIT_EXCEP) { + CPrintk(0, "c7000: c7000_check_csw: unit exception for unit 0x%x\n", devstat->devno); + return(C7000_UE); + + } else if (devstat->dstat & DEV_STAT_ATTENTION) { + CPrintk(0, "c7000: c7000_check_csw: attention for unit 0x%x\n", devstat->devno); + return(C7000_ATTN); + + } else if (devstat->dstat & DEV_STAT_BUSY) { + CPrintk(0, "c7000: c7000_check_csw: busy for unit 0x%x\n", devstat->devno); + return(C7000_BUSY); + + } else { + CPrintk(0, "c7000: c7000_check_csw: channel status 0x%2.2x , device status 0x%2.2x, devstat flags 0x%8.8x for unit 0x%x\n", + devstat->cstat, devstat->dstat, devstat->flag, devstat->devno); + return(C7000_OTHER); + } + + /* NOT REACHED */ + +} + +/* + Retry the last CCW chain to the unit. +*/ + +static void +c7000_retry_io(struct c7000_unit *cup) +{ + int rc; + __u32 parm; + __u8 flags = 0x00; + ccw1_t *ccwp; + + if (++cup->retries > C7000_MAX_RETRIES) { + c7000_error(cup->cntlp); + CPrintk(0, "c7000: c7000_retry_io: retry IO for unit 0x%x exceeds maximum retry count\n", cup->devno); + return; + } + + set_bit(0, (void *)&cup->IO_active); + parm = (__u32)cup; + + if (cup->state == C7000_READ || cup->state == C7000_WRITE) + ccwp = &cup->proc_head->ccws[0]; + else + ccwp = &cup->ccws[0]; + + if ((rc = do_IO(cup->irq, ccwp, parm, 0xff, flags)) != 0) { + CPrintk(0, "c7000: c7000_retry_io: can not retry IO for unit 0x%x, return code %d\n", cup->devno, rc); + clear_bit(0, (void *)&cup->IO_active); + c7000_error(cup->cntlp); + } + + CPrintk(1, "c7000: c7000_retry_io: retry IO for unit 0x%x, retry count %d\n", cup->devno, cup->retries); + return; +} + +/* + Process a read interrupt by scanning the list of buffers + for ones that have completed and queue them for the bottom + half to process. +*/ + +static void +c7000_proc_rintr(struct c7000_unit *cup) +{ + struct c7000_buffer *buf; + struct c7000_rd_header *head; + int num_read = 0; + + while (cup->proc_head != NULL) { + head = (struct c7000_rd_header *)(cup->proc_head->data + C7000_DATAL); + + /* + The flag byte in the read header will be set to + FLAG_FF when the buffer has been read. + */ + + if (head->flag != FLAG_FF) + break; + + /* + Dequeue the buffer from the proc chain + and enqueue it on the bh chain for + the bh routine to process. + */ + + buf = c7000_dequeue_buffer(cup); + c7000_queue_bh_buffer(cup, buf); + num_read++; + } + + CPrintk(1, "c7000: c7000_proc_rintr: %d buffers read for unit 0x%x\n", num_read, cup->devno); + return; +} + +/* + Process all completed buffers on the proc chain. + A buffer is completed if it's READFF flag is FLAG_FF. +*/ + +static int +c7000_proc_wintr(struct c7000_unit *cup) +{ + struct c7000_controller *ccp = cup->cntlp; + struct c7000_buffer *buf; + int num_write = 0; + + if (cup->proc_head == NULL) { + CPrintk(0, "c7000: c7000_proc_wintr: unexpected NULL processing chain pointer for unit 0x%x\n", cup->devno); + return(num_write); + } + + while (cup->proc_head != NULL) { + + /* + Check if the buffer has completed. + */ + + if (*(cup->proc_head->data + C7000_DATAL + C7000_READHDRL) != FLAG_FF) + break; + + /* + Remove buffer from top of processing chain. + Place it on free list. + */ + + buf = c7000_dequeue_buffer(cup); + c7000_release_buffer(cup, buf); + num_write++; + + /* + Update transmitted packets statistic. + */ + + ccp->stats.tx_packets++; + } + + CPrintk(1, "c7000: c7000_proc_wintr: %d buffers written for unit 0x%x\n", num_write, cup->devno); + return(num_write); +} + +/* + Interrupt handler. +*/ + +static void +c7000_intr(int irq, void *initparm, struct pt_regs *regs) +{ + devstat_t *devstat = ((devstat_t *) initparm); + struct c7000_unit *cup = NULL; + struct c7000_controller *ccp = NULL; + STRUCT_NET_DEVICE *dev = NULL; + __u32 parm; + __u8 flags = 0x00; + int rc; + + /* + Discard unsolicited interrupts + */ + + if (devstat->intparm == 0) { + CPrintk(0, "c7000: c7000_intr: unsolicited interrupt for device 0x%x, cstat = 0x%2.2x, dstat = 0x%2.2x, flag = 0x%8.8x\n", + devstat->devno, devstat->cstat, devstat->dstat, devstat->flag); + return; + } + + /* + Obtain the c7000_unit structure pointer. + */ + + cup = (struct c7000_unit *)(devstat->intparm); + + /* + Obtain the c7000_controller structure and device structure + pointers. + */ + + if (cup == NULL) { + CPrintk(0, "c7000: c7000_intr: c7000_unit pointer is NULL in devstat\n"); + return; + } + + ccp = cup->cntlp; + + if (ccp == NULL) { + CPrintk(0, "c7000: c7000_intr: c7000_cntlp pointer is NULL in c7000_unit structure 0x%x for unit 0x%x\n", (int)cup, cup->devno); + return; + } + + dev = ccp->dev; + + if (dev == NULL) { + CPrintk(0, "c7000: c7000_intr: device pointer is NULL in c7000_controller structure 0x%x for unit 0x%x\n", (int)ccp, cup->devno); + return; + } + + /* + Finite state machine (fsm) handling. + */ + + CPrintk(1, "c7000: c7000_intr: entered with state %d flag 0x%8.8x for unit 0x%x\n", cup->state, devstat->flag, cup->devno); + + switch(cup->state) { + + /* + Not expected to be here when in INIT state. + */ + + case C7000_INIT: + + break; + + /* + Enter state C7000_SID and wakeup the sleeping + process in c7000_open. + */ + + case C7000_HALT: + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + cup->state = C7000_SID; + wake_up(&cup->wait); + break; + + /* + Enter state C7000_SYSVAL and wakeup the sleeping + process in c7000_open. + */ + + case C7000_SID: + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + if (c7000_check_csw(devstat) != 0) { + c7000_retry_io(cup); + + if (cup->state == C7000_ERROR) + wake_up(&cup->wait); + + break; + } + + cup->retries = 0; + cup->state = C7000_SYSVAL; + wake_up(&cup->wait); + break; + + /* + Wakeup the sleeping process in c7000_open. + */ + + case C7000_SYSVAL: + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + if (c7000_check_csw(devstat) != 0) { + c7000_retry_io(cup); + + if (cup->state == C7000_ERROR) + wake_up(&cup->wait); + + break; + } + + cup->retries = 0; + wake_up(&cup->wait); + break; + + /* + Wakeup the sleeping process in c7000_open. + */ + + case C7000_CONNECT: + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + if (c7000_check_csw(devstat) != 0) { + c7000_retry_io(cup); + + if (cup->state == C7000_ERROR) + wake_up(&cup->wait); + + break; + } + + cup->retries = 0; + wake_up(&cup->wait); + break; + + /* + Not expected to be entered here. + */ + + case C7000_READY: + break; + + /* + Process the data that was just read. + */ + + case C7000_READ: + + if ((devstat->flag & (DEVSTAT_PCI | DEVSTAT_FINAL_STATUS)) == 0) + break; + + CPrintk(1, "c7000: c7000_intr: process read interrupt for unit 0x%x , devstat flag = 0x%8.8x\n", cup->devno, devstat->flag); + + /* + Check for serious errors. + */ + + if (c7000_check_csw(devstat) != 0) { + ccp->stats.rx_errors++; + c7000_error(cup->cntlp); + break; + } + + /* + Build the bottom half buffer list. + */ + + c7000_proc_rintr(cup); + + /* + When final status is received clear + the IO active bit. + */ + + if (devstat->flag & DEVSTAT_FINAL_STATUS) { + clear_bit(0, (void *)&cup->IO_active); + } + + /* + If there are free buffers redrive the IO. + */ + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) && + (cup->free != NULL)) { + c7000_bld_read_chain(cup); + parm = (__u32)cup; + set_bit(0, (void *)&cup->IO_active); + + if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) { + clear_bit(0, (void *)&cup->IO_active); + CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno); + c7000_error(cup->cntlp); + break; + } + + CPrintk(1, "c7000: c7000_intr: started read io for unit 0x%x\n", cup->devno); + } + + /* + Initiate bottom half routine to process + data that was read. + */ + + if (test_and_set_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a) == 0) { + queue_task(&cup->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + break; + + /* + Free the transmitted buffers and restart the channel + process (if necessary). + */ + + case C7000_WRITE: + + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + if (c7000_check_csw(devstat) != 0) { + ccp->stats.tx_errors++; + c7000_error(cup->cntlp); + break; + } + + /* + If at least one buffer was freed, clear + the NOBUFFER indication. + */ + + if (c7000_proc_wintr(cup) != 0) { + c7000_clearbit_busy(TB_NOBUFFER, dev); + } + + /* + Restart the channel program if there are more + buffers on the processing chain. + */ + + if (cup->proc_head != NULL) { + c7000_bld_wrt_chain(cup); + parm = (__u32)cup; + set_bit(0, (void *)&cup->IO_active); + + if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) { + CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno); + clear_bit(0, (void *)&cup->IO_active); + c7000_error(cup->cntlp); + break; + } + + dev->trans_start = jiffies; + } else { + clear_bit(0, (void *)&cup->IO_active); + cup->state = C7000_READY; + } + + break; + + /* + Disconnect message completed. Wakeup the + sleeping process in c7000_stop. + */ + + case C7000_DISC: + if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0) + break; + + if (c7000_check_csw(devstat) != 0) { + c7000_retry_io(cup); + + if (cup->state == C7000_ERROR) + wake_up(&cup->wait); + + break; + } + + cup->retries = 0; + wake_up(&cup->wait); + break; + + /* + Subchannel is now halted. Wakeup the sleeping + process in c7000_stop. Set the state to C7000_STOPPED. + */ + + case C7000_STOP: + + cup->state = C7000_STOPPED; + wake_up(&cup->wait); + break; + + /* + When in error state, stay there until the + interface is recycled. + */ + + case C7000_ERROR: + + break; + + /* + Should not reach here + */ + + default: + CPrintk(0, "c7000: c7000_intr: entered default case for unit 0x%x, state %d\n", cup->devno, cup->state); + break; + + } + + CPrintk(1, "c7000: c7000_intr: exited with state %d for unit 0x%x\n", cup->state, cup->devno); + return; +} + +/* + Fill in system validation name padding it with blanks. +*/ + +static void +c7000_fill_name(char *dst, char *src) +{ + char *tmp = dst; + int i; + + for (i = 0; i < NAMLEN; i++, tmp++) + *tmp = ' '; + + for (i = 0; i < NAMLEN && *src != '\0'; i++) + *dst++ = *src++; + + return; +} + +/* + Initialization routine called when the device is registered. +*/ + +static int +c7000_init(STRUCT_NET_DEVICE *dev) +{ + struct c7000_controller *ccp; + int i; + int unitaddr; + int irq; + + /* + Find the position of base_addr in the bases array. + */ + + for (i = 0; i < MAX_C7000; i++) + if (bases[i] == dev->base_addr) + break; + + if (i == MAX_C7000) + return(-ENODEV); + + /* + Make sure it is a C7000 type of device. + */ + + if (c7000_check_devices(dev->base_addr) != 0) { + CPrintk(0, "c7000: c7000_init: base unit 0x%lx is not the right type\n", dev->base_addr); + return(-ENODEV); + } + + /* + Initialize the device structure functions. + Note that ARP is not done on the CLAW interface. + There is no ethernet header. + */ + + dev->mtu = C7000_DATAL; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->type = ARPHRD_SLIP; + dev->tx_queue_len = C7000_TXQUEUE_LEN; + dev->flags = IFF_NOARP; + dev->open = c7000_open; + dev->stop = c7000_stop; + dev->set_config = c7000_config; + dev->hard_start_xmit = c7000_xmit; + dev->do_ioctl = c7000_ioctl; + dev->get_stats = c7000_stats; + + /* + Allocate space for a private data structure. + */ + + if ((ccp = dev->priv = kmalloc(sizeof(struct c7000_controller), GFP_KERNEL)) == NULL) { + CPrintk(0, "c7000: c7000_init: base unit 0x%lx can not be initialized\n", dev->base_addr); + return(-ENOMEM); + } + + CPrintk(1, "c7000: c7000_init: allocated a c7000_controller structure at address 0x%x\n", (int)ccp); + memset(ccp, '\0', sizeof(struct c7000_controller)); + ccp->dev = dev; + ccp->base_addr = dev->base_addr; + + /* + Populate the system validation name with default values. + */ + + c7000_fill_name(ccp->lappl, C7000_DFLT_LAPPL); + c7000_fill_name(ccp->lhost, C7000_DFLT_LHOST); + c7000_fill_name(ccp->uappl, C7000_DFLT_UAPPL); + c7000_fill_name(ccp->uhost, C7000_DFLT_UHOST); + + /* + When values have been supplied, replace the prior defaults. + */ + + if (i == 0) { + + if (lappl0 != NULL) + c7000_fill_name(ccp->lappl, lappl0); + + if (lhost0 != NULL) + c7000_fill_name(ccp->lhost, lhost0); + + if (uappl0 != NULL) + c7000_fill_name(ccp->uappl, uappl0); + + if (uhost0 != NULL) + c7000_fill_name(ccp->uhost, uhost0); + + } else if (i == 1) { + + if (lappl1 != NULL) + c7000_fill_name(ccp->lappl, lappl1); + + if (lhost1 != NULL) + c7000_fill_name(ccp->lhost, lhost1); + + if (uappl1 != NULL) + c7000_fill_name(ccp->uappl, uappl1); + + if (uhost1 != NULL) + c7000_fill_name(ccp->uhost, uhost1); + + } else if (i == 2) { + + if (lappl2 != NULL) + c7000_fill_name(ccp->lappl, lappl2); + + if (lhost2 != NULL) + c7000_fill_name(ccp->lhost, lhost2); + + if (uappl2 != NULL) + c7000_fill_name(ccp->uappl, uappl2); + + if (uhost2 != NULL) + c7000_fill_name(ccp->uhost, uhost2); + + } else { + + if (lappl3 != NULL) + c7000_fill_name(ccp->lappl, lappl3); + + if (lhost3 != NULL) + c7000_fill_name(ccp->lhost, lhost3); + + if (uappl3 != NULL) + c7000_fill_name(ccp->uappl, uappl3); + + if (uhost3 != NULL) + c7000_fill_name(ccp->uhost, uhost3); + + } + + CPrintk(1, "c7000: c7000_init: lappl = %8.8s lhost = %8.8s uappl = %8.8s uhost = %8.8s for base unit 0x%x\n", ccp->lappl, ccp->lhost, ccp->uappl, ccp->uhost, ccp->base_addr); + ccp->version = 2; + ccp->linkid = 0; + + /* + Initialize the fields in the embedded cunits + array. This type of controller occupies a range + of three contiguous device numbers. + */ + + for (i = 0; i < NUNITS; i++) { + unitaddr = dev->base_addr + i; + + /* + Get the subchannel number. + */ + + if ((irq = ccp->cunits[i].irq = get_irq_by_devno(unitaddr)) == -1) { + CPrintk(0, "c7000: c7000_init: can not get subchannel for unit 0x%x\n", unitaddr); + return(-ENODEV); + } + + /* + Get control of the subchannel. + */ + + if (request_irq(irq, c7000_intr, SA_INTERRUPT, dev->name, &ccp->cunits[i].devstat) != 0) { + CPrintk(0, "c7000: c7000_init: can not get control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr); + return(-EBUSY); + } + + CPrintk(1, "c7000: c7000_init: obtained control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr); + ccp->cunits[i].devno = unitaddr; + ccp->cunits[i].IO_active = 0; + ccp->cunits[i].state = C7000_INIT; + ccp->cunits[i].cntlp = ccp; + CPrintk(1, "c7000: c7000_init: initialized unit 0x%x on subchannel 0x%x\n", unitaddr, irq); + } + + return(0); +} + +/* + Probe for the Cisco 7000 unit base addresses. +*/ + +static void +c7000_probe(void) +{ + s390_dev_info_t d; + int i; + int j; + int idx; + + /* + Probe for up to MAX_C7000 devices. + Get the first irq into variable idx. + */ + + idx = get_irq_first(); + + for (j = 0; j < MAX_C7000; j++) { + + if (idx < 0) + break; + + /* + Continue scanning the irq's. Variable idx + maintains the location from the prior scan. + */ + + for (i = idx; i >= 0; i = get_irq_next(i)) { + + /* + Ignore invalid irq's. + */ + + if (get_dev_info_by_irq(i, &d) < 0) + continue; + + /* + A Cisco 7000 is defined as a 3088 model + type 0x61. + */ + + if (d.sid_data.cu_type == C7000_CU_TYPE && + d.sid_data.cu_model == C7000_CU_MODEL) { + CPrintk(0, "c7000_probe: unit probe found 0x%x\n", d.devno); + bases[j] = d.devno; + + /* + Skip the write irq and setup idx + to probe for the next box. + */ + + idx = get_irq_next(i + 1); + break; + } + + } + + } + + return; +} + +/* + Module loading. Register each C7000 interface found via probing + or insmod command parameters. +*/ + +int +init_module(void) +{ + int result; + int i; + + for (i = 0 ; i < MAX_C7000; i++) + bases[i] = -1; + + /* + Perform automatic detection provided it has not been disabled + by the noauto parameter. + */ + + if (noauto == 0) + c7000_probe(); + + /* + Populate bases array from the module basex parameters replacing + what probing found above. + */ + + if (base0 != -1) + bases[0] = base0; + + if (base1 != -1) + bases[1] = base1; + + if (base2 != -1) + bases[2] = base2; + + if (base3 != -1) + bases[3] = base3; + + for (i = 0; i < MAX_C7000; i++) { + + if (bases[i] == -1) + continue; + + /* + Initialize the device structure. + */ + + memset(&c7000_devices[i], '\0', sizeof(STRUCT_NET_DEVICE)); +#ifdef NEWSTUFF + strcpy(c7000_devices[i].name, ifnames[i]); +#else + c7000_devices[i].name = &ifnames[i][0]; +#endif + c7000_devices[i].base_addr = bases[i]; + c7000_devices[i].init = c7000_init; + + /* + Register the device. This creates the interface + such as ci0. + */ + + if ((result = register_netdev(&c7000_devices[i])) != 0) { + CPrintk(0, "c7000: init_module: error %d registering base unit 0x%x\n", + result, bases[i]); + c7000_devices[i].base_addr = -1; + } else { + CPrintk(1, "c7000: init_module: registered base unit 0x%x on interface %s\n", bases[i], ifnames[i]); + } + + } + + CPrintk(0, "c7000: init_module: module loaded\n"); + return(0); +} + +/* + Module unloading. Unregister the interface and free kernel + allocated memory. +*/ + +void +cleanup_module(void) +{ + int i; + int j; + struct c7000_controller *ccp; + + for (i = 0; i < MAX_C7000; i++) { + + if (bases[i] == -1) + continue; + + /* + If the device was registered, it must be unregistered + prior to unloading the module. + */ + + if (c7000_devices[i].base_addr != -1) { + + ccp = (struct c7000_controller *) c7000_devices[i].priv; + + if (ccp != NULL) { + + for (j = 0; j < NUNITS ; j++) { + CPrintk(1, "c7000: clean_module: free subchannel 0x%x for unit 0x%x\n", ccp->cunits[j].irq, ccp->cunits[j].devno); + free_irq(ccp->cunits[j].irq, &ccp->cunits[j].devstat); + } + + CPrintk(1, "c7000: clean_module: free a c7000_controller structure at address 0x%x\n", (int)ccp); + kfree(ccp); + } + + unregister_netdev(&c7000_devices[i]); + } + + bases[i] = -1; + } + + CPrintk(0, "c7000: clean_module: module unloaded\n"); + return; +} diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/ctcmain.c linux.21rc1-ac2/drivers/s390/net/ctcmain.c --- linux.21rc1/drivers/s390/net/ctcmain.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/ctcmain.c 2003-04-25 13:51:49.000000000 +0100 @@ -1,5 +1,5 @@ /* - * $Id: ctcmain.c,v 1.55 2001/12/03 14:28:45 felfert Exp $ + * $Id: ctcmain.c,v 1.57 2002/10/23 18:51:01 felfert Exp $ * * CTC / ESCON network driver * @@ -35,7 +35,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.55 $ + * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.57 $ * */ @@ -301,15 +301,23 @@ ctc_profile prof; unsigned char *trans_skb_data; + + __u16 logflags; } channel; #define CHANNEL_FLAGS_READ 0 #define CHANNEL_FLAGS_WRITE 1 #define CHANNEL_FLAGS_INUSE 2 #define CHANNEL_FLAGS_BUFSIZE_CHANGED 4 +#define CHANNEL_FLAGS_FAILED 8 #define CHANNEL_FLAGS_RWMASK 1 #define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK) +#define LOG_FLAG_ILLEGALPKT 1 +#define LOG_FLAG_ILLEGALSIZE 2 +#define LOG_FLAG_OVERRUN 4 +#define LOG_FLAG_NOMEM 8 + /** * Linked list of all detected channels. */ @@ -337,6 +345,11 @@ struct proc_dir_entry *proc_stat_entry; struct proc_dir_entry *proc_ctrl_entry; int proc_registered; + + /** + * Timer for restarting after I/O Errors + */ + fsm_timer restart_timer; } ctc_priv; /** @@ -387,7 +400,7 @@ */ static void print_banner(void) { static int printed = 0; - char vbuf[] = "$Revision: 1.55 $"; + char vbuf[] = "$Revision: 1.57 $"; char *version = vbuf; if (printed) @@ -517,6 +530,7 @@ DEV_EVENT_TXUP, DEV_EVENT_RXDOWN, DEV_EVENT_TXDOWN, + DEV_EVENT_RESTART, /** * MUST be always the last element!! */ @@ -530,6 +544,7 @@ "TX up", "RX down", "TX down", + "Restart", }; /** @@ -745,16 +760,24 @@ skb_pull(pskb, LL_HEADER_LENGTH); if ((ch->protocol == CTC_PROTO_S390) && (header->type != ETH_P_IP)) { - /** - * Check packet type only if we stick strictly - * to S/390's protocol of OS390. This only - * supports IP. Otherwise allow any packet - * type. - */ - printk(KERN_WARNING - "%s Illegal packet type 0x%04x " - "received, dropping\n", - dev->name, header->type); +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) { +#endif + /** + * Check packet type only if we stick strictly + * to S/390's protocol of OS390. This only + * supports IP. Otherwise allow any packet + * type. + */ + printk(KERN_WARNING + "%s Illegal packet type 0x%04x " + "received, dropping\n", + dev->name, header->type); + ch->logflags |= LOG_FLAG_ILLEGALPKT; +#ifndef DEBUG + } +#endif + #ifdef DEBUG ctc_dump_skb(pskb, -6); #endif @@ -767,11 +790,18 @@ if ((header->length == 0) || (header->length > skb_tailroom(pskb)) || (header->length > len)) { - printk(KERN_WARNING - "%s Illegal packet size %d " - "received (MTU=%d blocklen=%d), " - "dropping\n", dev->name, header->length, - dev->mtu, len); +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { +#endif + printk(KERN_WARNING + "%s Illegal packet size %d " + "received (MTU=%d blocklen=%d), " + "dropping\n", dev->name, header->length, + dev->mtu, len); + ch->logflags |= LOG_FLAG_ILLEGALSIZE; +#ifndef DEBUG + } +#endif #ifdef DEBUG ctc_dump_skb(pskb, -6); #endif @@ -780,10 +810,17 @@ return; } if (header->length > skb_tailroom(pskb)) { - printk(KERN_WARNING - "%s Illegal packet size %d " - "(beyond the end of received data), " - "dropping\n", dev->name, header->length); +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_OVERRUN)) { +#endif + printk(KERN_WARNING + "%s Illegal packet size %d " + "(beyond the end of received data), " + "dropping\n", dev->name, header->length); + ch->logflags |= LOG_FLAG_OVERRUN; +#ifndef DEBUG + } +#endif #ifdef DEBUG ctc_dump_skb(pskb, -6); #endif @@ -796,9 +833,16 @@ len -= (LL_HEADER_LENGTH + header->length); skb = dev_alloc_skb(pskb->len); if (!skb) { - printk(KERN_WARNING - "%s Out of memory in ctc_unpack_skb\n", - dev->name); +#ifndef DEBUG + if (!(ch->logflags & LOG_FLAG_NOMEM)) { +#endif + printk(KERN_WARNING + "%s Out of memory in ctc_unpack_skb\n", + dev->name); + ch->logflags |= LOG_FLAG_OVERRUN; +#ifndef DEBUG + } +#endif privptr->stats.rx_dropped++; return; } @@ -811,6 +855,10 @@ ctc_tty_netif_rx(skb); else netif_rx(skb); + /** + * Successful rx; reset logflags + */ + ch->logflags = 0; privptr->stats.rx_packets++; privptr->stats.rx_bytes += skb->len; if (len > 0) { @@ -1110,14 +1158,14 @@ fsm_deltimer(&ch->timer); if (len < 8) { - printk(KERN_WARNING "%s: got packet with length %d < 8\n", + printk(KERN_DEBUG "%s: got packet with length %d < 8\n", dev->name, len); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; goto again; } if (len > ch->max_bufsize) { - printk(KERN_WARNING "%s: got packet with length %d > %d\n", + printk(KERN_DEBUG "%s: got packet with length %d > %d\n", dev->name, len, ch->max_bufsize); privptr->stats.rx_dropped++; privptr->stats.rx_length_errors++; @@ -1137,7 +1185,7 @@ break; } if ((len < block_len) || (len > check_len)) { - printk(KERN_WARNING "%s: got block length %d != rx length %d\n", + printk(KERN_DEBUG "%s: got block length %d != rx length %d\n", dev->name, block_len, len); #ifdef DEBUG ctc_dump_skb(skb, 0); @@ -1756,6 +1804,16 @@ fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); } } + +static void ch_action_reinit(fsm_instance *fi, int event, void *arg) +{ + channel *ch = (channel *)arg; + net_device *dev = ch->netdev; + ctc_priv *privptr = dev->priv; + + ch_action_iofatal(fi, event, arg); + fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev); +} /** * The statemachine for a channel. @@ -1777,7 +1835,7 @@ { CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode }, { CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr }, { CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio }, @@ -1792,7 +1850,7 @@ { CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr }, { CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode }, { CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio }, @@ -1803,7 +1861,7 @@ { CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr }, { CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail }, { CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio }, { CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, @@ -1813,7 +1871,7 @@ { CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc }, // { CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry }, { CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx }, @@ -1824,7 +1882,7 @@ { CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr }, { CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr }, { CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio }, @@ -1833,7 +1891,7 @@ { CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop }, { CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop }, { CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop }, @@ -1857,7 +1915,7 @@ { CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry }, { CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry }, { CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - { CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_iofatal }, + { CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit }, { CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio }, @@ -2274,6 +2332,7 @@ ctc_priv *privptr = dev->priv; int direction; + fsm_deltimer(&privptr->restart_timer); fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); for (direction = READ; direction <= WRITE; direction++) { channel *ch = privptr->channel[direction]; @@ -2301,6 +2360,18 @@ } } +static void dev_action_restart(fsm_instance *fi, int event, void *arg) +{ + net_device *dev = (net_device *)arg; + ctc_priv *privptr = dev->priv; + + printk(KERN_DEBUG "%s: Restarting\n", dev->name); + dev_action_stop(fi, event, arg); + fsm_event(privptr->fsm, DEV_EVENT_STOP, dev); + fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC, + DEV_EVENT_START, dev); +} + /** * Called from channel statemachine * when a channel is up and running. @@ -2402,43 +2473,50 @@ } static const fsm_node dev_fsm[] = { - { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start }, - { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start }, - { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, - { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, - - { DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start }, - { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, - { DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, - { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown }, - - { DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start }, - { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, - { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, - { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown }, - - { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, - { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, - - { DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown }, - - { DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, - { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown }, - - { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, - { DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown }, - { DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown }, - { DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop }, - { DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop }, + { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, + { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, + { DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, + { DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, + { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown }, + { DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start }, + { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, + { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, + { DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown }, + { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, + { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown }, + { DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, + { DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown }, + { DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, + + { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, + { DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown }, + { DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown }, + { DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop }, + { DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop }, + { DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart }, }; static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node); @@ -3042,7 +3120,7 @@ proc_net_unregister(ctc_dir.low_ino); #endif } -#endif MODULE +#endif /* MODULE */ /** * Create a device specific subdirectory in /proc/net/ctc/ with the @@ -3501,6 +3579,7 @@ return NULL; } fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); + fsm_settimer(privptr->fsm, &privptr->restart_timer); dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2; dev->hard_start_xmit = ctc_tx; dev->open = ctc_open; @@ -3552,6 +3631,7 @@ case chandev_status_gone: for (direction = READ; direction <= WRITE; direction++) { channel *ch = privptr->channel[direction]; + ch->flags |= CHANNEL_FLAGS_FAILED; fsm_event(ch->fsm, CH_EVENT_MC_FAIL, ch); } printk(KERN_WARNING @@ -3560,6 +3640,12 @@ case chandev_status_all_chans_good: for (direction = READ; direction <= WRITE; direction++) { channel *ch = privptr->channel[direction]; + if (!(ch->flags & CHANNEL_FLAGS_FAILED)) + fsm_event(ch->fsm, CH_EVENT_MC_FAIL, ch); + } + for (direction = READ; direction <= WRITE; direction++) { + channel *ch = privptr->channel[direction]; + ch->flags &= ~CHANNEL_FLAGS_FAILED; fsm_event(ch->fsm, CH_EVENT_MC_GOOD, ch); } printk(KERN_WARNING @@ -3857,7 +3943,7 @@ } #define ctc_init init_module -#endif MODULE +#endif /* MODULE */ /** * Initialize module. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/ctctty.c linux.21rc1-ac2/drivers/s390/net/ctctty.c --- linux.21rc1/drivers/s390/net/ctctty.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/ctctty.c 2003-04-25 13:51:49.000000000 +0100 @@ -1,5 +1,5 @@ /* - * $Id: ctctty.c,v 1.8 2001/05/16 16:28:31 felfert Exp $ + * $Id: ctctty.c,v 1.10 2002/06/05 14:24:45 felfert Exp $ * * CTC / ESCON network driver, tty interface. * @@ -88,6 +88,7 @@ struct semaphore write_sem; struct tq_struct tq; struct timer_list stoptimer; + struct timer_list flowtimer; } ctc_tty_info; /* Description of one CTC-tty */ @@ -114,7 +115,7 @@ static char *ctc_ttyname = CTC_TTY_NAME; #endif -char *ctc_tty_revision = "$Revision: 1.8 $"; +char *ctc_tty_revision = "$Revision: 1.10 $"; static __u32 ctc_tty_magic = CTC_ASYNC_MAGIC; static int ctc_tty_shuttingdown = 0; @@ -163,35 +164,44 @@ static int ctc_tty_readmodem(ctc_tty_info *info) { - int ret = 1; + int c; struct tty_struct *tty; + struct sk_buff *skb; - if ((tty = info->tty)) { - if (info->mcr & UART_MCR_RTS) { - int c = TTY_FLIPBUF_SIZE - tty->flip.count; - struct sk_buff *skb; - - if ((c > 0) && (skb = skb_dequeue(&info->rx_queue))) { - int len = skb->len; - if (len > c) - len = c; - memcpy(tty->flip.char_buf_ptr, skb->data, len); - skb_pull(skb, len); - memset(tty->flip.flag_buf_ptr, 0, len); - tty->flip.count += len; - tty->flip.char_buf_ptr += len; - tty->flip.flag_buf_ptr += len; - tty_flip_buffer_push(tty); - if (skb->len > 0) - skb_queue_head(&info->rx_queue, skb); - else { - kfree_skb(skb); - ret = skb_queue_len(&info->rx_queue); - } - } + if (!(tty = info->tty)) + return 0; + + /* If the upper layer is flow blocked or just + * has no room for data we schedule a timer to + * try again later - wilder + */ + c = TTY_FLIPBUF_SIZE - tty->flip.count; + if ( !(info->mcr & UART_MCR_RTS) || (c <= 0) ) { + /* can't do any work now, wake up later */ + mod_timer(&info->flowtimer, jiffies+(HZ/100) ); + return 0; + } + + if ((skb = skb_dequeue(&info->rx_queue))) { + int len = skb->len; + if (len > c) + len = c; + memcpy(tty->flip.char_buf_ptr, skb->data, len); + skb_pull(skb, len); + memset(tty->flip.flag_buf_ptr, 0, len); + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + tty->flip.flag_buf_ptr += len; + tty_flip_buffer_push(tty); + + if (skb->len > 0){ + skb_queue_head(&info->rx_queue, skb); + }else { + kfree_skb(skb); } } - return ret; + + return skb_queue_len(&info->rx_queue); } void @@ -234,6 +244,11 @@ dev_kfree_skb(skb); return; } + if ( !(info->tty) ) { + dev_kfree_skb(skb); + return; + } + if (skb->len < 6) { dev_kfree_skb(skb); return; @@ -270,7 +285,12 @@ /* Direct deliver failed or queue wasn't empty. * Queue up for later dequeueing via timer-irq. */ - skb_queue_tail(&info->rx_queue, skb); + if (skb_queue_len(&info->rx_queue) < 50) + skb_queue_tail(&info->rx_queue, skb); + else { + kfree_skb(skb); + printk(KERN_DEBUG "ctctty: RX overrun\n"); + } /* Schedule dequeuing */ queue_task(&info->tq, &tq_immediate); mark_bh(IMMEDIATE_BH); @@ -330,6 +350,13 @@ skb_queue_head(&info->tx_queue, skb); else kfree_skb(skb); + + /* The connection is not up yet, try again in one second. - wilder */ + if ( rc == -EBUSY ){ + mod_timer(&info->flowtimer, jiffies+(HZ) ); + return 0; + } + } else { struct tty_struct *tty = info->tty; @@ -484,6 +511,18 @@ info->flags &= ~CTC_ASYNC_NETDEV_OPEN; } +/* Run from the timer queue when we are flow blocked + * to kick start the bottom half - wilder */ +static void +ctc_tty_startupbh(unsigned long data) +{ + ctc_tty_info *info = (ctc_tty_info *)data; + if (( !ctc_tty_shuttingdown) && info) { + queue_task(&info->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. @@ -577,6 +616,12 @@ if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_write_room")) return 0; + +/* wilder */ + if (skb_queue_len(&info->tx_queue) > 10 ) { + return 0; + } + return CTC_TTY_XMIT_SIZE; } @@ -1066,7 +1111,7 @@ #endif return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1262,6 +1307,9 @@ init_timer(&info->stoptimer); info->stoptimer.function = ctc_tty_stopdev; info->stoptimer.data = (unsigned long)info; + init_timer(&info->flowtimer); + info->flowtimer.function = ctc_tty_startupbh; + info->flowtimer.data = (unsigned long)info; info->mcr = UART_MCR_RTS; } return 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/fsm.h linux.21rc1-ac2/drivers/s390/net/fsm.h --- linux.21rc1/drivers/s390/net/fsm.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/fsm.h 2003-04-25 13:51:49.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: fsm.h,v 1.4 2001/09/24 10:38:02 mschwide Exp $ +/* $Id: fsm.h,v 1.5 2002/08/05 09:07:56 heicarst Exp $ */ #ifndef _FSM_H_ #define _FSM_H_ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/iucv.c linux.21rc1-ac2/drivers/s390/net/iucv.c --- linux.21rc1/drivers/s390/net/iucv.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/iucv.c 2003-04-25 13:52:30.000000000 +0100 @@ -1,5 +1,5 @@ /* - * $Id: iucv.c,v 1.32 2002/02/12 21:52:20 felfert Exp $ + * $Id: iucv.c,v 1.39 2003/02/14 15:45:15 felfert Exp $ * * IUCV network driver * @@ -29,7 +29,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.32 $ + * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.39 $ * */ @@ -302,7 +302,7 @@ if (debuglevel < 3) return; - printk(KERN_DEBUG __FUNCTION__ ": %s\n", title); + printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, title); printk(" "); for (i = 0; i < len; i++) { if (!(i % 16) && i != 0) @@ -318,7 +318,7 @@ #define iucv_debug(lvl, fmt, args...) \ do { \ if (debuglevel >= lvl) \ - printk(KERN_DEBUG __FUNCTION__ ": " fmt "\n" , ## args); \ + printk(KERN_DEBUG __FUNCTION__ ": " fmt "\n", ## args); \ } while (0) #else @@ -338,7 +338,7 @@ static void iucv_banner(void) { - char vbuf[] = "$Revision: 1.32 $"; + char vbuf[] = "$Revision: 1.39 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -384,6 +384,14 @@ return -ENOMEM; } memset(iucv_param_pool, 0, sizeof(iucv_param) * PARAM_POOL_SIZE); +#if 0 + /* Show parameter pool on startup */ + { + int i; + for (i = 0; i < PARAM_POOL_SIZE; i++) + printk("iparm[%d] at %p\n", i, &iucv_param_pool[i]); + } +#endif /* Initialize task queue */ INIT_LIST_HEAD(&iucv_tq.list); @@ -429,7 +437,7 @@ grab_param(void) { iucv_param *ret; - static int i = 0; + int i = 0; while (atomic_compare_and_swap(0, 1, &iucv_param_pool[i].in_use)) { i++; @@ -1047,6 +1055,7 @@ iucv_handle_t handle, void *pgm_data) { iparml_control *parm; + iparml_control local_parm; struct list_head *lh; ulong b2f0_result = 0; ulong flags; @@ -1103,24 +1112,48 @@ EBC_TOUPPER(parm->iptarget, sizeof(parm->iptarget)); } + /* In order to establish an IUCV connection, the procedure is: + * + * b2f0(CONNECT) + * take the ippathid from the b2f0 call + * register the handler to the ippathid + * + * Unfortunately, the ConnectionEstablished message gets sent after the + * b2f0(CONNECT) call but before the register is handled. + * + * In order for this race condition to be eliminated, the IUCV Control + * Interrupts must be disabled for the above procedure. + * + * David Kennedy + */ + + /* Enable everything but IUCV Control messages */ + iucv_setmask(~(IUCVControlInterruptsFlag)); + parm->ipflags1 = (__u8)flags1; b2f0_result = b2f0(CONNECT, parm); + memcpy(&local_parm, parm, sizeof(local_parm)); + release_param(parm); + parm = &local_parm; if (b2f0_result) { - release_param(parm); + iucv_setmask(~0); return b2f0_result; } add_pathid_result = iucv_add_pathid(parm->ippathid, h); *pathid = parm->ippathid; + /* Enable everything again */ + iucv_setmask(~0); + if (msglim) *msglim = parm->ipmsglim; if (flags1_out) *flags1_out = (parm->ipflags1 & IPPRTY) ? IPPRTY : 0; if (add_pathid_result) { - iucv_sever(parm->ippathid, no_memory); + iucv_sever(*pathid, no_memory); printk(KERN_WARNING "%s: add_pathid failed with rc =" " %d\n", __FUNCTION__, add_pathid_result); return(add_pathid_result); @@ -1736,6 +1769,7 @@ { iparml_db *parm; ulong b2f0_result; + iucv_param save_param; iucv_debug(2, "entering"); @@ -1752,7 +1786,13 @@ parm->ipmsgtag = msgtag; parm->ipflags1 = (IPNORPY | flags1); /* one way priority message */ + memcpy((void *)&save_param, (void *)parm, sizeof(iucv_param)); b2f0_result = b2f0(SEND, parm); + if (b2f0_result != 0) { + printk("b2f0 call returned %lx\n", b2f0_result); + iucv_dumpit("PL before:", &save_param, sizeof(iucv_param)); + iucv_dumpit("PL after:", parm, sizeof(iucv_param)); + } if ((b2f0_result == 0) && (msgid)) *msgid = parm->ipmsgid; @@ -2103,6 +2143,24 @@ return b2f0_result; } +void +iucv_setmask_cpu0 (void *result) +{ + iparml_set_mask *parm; + + if (smp_processor_id() != 0) + return; + + iucv_debug(1, "entering"); + parm = (iparml_set_mask *)grab_param(); + parm->ipmask = *((__u8*)result); + *((ulong *)result) = b2f0(SETMASK, parm); + release_param(parm); + + iucv_debug(1, "b2f0_result = %ld", *((ulong *)result)); + iucv_debug(1, "exiting"); +} + /* * Name: iucv_setmask * Purpose: This function enables or disables the following IUCV @@ -2113,28 +2171,25 @@ * 0x40 - Priority_MessagePendingInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag + * 0x08 - IUCVControlInterruptsFlag * Output: NA * Return: b2f0_result - return code from CP */ int iucv_setmask (int SetMaskFlag) { - iparml_set_mask *parm; - ulong b2f0_result = 0; - - iucv_debug(1, "entering"); - - parm = (iparml_set_mask *)grab_param(); - - parm->ipmask = (__u8)SetMaskFlag; - - b2f0_result = b2f0(SETMASK, parm); - release_param(parm); + union { + ulong result; + __u8 param; + } u; - iucv_debug(1, "b2f0_result = %ld", b2f0_result); - iucv_debug(1, "exiting"); + u.param = SetMaskFlag; + if (smp_processor_id() == 0) + iucv_setmask_cpu0(&u); + else + smp_call_function(iucv_setmask_cpu0, &u, 0, 1); - return b2f0_result; + return u.result; } /** @@ -2300,9 +2355,11 @@ case 0x02: /*connection complete */ if (h) { if (interrupt->ConnectionComplete) + { interrupt->ConnectionComplete( (iucv_ConnectionComplete *)int_buf, h->pgm_data); + } else iucv_debug(1, "ConnectionComplete not called"); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/iucv.h linux.21rc1-ac2/drivers/s390/net/iucv.h --- linux.21rc1/drivers/s390/net/iucv.h 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/iucv.h 2003-04-25 13:52:30.000000000 +0100 @@ -62,6 +62,8 @@ #define Priority_MessagePendingInterruptsFlag 0x40 #define Nonpriority_MessageCompletionInterruptsFlag 0x20 #define Priority_MessageCompletionInterruptsFlag 0x10 +#define IUCVControlInterruptsFlag 0x08 + /* * Mapping of external interrupt buffers should be used with the corresponding * interrupt types. @@ -735,6 +737,7 @@ * 0x40 - Priority_MessagePendingInterruptsFlag * 0x20 - Nonpriority_MessageCompletionInterruptsFlag * 0x10 - Priority_MessageCompletionInterruptsFlag + * 0x08 - IUCVControlInterruptsFlag * Output: NA * Return: Return code from CP IUCV call. */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/Makefile linux.21rc1-ac2/drivers/s390/net/Makefile --- linux.21rc1/drivers/s390/net/Makefile 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/Makefile 2003-04-22 16:44:37.000000000 +0100 @@ -12,6 +12,7 @@ obj-$(CONFIG_IUCV) += iucv.o fsm.o obj-$(CONFIG_CTC) += ctc.o fsm.o obj-$(CONFIG_IUCV) += netiucv.o +obj-$(CONFIG_C7000) += c7000.o include $(TOPDIR)/Rules.make diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/net/netiucv.c linux.21rc1-ac2/drivers/s390/net/netiucv.c --- linux.21rc1/drivers/s390/net/netiucv.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/net/netiucv.c 2003-04-25 13:52:30.000000000 +0100 @@ -1,5 +1,5 @@ /* - * $Id: netiucv.c,v 1.17 2002/02/12 21:52:20 felfert Exp $ + * $Id: netiucv.c,v 1.21 2002/12/09 18:40:44 mschwide Exp $ * * IUCV network driver * @@ -28,7 +28,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * RELEASE-TAG: IUCV network driver $Revision: 1.17 $ + * RELEASE-TAG: IUCV network driver $Revision: 1.21 $ * */ @@ -96,6 +96,8 @@ unsigned long txlen; unsigned long tx_time; struct timeval send_stamp; + unsigned long tx_pending; + unsigned long tx_max_pending; } connection_profile; /** @@ -108,6 +110,7 @@ struct sk_buff *rx_buff; struct sk_buff *tx_buff; struct sk_buff_head collect_queue; + struct sk_buff_head commit_queue; spinlock_t collect_lock; int collect_len; int max_buffsize; @@ -443,6 +446,10 @@ iucv_connection *conn = (iucv_connection *)pgm_data; iucv_event ev; +#ifdef DEBUG + printk(KERN_DEBUG "%s() called\n", __FUNCTION__); +#endif + ev.conn = conn; ev.data = (void *)eib; fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, &ev); @@ -619,10 +626,11 @@ iucv_MessageComplete *eib = (iucv_MessageComplete *)ev->data; netiucv_priv *privptr = NULL; /* Shut up, gcc! skb is always below 2G. */ - struct sk_buff *skb = (struct sk_buff *)(unsigned long)eib->ipmsgtag; + __u32 single_flag = eib->ipmsgtag; __u32 txbytes = 0; __u32 txpackets = 0; __u32 stat_maxcq = 0; + struct sk_buff *skb; unsigned long saveflags; ll_header header; @@ -632,13 +640,17 @@ fsm_deltimer(&conn->timer); if (conn && conn->netdev && conn->netdev->priv) privptr = (netiucv_priv *)conn->netdev->priv; - if (skb) { + conn->prof.tx_pending--; + if (single_flag) { + if ((skb = skb_dequeue(&conn->commit_queue))) { + atomic_dec(&skb->users); + dev_kfree_skb_any(skb); + } if (privptr) { privptr->stats.tx_packets++; privptr->stats.tx_bytes += (skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN); } - dev_kfree_skb_any(skb); } conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head; conn->tx_buff->len = 0; @@ -672,11 +684,17 @@ conn->tx_buff->data, conn->tx_buff->len); conn->prof.doios_multi++; conn->prof.txlen += conn->tx_buff->len; + conn->prof.tx_pending++; + if (conn->prof.tx_pending > conn->prof.tx_max_pending) + conn->prof.tx_max_pending = conn->prof.tx_pending; if (rc != 0) { fsm_deltimer(&conn->timer); + conn->prof.tx_pending--; fsm_newstate(fi, CONN_STATE_IDLE); if (privptr) privptr->stats.tx_errors += txpackets; + printk(KERN_DEBUG "iucv_send returned %08x\n", + rc); } else { if (privptr) { privptr->stats.tx_packets += txpackets; @@ -807,10 +825,15 @@ printk(KERN_DEBUG "%s('%s'): connecting ...\n", conn->netdev->name, conn->userid); #endif + + /* We must set the state before calling iucv_connect because the callback + * handler could be called at any point after the connection request is + * sent. */ + + fsm_newstate(fi, CONN_STATE_SETUPWAIT); rc = iucv_connect(&(conn->pathid), NETIUCV_QUEUELEN_DEFAULT, iucvMagic, conn->userid, iucv_host, 0, NULL, NULL, conn->handle, conn); - fsm_newstate(fi, CONN_STATE_SETUPWAIT); switch (rc) { case 0: return; @@ -885,6 +908,7 @@ if (conn->handle) iucv_unregister_program(conn->handle); conn->handle = 0; + netiucv_purge_skb_queue(&conn->commit_queue); fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev); } @@ -928,6 +952,7 @@ { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx }, { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone }, + { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone }, }; static const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node); @@ -1130,14 +1155,21 @@ CONN_EVENT_TIMER, conn); conn->prof.send_stamp = xtime; - rc = iucv_send(conn->pathid, NULL, 0, 0, - /* Shut up, gcc! nskb is always below 2G. */ - (__u32)(((unsigned long)nskb)&0xffffffff), 0, - nskb->data, nskb->len); + rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */, + 0, nskb->data, nskb->len); conn->prof.doios_single++; conn->prof.txlen += skb->len; + conn->prof.tx_pending++; + if (conn->prof.tx_pending > conn->prof.tx_max_pending) + conn->prof.tx_max_pending = conn->prof.tx_pending; if (rc != 0) { + netiucv_priv *privptr; fsm_deltimer(&conn->timer); + fsm_newstate(conn->fsm, CONN_STATE_IDLE); + conn->prof.tx_pending--; + privptr = (netiucv_priv *)conn->netdev->priv; + if (privptr) + privptr->stats.tx_errors++; if (copied) dev_kfree_skb(nskb); else { @@ -1148,9 +1180,13 @@ skb_pull(skb, NETIUCV_HDRLEN); skb_trim(skb, skb->len - NETIUCV_HDRLEN); } + printk(KERN_DEBUG "iucv_send returned %08x\n", + rc); } else { if (copied) dev_kfree_skb(skb); + atomic_inc(&nskb->users); + skb_queue_tail(&conn->commit_queue, nskb); } } @@ -1549,12 +1585,7 @@ if (!(dev = find_netdev_by_ino(ino))) return -ENODEV; privptr = (netiucv_priv *)dev->priv; - privptr->conn->prof.maxmulti = 0; - privptr->conn->prof.maxcqueue = 0; - privptr->conn->prof.doios_single = 0; - privptr->conn->prof.doios_multi = 0; - privptr->conn->prof.txlen = 0; - privptr->conn->prof.tx_time = 0; + memset(&(privptr->conn->prof), 0, sizeof(privptr->conn->prof)); return count; } @@ -1593,6 +1624,10 @@ privptr->conn->prof.txlen); p += sprintf(p, "Max. TX IO-time: %ld\n", privptr->conn->prof.tx_time); + p += sprintf(p, "Pending transmits: %ld\n", + privptr->conn->prof.tx_pending); + p += sprintf(p, "Max. pending transmits: %ld\n", + privptr->conn->prof.tx_max_pending); } l = strlen(sbuf); p = sbuf; @@ -1833,6 +1868,7 @@ if (conn) { memset(conn, 0, sizeof(iucv_connection)); skb_queue_head_init(&conn->collect_queue); + skb_queue_head_init(&conn->commit_queue); conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; conn->netdev = dev; @@ -2005,7 +2041,7 @@ static void netiucv_banner(void) { - char vbuf[] = "$Revision: 1.17 $"; + char vbuf[] = "$Revision: 1.21 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { @@ -2066,7 +2102,7 @@ netiucv_proc_create_main(); while (p) { - if (isalnum(*p)) { + if (isalnum(*p) || (*p == '$')) { username[i++] = *p++; username[i] = '\0'; if (i > 8) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/s390io.c linux.21rc1-ac2/drivers/s390/s390io.c --- linux.21rc1/drivers/s390/s390io.c 2003-04-22 16:39:39.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/s390io.c 2003-04-25 13:48:57.000000000 +0100 @@ -1,7 +1,7 @@ /* * drivers/s390/s390io.c * S/390 common I/O routines - * $Revision: 1.171.2.21 $ + * $Revision: 1.241 $ * * S390 version * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, @@ -116,6 +116,11 @@ ##args); \ } while (0) +#define CIO_HEX_EVENT(imp, args...) do { \ + if (cio_debug_initialized) \ + debug_event(cio_debug_trace_id, imp, ##args); \ + } while (0) + #undef CONFIG_DEBUG_IO #define CONFIG_DEBUG_CRW #define CONFIG_DEBUG_CHSC @@ -175,14 +180,13 @@ int s390_start_IO (int irq, ccw1_t * cpa, unsigned long user_intparm, __u8 lpm, unsigned long flag); -static int s390_send_nop(int irq, __u8 lpm); - #ifdef CONFIG_PROC_FS static int chan_proc_init (void); #endif static inline void do_adapter_IO (__u32 intparm); +static void s390_schedule_path_verification(unsigned long irq); int s390_DevicePathVerification (int irq, __u8 domask); int s390_register_adapter_interrupt (adapter_int_handler_t handler); int s390_unregister_adapter_interrupt (adapter_int_handler_t handler); @@ -623,7 +627,6 @@ /* End of blacklist handling */ void s390_displayhex (char *str, void *ptr, s32 cnt); -void s390_displayhex2 (char *str, void *ptr, s32 cnt, int level); void s390_displayhex (char *str, void *ptr, s32 cnt) @@ -644,26 +647,6 @@ } } -void -s390_displayhex2 (char *str, void *ptr, s32 cnt, int level) -{ - s32 cnt1, cnt2, maxcnt2; - u32 *currptr = (__u32 *) ptr; - char buffer[cnt * 12]; - - debug_text_event (cio_debug_msg_id, level, str); - - for (cnt1 = 0; cnt1 < cnt; cnt1 += 16) { - sprintf (buffer, "%08lX ", (unsigned long) currptr); - maxcnt2 = cnt - cnt1; - if (maxcnt2 > 16) - maxcnt2 = 16; - for (cnt2 = 0; cnt2 < maxcnt2; cnt2 += 4) - sprintf (buffer, "%08X ", *currptr++); - } - debug_text_event (cio_debug_msg_id, level, buffer); -} - static int __init cio_setup (char *parm) { @@ -815,93 +798,6 @@ return; } - -/* - * Function: s390_send_nop - * - * sends a nop CCW to the specified subchannel down the given path(s) - */ -static int -s390_send_nop(int irq, __u8 lpm) -{ - char dbf_txt[15]; - ccw1_t *nop_ccw; - devstat_t devstat; - devstat_t *pdevstat = &devstat; - unsigned long flags; - - int irq_ret = 0; - int inlreq = 0; - - SANITY_CHECK(irq); - - if (!ioinfo[irq]->ui.flags.oper) - /* no sense in trying */ - return -ENODEV; - - sprintf(dbf_txt, "snop%x", irq); - CIO_TRACE_EVENT(5, dbf_txt); - - if (!ioinfo[irq]->ui.flags.ready) { - /* - * If there's no handler, use our dummy handler. - */ - irq_ret = request_irq (irq, - init_IRQ_handler, - SA_PROBE, - "SNOP", - pdevstat); - if (!irq_ret) - inlreq = 1; - } else { - pdevstat = ioinfo[irq]->irq_desc.dev_id; - } - - if (irq_ret) - return irq_ret; - - s390irq_spin_lock_irqsave (irq, flags); - - if (init_IRQ_complete) - nop_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA); - else - nop_ccw = alloc_bootmem_low (sizeof (ccw1_t)); - - nop_ccw->cmd_code = CCW_CMD_NOOP; - nop_ccw->cda = 0; - nop_ccw->count = 0; - nop_ccw->flags = CCW_FLAG_SLI; - - memset (pdevstat, '\0', sizeof (devstat_t)); - - irq_ret = s390_start_IO (irq, nop_ccw, 0xE2D5D6D7, lpm, - DOIO_WAIT_FOR_INTERRUPT - | DOIO_TIMEOUT - | DOIO_DONT_CALL_INTHDLR - | DOIO_VALID_LPM); - - if (irq_ret == -ETIMEDOUT) { - - /* better cancel... */ - cancel_IO(irq); - } - - if (init_IRQ_complete) - kfree (nop_ccw); - else - free_bootmem ((unsigned long) nop_ccw, sizeof (ccw1_t)); - - s390irq_spin_unlock_irqrestore (irq, flags); - - if (inlreq) - free_irq (irq, pdevstat); - - return irq_ret; - -} - - - /* * Note : internal use of irqflags SA_PROBE for NOT path grouping * @@ -930,7 +826,7 @@ if (ioinfo[irq]->st) return -ENODEV; - sprintf (dbf_txt, "reqs%x", irq); + sprintf (dbf_txt, "reqsp%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -1054,6 +950,7 @@ s390irq_spin_unlock_irqrestore (irq, flags); udelay (200000); /* 200 ms */ s390irq_spin_lock_irqsave (irq, flags); + ret = disable_subchannel (irq); if (ret == -EBUSY) { @@ -1130,7 +1027,7 @@ if (!ioinfo[irq]->ui.flags.ready) return -ENODEV; - sprintf (dbf_txt, "dirq%x", irq); + sprintf (dbf_txt, "disirq%x", irq); CIO_TRACE_EVENT (4, dbf_txt); s390irq_spin_lock_irqsave (irq, flags); @@ -1157,7 +1054,7 @@ if (!ioinfo[irq]->ui.flags.ready) return -ENODEV; - sprintf (dbf_txt, "eirq%x", irq); + sprintf (dbf_txt, "enirq%x", irq); CIO_TRACE_EVENT (4, dbf_txt); s390irq_spin_lock_irqsave (irq, flags); @@ -1183,7 +1080,7 @@ SANITY_CHECK (irq); - sprintf (dbf_txt, "esch%x", irq); + sprintf (dbf_txt, "ensch%x", irq); CIO_TRACE_EVENT (2, dbf_txt); /* @@ -1268,7 +1165,7 @@ SANITY_CHECK (irq); - sprintf (dbf_txt, "dsch%x", irq); + sprintf (dbf_txt, "dissch%x", irq); CIO_TRACE_EVENT (2, dbf_txt); if (ioinfo[irq]->ui.flags.busy) { @@ -1367,6 +1264,7 @@ { unsigned long flags; /* PSW flags */ long cr6 __attribute__ ((aligned (8))); + cpuid_t cpuid; asm volatile ("STCK %0":"=m" (irq_IPL_TOD)); @@ -1406,8 +1304,13 @@ global_pgid = (pgid_t *)alloc_bootmem(sizeof(pgid_t)); - global_pgid->cpu_addr = *(__u16 *) __LC_CPUADDR; - global_pgid->cpu_id = ((cpuid_t *) __LC_CPUID)->ident; + cpuid = *(cpuid_t*) __LC_CPUID; + + if (MACHINE_NEW_STIDP) + global_pgid->cpu_addr = 0x8000; + else + global_pgid->cpu_addr = *(__u16 *) __LC_CPUADDR; + global_pgid->cpu_id = cpuid.ident; global_pgid->cpu_model = ((cpuid_t *) __LC_CPUID)->machine; global_pgid->tod_high = *(__u32 *) & irq_IPL_TOD; @@ -1445,7 +1348,6 @@ { /* flags */ int ccode; int ret = 0; - char buffer[80]; char dbf_txt[15]; SANITY_CHECK (irq); @@ -1512,7 +1414,14 @@ /* * Issue "Start subchannel" and process condition code */ - ccode = ssch (irq, &(ioinfo[irq]->orb)); + if (flag & DOIO_USE_DIAG98) { + ioinfo[irq]->orb.key = STORAGE_ACC_KEY; + ioinfo[irq]->orb.cpa = (__u32) pfix_get_addr(ioinfo[irq]-> + orb.cpa); + ccode = diag98 (irq, &(ioinfo[irq]->orb)); + } else { + ccode = ssch (irq, &(ioinfo[irq]->orb)); + } sprintf (dbf_txt, "ccode:%d", ccode); CIO_TRACE_EVENT (4, dbf_txt); @@ -1674,6 +1583,16 @@ break; case 1: /* status pending */ + + /* + * Don't do an inline processing of pending interrupt conditions + * while doing async. I/O. The interrupt will pop up when we are + * enabled again and the I/O can be retried. + */ + if (!ioinfo[irq]->ui.flags.syncio) { + ret = -EBUSY; + break; + } ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION | DEVSTAT_STATUS_PENDING; @@ -1715,14 +1634,6 @@ * than the one used (i.e. path available mask is non-zero). */ if (ioinfo[irq]->devstat.ii.irb.scsw.cc == 3) { - if (flag & DOIO_VALID_LPM) { - ioinfo[irq]->opm &= - ~(ioinfo[irq]->devstat.ii.irb.esw.esw1. - lpum); - } else { - ioinfo[irq]->opm = 0; - - } if (ioinfo[irq]->opm == 0) { ret = -ENODEV; @@ -1777,39 +1688,23 @@ #endif if (cio_debug_initialized) { stsch (irq, &(ioinfo[irq]->schib)); - - sprintf (buffer, - "s390_start_IO(%04X) - irb for " - "device %04X, after status pending\n", - irq, ioinfo[irq]->devstat.devno); - - s390_displayhex2 (buffer, - &(ioinfo[irq]->devstat.ii. - irb), sizeof (irb_t), 2); - - sprintf (buffer, - "s390_start_IO(%04X) - schib for " - "device %04X, after status pending\n", - irq, ioinfo[irq]->devstat.devno); - - s390_displayhex2 (buffer, - &(ioinfo[irq]->schib), - sizeof (schib_t), 2); + + sprintf(dbf_txt, "sp%x", irq); + CIO_TRACE_EVENT(2, dbf_txt); + CIO_TRACE_EVENT(2, "irb:"); + CIO_HEX_EVENT(2, &(ioinfo[irq]->devstat.ii.irb), + sizeof (irb_t)); + CIO_TRACE_EVENT(2, "schib:"); + CIO_HEX_EVENT(2, &(ioinfo[irq]->schib), + sizeof (schib_t)); if (ioinfo[irq]->devstat. flag & DEVSTAT_FLAG_SENSE_AVAIL) { - sprintf (buffer, - "s390_start_IO(%04X) " - "- sense data for device %04X," - " after status pending\n", - irq, - ioinfo[irq]->devstat.devno); - - s390_displayhex2 (buffer, - ioinfo[irq]->irq_desc. - dev_id->ii.sense.data, - ioinfo[irq]->irq_desc. - dev_id->rescnt, 2); + CIO_TRACE_EVENT(2, "sense:"); + CIO_HEX_EVENT(2, ioinfo[irq]->irq_desc. + dev_id->ii.sense.data, + ioinfo[irq]->irq_desc. + dev_id->rescnt); } } @@ -1860,14 +1755,10 @@ #endif if (cio_debug_initialized) { stsch (irq, &(ioinfo[irq]->schib)); - - sprintf (buffer, "s390_start_IO(%04X) - schib for " - "device %04X, after 'not oper' status\n", - irq, ioinfo[irq]->devstat.devno); - - s390_displayhex2 (buffer, - &(ioinfo[irq]->schib), - sizeof (schib_t), 2); + sprintf(dbf_txt, "no%x", irq); + CIO_TRACE_EVENT(2, dbf_txt); + CIO_HEX_EVENT(2, &(ioinfo[irq]->schib), + sizeof (schib_t)); } break; @@ -1908,6 +1799,9 @@ sprintf (dbf_txt, "doIO%x", irq); CIO_TRACE_EVENT (4, dbf_txt); + if (ioinfo[irq]->ui.flags.noio) + return -EBUSY; + /* * Note: We ignore the device operational status - if not operational, * the SSCH will lead to an -ENODEV condition ... @@ -1963,7 +1857,7 @@ SANITY_CHECK (irq); - sprintf (dbf_txt, "rsIO%x", irq); + sprintf (dbf_txt, "resIO%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -1982,7 +1876,6 @@ break; case 1: - s390_process_IRQ (irq); ret = -EBUSY; break; @@ -2025,6 +1918,9 @@ SANITY_CHECK (irq); + if (ioinfo[irq]->ui.flags.noio) + return -EBUSY; + /* * we only allow for halt_IO if the device has an I/O handler associated */ @@ -2038,8 +1934,7 @@ if (ioinfo[irq]->ui.flags.w4sense) { return 0; } - CIO_TRACE_EVENT (2, "haltIO"); - sprintf (dbf_txt, "%x", irq); + sprintf (dbf_txt, "haltIO%x", irq); CIO_TRACE_EVENT (2, dbf_txt); /* @@ -2158,6 +2053,16 @@ case 1: /* status pending */ + /* + * Don't do an inline processing of pending interrupt conditions + * while doing async. I/O. The interrupt will pop up when we are + * enabled again and the I/O can be retried. + */ + if (!ioinfo[irq]->ui.flags.syncio) { + ret = -EBUSY; + break; + } + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; /* @@ -2248,6 +2153,8 @@ if (ioinfo[irq] == INVALID_STORAGE_AREA) return (-ENODEV); + if (ioinfo[irq]->ui.flags.noio) + return -EBUSY; /* * we only allow for clear_IO if the device has an I/O handler associated */ @@ -2260,8 +2167,7 @@ if (ioinfo[irq]->ui.flags.w4sense) return 0; - CIO_TRACE_EVENT (2, "clearIO"); - sprintf (dbf_txt, "%x", irq); + sprintf (dbf_txt, "clearIO%x", irq); CIO_TRACE_EVENT (2, dbf_txt); /* @@ -2418,8 +2324,7 @@ SANITY_CHECK (irq); - CIO_TRACE_EVENT (2, "cancelIO"); - sprintf (dbf_txt, "%x", irq); + sprintf (dbf_txt, "cancelIO%x", irq); CIO_TRACE_EVENT (2, dbf_txt); ccode = xsch (irq); @@ -2435,8 +2340,6 @@ case 1: /* status pending */ - /* process the pending irq... */ - s390_process_IRQ (irq); ret = -EBUSY; break; @@ -2595,7 +2498,7 @@ dp = &ioinfo[irq]->devstat; udp = ioinfo[irq]->irq_desc.dev_id; - + /* * It might be possible that a device was not-oper. at the time * of free_irq() processing. This means the handler is no longer @@ -2719,7 +2622,7 @@ } dp->cpa = dp->ii.irb.scsw.cpa; - + } irb_cc = dp->ii.irb.scsw.cc; @@ -2806,18 +2709,9 @@ irq, dp->devno); s390_displayhex (buffer, &(dp->ii.irb), sizeof (irb_t)); - if (cio_debug_initialized) { - - sprintf (buffer, - "s390_process_IRQ(%04X) - irb for " - "device %04X after channel check " - "or interface control check\n", - irq, dp->devno); - - s390_displayhex2 (buffer, - &(dp->ii.irb), - sizeof (irb_t), 0); - } + sprintf(dbf_txt, "chk%x", irq); + CIO_TRACE_EVENT(0, dbf_txt); + CIO_HEX_EVENT(0, &(dp->ii.irb), sizeof (irb_t)); } ioinfo[irq]->stctl |= stctl; @@ -2840,6 +2734,7 @@ */ if (!(stctl & SCSW_STCTL_ALERT_STATUS) && (ioinfo[irq]->ui.flags.busy == 0)) { + #ifdef CONFIG_DEBUG_IO if (irq != cons_dev) printk (KERN_INFO @@ -2863,16 +2758,9 @@ "subchannel status : %02X\n", dp->devno, irq, dp->dstat, dp->cstat); - if (cio_debug_initialized) { - sprintf (buffer, - "s390_process_IRQ(%04X) - irb for " - "device %04X, ending_status %d\n", irq, - dp->devno, ending_status); - - s390_displayhex2 (buffer, - &(dp->ii.irb), - sizeof (irb_t), 2); - } + sprintf(dbf_txt, "uint%x", irq); + CIO_TRACE_EVENT(2, dbf_txt); + CIO_HEX_EVENT(2, &(dp->ii.irb), sizeof (irb_t)); } /* @@ -2899,13 +2787,25 @@ unsigned long s_flag = 0; if (ending_status) { + /* there is a chance that the command + * that gave us the unit check actually + * was a basic sense, so we must not + * overwrite *udp in that case + */ + if (ioinfo[irq]->ui.flags.w4sense && + (dp->ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK)) { + CIO_MSG_EVENT(4,"double unit check irq %04x, dstat %02x," + "flags %8x\n", irq, dp->ii.irb.scsw.dstat, + ioinfo[irq]->ui.info, ending_status); + } else { /* * We copy the current status information into the device driver * status area. Then we can use the local devstat area for device * sensing. When finally calling the IRQ handler we must not overlay * the original device status but copy the sense data only. */ - memcpy (udp, dp, sizeof (devstat_t)); + memcpy (udp, dp, sizeof (devstat_t)); + } s_ccw->cmd_code = CCW_CMD_BASIC_SENSE; s_ccw->cda = @@ -2919,10 +2819,13 @@ * process we have to sense synchronously */ if (ioinfo[irq]->ui.flags.unready - || ioinfo[irq]->ui.flags.syncio) { - s_flag = DOIO_WAIT_FOR_INTERRUPT; + || ioinfo[irq]->ui.flags.syncio) + s_flag = DOIO_WAIT_FOR_INTERRUPT + | DOIO_TIMEOUT + | DOIO_VALID_LPM; - } + else + s_flag = DOIO_VALID_LPM; /* * Reset status info @@ -2950,8 +2853,34 @@ ioinfo[irq]->ui.flags.w4sense = 1; ret_io = s390_start_IO (irq, s_ccw, 0xE2C5D5E2, /* = SENSe */ - 0, /* n/a */ + 0xff, s_flag); + switch (ret_io) { + case 0: /* OK */ + break; + case -ENODEV: + /* + * The device is no longer operational. + * We won't get any sense data. + */ + ioinfo[irq]->ui.flags.w4sense = 0; + ioinfo[irq]->ui.flags.oper = 0; + allow4handler = 1; /* to notify the driver */ + break; + case -EBUSY: + /* + * The channel subsystem is either busy, or we have + * a status pending. Retry later. + */ + ioinfo[irq]->ui.flags.w4sense = 0; + ioinfo[irq]->ui.flags.delsense = 1; + break; + default: + printk(KERN_ERR"irq %04X: Unexpected rc %d " + "for BASIC SENSE!\n", irq, ret_io); + ioinfo[irq]->ui.flags.w4sense = 0; + allow4handler = 1; + } } else { /* * we received an Unit Check but we have no final @@ -3024,10 +2953,12 @@ udp->flag |= DEVSTAT_FLAG_SENSE_AVAIL; udp->scnt = sense_count; - if (sense_count >= 0) { + if (sense_count > 0) { memcpy (udp->ii.sense.data, ioinfo[irq]->sense_data, sense_count); + } else if (sense_count == 0) { + udp->flag &= ~DEVSTAT_FLAG_SENSE_AVAIL; } else { panic ("s390_process_IRQ(%04x) encountered " @@ -3090,7 +3021,8 @@ dp->flag |= DEVSTAT_FINAL_STATUS; udp->flag |= DEVSTAT_FINAL_STATUS; - ioinfo[irq]->irq_desc.handler (irq, udp, NULL); + if (!ioinfo[irq]->ui.flags.killio) + ioinfo[irq]->irq_desc.handler (irq, udp, NULL); /* * reset intparm after final status or we will badly present unsolicited @@ -3156,7 +3088,7 @@ udp->flag |= DEVSTAT_SUSPENDED; } - + ioinfo[irq]->irq_desc.handler (irq, udp, NULL); } @@ -3174,16 +3106,15 @@ dp->cstat = 0; dp->dstat = 0; - if (ioinfo[irq]->ulpm != ioinfo[irq]->opm) { - /* - * either it was the only path or it was restricted ... - */ - ioinfo[irq]->opm &= - ~(ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum); - } else { - ioinfo[irq]->opm = 0; - - } + if ((dp->ii.irb.scsw.fctl != 0) && + ((dp->ii.irb.scsw.stctl & SCSW_STCTL_STATUS_PEND) != 0) && + (((dp->ii.irb.scsw.stctl & SCSW_STCTL_INTER_STATUS) == 0) || + ((dp->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) != 0))) + if (dp->ii.irb.scsw.pno) { + stsch(irq, &ioinfo[irq]->schib); + ioinfo[irq]->opm &= + ~ioinfo[irq]->schib.pmcw.pnom; + } if (ioinfo[irq]->opm == 0) { ioinfo[irq]->ui.flags.oper = 0; @@ -3234,9 +3165,10 @@ ioinfo[irq]->devstat.intparm = 0; if (!ioinfo[irq]->ui.flags.s_pend - && !ioinfo[irq]->ui.flags.repnone) { - ioinfo[irq]->irq_desc.handler (irq, udp, NULL); + && !ioinfo[irq]->ui.flags.repnone + && !ioinfo[irq]->ui.flags.killio) { + ioinfo[irq]->irq_desc.handler (irq, udp, NULL); } ending_status = 1; @@ -3245,6 +3177,18 @@ } + if (ending_status && + ioinfo[irq]->ui.flags.noio && + !ioinfo[irq]->ui.flags.syncio && + !ioinfo[irq]->ui.flags.w4sense) { + if(ioinfo[irq]->ui.flags.ready) { + s390_schedule_path_verification(irq); + } else { + ioinfo[irq]->ui.flags.killio = 0; + ioinfo[irq]->ui.flags.noio = 0; + } + } + return (ending_status); } @@ -3277,7 +3221,7 @@ if (cons_dev != -1) return -EBUSY; - sprintf (dbf_txt, "scd%x", irq); + sprintf (dbf_txt, "scons%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -3322,7 +3266,7 @@ if (cons_dev != -1) return -EBUSY; - sprintf (dbf_txt, "rscd%x", irq); + sprintf (dbf_txt, "rcons%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -3366,7 +3310,7 @@ if (irq != cons_dev) return -EINVAL; - sprintf (dbf_txt, "wcd%x", irq); + sprintf (dbf_txt, "wcons%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -3417,7 +3361,7 @@ int rc = 0; char dbf_txt[15]; - sprintf (dbf_txt, "eisc%x", irq); + sprintf (dbf_txt, "enisc%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* This one spins until it can get the sync_isc lock for irq# irq */ @@ -3438,7 +3382,6 @@ * we only run the STSCH/MSCH path for the first enablement */ else if (sync_isc_cnt == 1) { - ioinfo[irq]->ui.flags.syncio = 1; ccode = stsch (irq, &(ioinfo[irq]->schib)); @@ -3463,6 +3406,7 @@ * if neccessary */ if (cons_dev != -1) cr6 &= 0xFEFFFFFF; + ioinfo[irq]->ui.flags.syncio = 1; __ctl_load (cr6, 6, 6); rc = 0; retry = 0; @@ -3505,6 +3449,15 @@ if (rc) { /* can only happen if stsch/msch fails */ sync_isc_cnt = 0; atomic_set (&sync_isc, -1); + } else if (sync_isc_cnt == 1) { + int ccode; + + ccode = stsch(irq, &ioinfo[irq]->schib); + if (!ccode && ioinfo[irq]->schib.pmcw.isc != 5) { + ioinfo[irq]->ui.flags.syncio = 0; + sync_isc_cnt = 0; + atomic_set (&sync_isc, -1); + } } } else { #ifdef CONFIG_SYNC_ISC_PARANOIA @@ -3531,7 +3484,7 @@ char dbf_txt[15]; - sprintf (dbf_txt, "disc%x", irq); + sprintf (dbf_txt, "disisc%x", irq); CIO_TRACE_EVENT (4, dbf_txt); if ((irq <= highest_subchannel) && @@ -3621,7 +3574,7 @@ sync_isc_cnt = 0; atomic_set (&sync_isc, -1); - + } else { sync_isc_cnt--; @@ -3641,6 +3594,26 @@ return (rc); } +int diag210 (diag210_t *addr) +{ + int ccode; + + __asm__ __volatile__( +#ifdef CONFIG_ARCH_S390X + " sam31\n" + " diag %1,0,0x210\n" + " sam64\n" +#else + " diag %1,0,0x210\n" +#endif + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) + : "a" (addr) + : "cc" ); + return ccode; +} + /* * Input : * devno - device number @@ -3658,11 +3631,13 @@ CIO_TRACE_EVENT (4, "VMvdinf"); if (init_IRQ_complete) { - p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA); + p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA | GFP_ATOMIC); } else { p_diag_data = alloc_bootmem_low (sizeof (diag210_t)); } + if (!p_diag_data) + return; p_diag_data->vrdcdvno = devno; p_diag_data->vrdclen = sizeof (diag210_t); @@ -3980,7 +3955,7 @@ int read_dev_chars (int irq, void **buffer, int length) { - unsigned int flags; + unsigned long flags; ccw1_t *rdc_ccw; devstat_t devstat; char *rdc_buf; @@ -4009,7 +3984,7 @@ return -EUSERS; } - sprintf (dbf_txt, "rdc%x", irq); + sprintf (dbf_txt, "rddevch%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -4052,8 +4027,7 @@ rdc_ccw->count = length; rdc_ccw->flags = CCW_FLAG_SLI; ret = - set_normalized_cda (rdc_ccw, (unsigned long) - rdc_buf); + set_normalized_cda (rdc_ccw, rdc_buf); if (!ret) { memset (ioinfo[irq]->irq_desc.dev_id, @@ -4139,7 +4113,7 @@ return -EUSERS; } - sprintf (dbf_txt, "rcd%x", irq); + sprintf (dbf_txt, "rdconf%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -4194,7 +4168,7 @@ if (init_IRQ_complete) { rcd_buf = kmalloc (ioinfo[irq]->senseid.ciw[ciw_cnt]. - count, GFP_DMA); + count, GFP_DMA | GFP_ATOMIC); } else { rcd_buf = alloc_bootmem_low (ioinfo[irq]->senseid. @@ -4553,7 +4527,7 @@ int ret; char dbf_txt[15]; - sprintf (dbf_txt, "dri%x", irq); + sprintf (dbf_txt, "devrec%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -4576,6 +4550,7 @@ if (!ret) { pgid_t pgid; + int i, mask; /* * First thing we should do is a sensePGID in * order to find out how we can proceed with @@ -4586,13 +4561,24 @@ */ memcpy(&pgid, global_pgid, sizeof(pgid_t)); - ret = s390_SensePGID(irq, 0xff, &pgid); - if (ret == -EOPNOTSUPP) - /* - * Doesn't prevent us from proceeding - */ - ret = 0; + ret = -EAGAIN; + for (i=0; i<8 && ret==-EAGAIN; i++) { + + mask = (0x80 >> i) & ioinfo[irq]->opm; + + if (!mask) + continue; + + ret = s390_SensePGID(irq, mask, &pgid); + + if (ret == -EOPNOTSUPP) + /* + * Doesn't prevent us from proceeding + */ + ret = 0; + + } if (!ret && !ioinfo[irq]->ui.flags.unfriendly) { @@ -4645,18 +4631,9 @@ s390_displayhex (buffer, prcd, lrcd); #endif - if (cio_debug_initialized) { - sprintf (buffer, - "RCD for device(%04X)/" - "subchannel(%04X) returns :\n", - ioinfo[irq]-> - schib.pmcw.dev, - irq); - - s390_displayhex2 - (buffer, prcd, lrcd, - 2); - } + CIO_TRACE_EVENT(2, "rcddata:"); + CIO_HEX_EVENT(2, prcd, lrcd); + if (init_IRQ_complete) { kfree (prcd); } else { @@ -4756,14 +4733,9 @@ int s390_trigger_resense(int irq) { - char dbf_txt[8]; SANITY_CHECK(irq); - CIO_TRACE_EVENT (2, "tsns"); - sprintf(dbf_txt, "%x", irq); - CIO_TRACE_EVENT (2, dbf_txt); - if (ioinfo[irq]->ui.flags.ready) { printk (KERN_WARNING "s390_trigger_resense(%04X): " "Device is in use!\n", irq); @@ -4826,7 +4798,7 @@ char dbf_txt[15]; - sprintf (dbf_txt, "vals%x", irq); + sprintf (dbf_txt, "valsch%x", irq); CIO_TRACE_EVENT (4, dbf_txt); /* @@ -4901,9 +4873,12 @@ alloc_bootmem_low (sizeof (ioinfo_t)); } else { ioinfo[irq] = (ioinfo_t *) - kmalloc (sizeof (ioinfo_t), GFP_DMA); + kmalloc (sizeof (ioinfo_t), GFP_DMA | GFP_ATOMIC); } + if (!ioinfo[irq]) + return -ENOMEM; + memset (ioinfo[irq], '\0', sizeof (ioinfo_t)); memcpy (&ioinfo[irq]->schib, p_init_schib, sizeof (schib_t)); @@ -4964,6 +4939,10 @@ /* disable using this path */ ioinfo[irq]->opm &= ~mask; } + } else { + /* This chpid is not available to us */ + clear_bit(ioinfo[irq]->schib.pmcw.chpid[chp], + &chpids); } } } @@ -5209,8 +5188,7 @@ return -EUSERS; } - CIO_TRACE_EVENT (4, "senseID"); - sprintf (dbf_txt, "%x", irq); + sprintf (dbf_txt, "snsID%x", irq); CIO_TRACE_EVENT (4, dbf_txt); inlreq = 0; /* to make the compiler quiet... */ @@ -5245,11 +5223,17 @@ s390irq_spin_lock (irq); if (init_IRQ_complete) { - sense_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA); + sense_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC); } else { sense_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t)); } + if (!sense_ccw) { + s390irq_spin_unlock (irq); + if (inlreq) + free_irq (irq, &devstat); + return -ENOMEM; + } /* more than one path installed ? */ if (ioinfo[irq]->schib.pmcw.pim != 0x80) { @@ -5642,115 +5626,273 @@ return (cc); } -/* - * Device Path Verification - * - * Path verification is accomplished by checking which paths (CHPIDs) are - * available. Further, a path group ID is set, if possible in multipath - * mode, otherwise in single path mode. - * - * Note : This function must not be called during normal device recognition, - * but during device driver initiated request_irq() processing only. - */ -int -s390_DevicePathVerification (int irq, __u8 usermask) +static int +s390_do_path_verification(int irq, __u8 usermask) { - int ccode; - __u8 pathmask; __u8 domask; -#ifdef CONFIG_CHSC - int chp; - int mask; - int old_opm = 0; -#endif /* CONFIG_CHSC */ - - int ret = 0; int i; pgid_t pgid; __u8 dev_path; int first = 1; - + int ret = 0; char dbf_txt[15]; - sprintf (dbf_txt, "dpvf%x", irq); - CIO_TRACE_EVENT (4, dbf_txt); - - if (ioinfo[irq]->st) - return -ENODEV; + sprintf(dbf_txt, "dopv%x", irq); + CIO_TRACE_EVENT(2, dbf_txt); -#ifdef CONFIG_CHSC - old_opm = ioinfo[irq]->opm; -#endif /* CONFIG_CHSC */ - ccode = stsch (irq, &(ioinfo[irq]->schib)); + dev_path = usermask ? usermask : ioinfo[irq]->opm; - if (ccode) { - return -ENODEV; + if (ioinfo[irq]->ui.flags.pgid == 0) { + memcpy (&ioinfo[irq]->pgid, global_pgid, sizeof (pgid_t)); + ioinfo[irq]->ui.flags.pgid = 1; } - if (ioinfo[irq]->schib.pmcw.pim == 0x80) { - /* - * no error, just not required for single path only devices - */ - ioinfo[irq]->ui.flags.pgid_supp = 0; - ret = 0; -#ifdef CONFIG_CHSC + for (i = 0; i < 8 && !ret; i++) { + + domask = dev_path & (0x80>>i); + + if (!domask) + continue; + + if (!test_bit(ioinfo[irq]->schib.pmcw.chpid[i], + &chpids_logical)) + /* Chpid is logically offline, don't do io */ + continue; + + ret = s390_SetPGID (irq, domask); + /* - * disable if chpid is logically offline + * For the *first* path we are prepared for recovery + * + * - If we fail setting the PGID we assume its + * using a different PGID already (VM) we + * try to sense. */ - if (!test_bit(ioinfo[irq]->schib.pmcw.chpid[0], - &chpids_logical)) { - not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc; - int was_oper = ioinfo[irq]->ui.flags.oper; + if (ret == -EOPNOTSUPP && first) { + *(int *) &pgid = 0; + + ret = s390_SensePGID (irq, domask, &pgid); + first = 0; - ioinfo[irq]->opm = 0; - ioinfo[irq]->ui.flags.oper = 0; - printk(KERN_WARNING - "No logical path for sch %d...\n", - irq); - if (old_opm && - was_oper && - ioinfo[irq]->ui.flags.ready) { -#ifdef CONFIG_PROC_FS - if (cio_proc_devinfo) - cio_procfs_device_remove - (ioinfo[irq]->devno); -#endif /* CONFIG_PROC_FS */ - free_irq( irq, ioinfo[irq]->irq_desc.dev_id); - if (nopfunc) - nopfunc( irq, DEVSTAT_DEVICE_GONE); + if (ret == 0) { + /* + * Check whether we retrieved + * a reasonable PGID ... + */ + if (pgid.inf.ps.state1 == SNID_STATE1_GROUPED) + memcpy (&ioinfo[irq]->pgid, + &pgid, sizeof (pgid_t)); + else /* ungrouped or garbage ... */ + ret = -EOPNOTSUPP; + + } else { + ioinfo[irq]->ui.flags.pgid_supp = 0; + +#ifdef CONFIG_DEBUG_IO + printk (KERN_WARNING + "PathVerification(%04X) - Device %04X " + "doesn't support path grouping\n", + irq, ioinfo[irq]->schib.pmcw.dev); +#endif + CIO_MSG_EVENT(2, "PathVerification(%04X) " + "- Device %04X doesn't " + " support path grouping\n", + irq, + ioinfo[irq]->schib.pmcw.dev); + } - ret = -ENODEV; - } else if (!old_opm) { + } else if (ret == -EIO) { +#ifdef CONFIG_DEBUG_IO + printk (KERN_ERR "PathVerification(%04X) - I/O error " + "on device %04X\n", irq, + ioinfo[irq]->schib.pmcw.dev); +#endif - /* - * check for opm... + CIO_MSG_EVENT(2, "PathVerification(%04X) - I/O error " + "on device %04X\n", irq, + ioinfo[irq]->schib.pmcw.dev); + + ioinfo[irq]->ui.flags.pgid_supp = 0; + + } else if (ret == -ETIMEDOUT) { +#ifdef CONFIG_DEBUG_IO + printk (KERN_ERR "PathVerification(%04X) - I/O timed " + "out on device %04X\n", irq, + ioinfo[irq]->schib.pmcw.dev); +#endif + CIO_MSG_EVENT(2, "PathVerification(%04X) - I/O timed " + "out on device %04X\n", irq, + ioinfo[irq]->schib.pmcw.dev); + + ioinfo[irq]->ui.flags.pgid_supp = 0; + + } else if (ret == -EAGAIN) { + + ret = 0; + } else if (ret == -EUSERS) { + +#ifdef CONFIG_DEBUG_IO + printk (KERN_ERR "PathVerification(%04X) " + "- Device is locked by someone else!\n", + irq); +#endif + CIO_MSG_EVENT(2, "PathVerification(%04X) " + "- Device is locked by someone else!\n", + irq); + } else if (ret == -ENODEV) { +#ifdef CONFIG_DEBUG_IO + printk (KERN_ERR "PathVerification(%04X) " + "- Device %04X is no longer there?!?\n", + irq, ioinfo[irq]->schib.pmcw.dev); +#endif + CIO_MSG_EVENT(2, "PathVerification(%04X) " + "- Device %04X is no longer there?!?\n", + irq, ioinfo[irq]->schib.pmcw.dev); + + } else if (ret == -EBUSY) { + /* + * The device is busy. Schedule the path verification + * bottom half and we'll hopefully get in next time. */ + if (!ioinfo[irq]->ui.flags.noio) { + s390_schedule_path_verification(irq); + } + return -EINPROGRESS; + } else if (ret) { +#ifdef CONFIG_DEBUG_IO + printk (KERN_ERR "PathVerification(%04X) " + "- Unexpected error %d on device %04X\n", + irq, ret, ioinfo[irq]->schib.pmcw.dev); +#endif + CIO_MSG_EVENT(2, "PathVerification(%04X) - " + "Unexpected error %d on device %04X\n", + irq, ret, ioinfo[irq]->schib.pmcw.dev); + + ioinfo[irq]->ui.flags.pgid_supp = 0; + } + } + if (stsch(irq, &ioinfo[irq]->schib) != 0) + /* FIXME: tell driver device is dead. */ + return -ENODEV; + + /* + * stsch() doesn't always yield the correct pim, pam, and pom + * values, if no device selection has been performed yet. + * However, after complete path verification they are up to date. + */ + ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim & + ioinfo[irq]->schib.pmcw.pam & + ioinfo[irq]->schib.pmcw.pom; + +#ifdef CONFIG_CHSC + if (ioinfo[irq]->opm) { + for (i=0;i<=7;i++) { + int mask = 0x80 >> i; + if ((ioinfo[irq]->opm & mask) && + (!test_bit(ioinfo[irq]->schib.pmcw.chpid[i], + &chpids_logical))) + /* disable using this path */ + ioinfo[irq]->opm &= ~mask; + } + } +#endif /* CONFIG_CHSC */ + + ioinfo[irq]->ui.flags.noio = 0; + + /* Eventually wake up the device driver. */ + if (ioinfo[irq]->opm != 0) { + devreg_t *pdevreg; + pdevreg = s390_search_devreg(ioinfo[irq]); + + if (pdevreg && pdevreg->oper_func) + pdevreg->oper_func(irq, pdevreg); + } + return ret; + +} + +/* + * Device Path Verification + * + * Path verification is accomplished by checking which paths (CHPIDs) are + * available. Further, a path group ID is set, if possible in multipath + * mode, otherwise in single path mode. + * + * Note : This function must not be called during normal device recognition, + * but during device driver initiated request_irq() processing only. + */ +int +s390_DevicePathVerification (int irq, __u8 usermask) +{ + int ccode; +#ifdef CONFIG_CHSC + int chp; + int mask; + int old_opm = 0; +#endif /* CONFIG_CHSC */ + + int ret = 0; + + char dbf_txt[15]; + devreg_t *pdevreg; + + sprintf (dbf_txt, "dpver%x", irq); + CIO_TRACE_EVENT (4, dbf_txt); + + if (ioinfo[irq]->st) + return -ENODEV; + +#ifdef CONFIG_CHSC + old_opm = ioinfo[irq]->opm; +#endif /* CONFIG_CHSC */ + ccode = stsch (irq, &(ioinfo[irq]->schib)); + + if (ccode) + return -ENODEV; + + if (ioinfo[irq]->schib.pmcw.pim == 0x80) { + /* + * no error, just not required for single path only devices + */ + ioinfo[irq]->ui.flags.pgid_supp = 0; + ret = 0; + ioinfo[irq]->ui.flags.noio = 0; + +#ifdef CONFIG_CHSC + /* + * disable if chpid is logically offline + */ + if (!test_bit(ioinfo[irq]->schib.pmcw.chpid[0], + &chpids_logical)) { + + ioinfo[irq]->opm = 0; + ioinfo[irq]->ui.flags.oper = 0; + printk(KERN_WARNING + "No logical path for sch %d...\n", + irq); + + if (ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + return -ENODEV; + } + if (!old_opm) { + ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim & ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom; - + if (ioinfo[irq]->opm) { - devreg_t *pdevreg; ioinfo[irq]->ui.flags.oper = 1; - pdevreg = s390_search_devreg( ioinfo[irq] ); - - if (pdevreg) - if (pdevreg->oper_func) - pdevreg->oper_func - ( irq, pdevreg); -#ifdef CONFIG_PROC_FS - if (cio_proc_devinfo) - if (highest_subchannel - < MAX_CIO_PROCFS_ENTRIES) { - cio_procfs_device_create - (ioinfo[irq]->devno); - } -#endif /* CONFIG_PROC_FS */ + pdevreg = s390_search_devreg(ioinfo[irq]); + + if (pdevreg && pdevreg->oper_func) + pdevreg->oper_func(irq, pdevreg); + ret = 0; + } else { + ret = -ENODEV; } - ret = 0; - } else { - ret = 0; } #endif /* CONFIG_CHSC */ return ret; @@ -5763,199 +5905,72 @@ if (ioinfo[irq]->opm) { for (chp=0;chp<=7;chp++) { mask = 0x80 >> chp; - if (ioinfo[irq]->opm & mask) { - if (!test_bit - (ioinfo[irq]->schib.pmcw.chpid[chp], - &chpids_logical)) { - /* disable using this path */ - ioinfo[irq]->opm &= ~mask; - } - } + if ((ioinfo[irq]->opm & mask) + &&(!test_bit(ioinfo[irq]->schib.pmcw.chpid[chp], + &chpids_logical))) + /* disable using this path */ + ioinfo[irq]->opm &= ~mask; } } - if ((ioinfo[irq]->opm == 0) && (old_opm)) { - not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc; - int was_oper = ioinfo[irq]->ui.flags.ready; - - ioinfo[irq]->ui.flags.oper = 0; - printk(KERN_WARNING "No logical path for sch %d...\n",irq); - if (was_oper && ioinfo[irq]->ui.flags.oper) { -#ifdef CONFIG_PROC_FS - if (cio_proc_devinfo) - cio_procfs_device_remove(ioinfo[irq]->devno); -#endif /* CONFIG_PROC_FS */ - free_irq( irq, ioinfo[irq]->irq_desc.dev_id); - if (nopfunc) - nopfunc( irq, DEVSTAT_DEVICE_GONE); - } - return -ENODEV; - } - - if (!old_opm) { - /* Hey, we have a new logical path... */ - devreg_t *pdevreg; - - ioinfo[irq]->ui.flags.oper = 1; - pdevreg = s390_search_devreg( ioinfo[irq] ); - - if (pdevreg) - if (pdevreg->oper_func) - pdevreg->oper_func( irq, pdevreg); -#ifdef CONFIG_PROC_FS - if (cio_proc_devinfo) - if (highest_subchannel < MAX_CIO_PROCFS_ENTRIES) { - cio_procfs_device_create(ioinfo[irq]->devno); - } -#endif /* CONFIG_PROC_FS */ - } #endif /* CONFIG_CHSC */ - if ( ioinfo[irq]->ui.flags.pgid_supp == 0 ) - return( 0); /* just exit ... */ + if (ioinfo[irq]->ui.flags.pgid_supp == 0) { - if (usermask) { - dev_path = usermask; - } else { - dev_path = ioinfo[irq]->opm; + if (ioinfo[irq]->opm == 0) + return -ENODEV; + + ioinfo[irq]->ui.flags.oper = 1; + ioinfo[irq]->ui.flags.noio = 0; - } + pdevreg = s390_search_devreg(ioinfo[irq]); + + if (pdevreg && pdevreg->oper_func) + pdevreg->oper_func(irq, pdevreg); - if (ioinfo[irq]->ui.flags.pgid == 0) { - memcpy (&ioinfo[irq]->pgid, global_pgid, sizeof (pgid_t)); - ioinfo[irq]->ui.flags.pgid = 1; + return 0; } - for (i = 0; i < 8 && !ret; i++) { - pathmask = 0x80 >> i; - - domask = dev_path & pathmask; - - if (domask) { - ret = s390_SetPGID (irq, domask); - - /* - * For the *first* path we are prepared - * for recovery - * - * - If we fail setting the PGID we assume its - * using a different PGID already (VM) we - * try to sense. - */ - if (ret == -EOPNOTSUPP && first) { - *(int *) &pgid = 0; - - ret = s390_SensePGID (irq, domask, &pgid); - first = 0; - - if (ret == 0) { - /* - * Check whether we retrieved - * a reasonable PGID ... - */ - if (pgid.inf.ps.state1 == - SNID_STATE1_GROUPED) { - memcpy (&ioinfo[irq]->pgid, - &pgid, sizeof (pgid_t)); - } else { /* ungrouped or garbage ... */ - ret = -EOPNOTSUPP; - - } - } else { - ioinfo[irq]->ui.flags.pgid_supp = 0; - -#ifdef CONFIG_DEBUG_IO - printk (KERN_WARNING - "PathVerification(%04X) " - "- Device %04X doesn't " - " support path grouping\n", - irq, - ioinfo[irq]->schib.pmcw.dev); -#endif - CIO_MSG_EVENT(2, - "PathVerification(%04X) " - "- Device %04X doesn't " - " support path grouping\n", - irq, - ioinfo[irq]->schib. - pmcw.dev); - - } - } else if (ret == -EIO) { -#ifdef CONFIG_DEBUG_IO - printk (KERN_ERR - "PathVerification(%04X) - I/O error " - "on device %04X\n", irq, - ioinfo[irq]->schib.pmcw.dev); -#endif + return s390_do_path_verification (irq, usermask); - CIO_MSG_EVENT(2, - "PathVerification(%04X) - I/O error " - "on device %04X\n", irq, - ioinfo[irq]->schib.pmcw.dev); - - ioinfo[irq]->ui.flags.pgid_supp = 0; +} - } else if (ret == -ETIMEDOUT) { -#ifdef CONFIG_DEBUG_IO - printk (KERN_ERR - "PathVerification(%04X) - I/O timed " - "out on device %04X\n", - irq, - ioinfo[irq]->schib.pmcw.dev); -#endif - CIO_MSG_EVENT(2, - "PathVerification(%04X) - I/O timed " - "out on device %04X\n", irq, - ioinfo[irq]->schib.pmcw.dev); - - ioinfo[irq]->ui.flags.pgid_supp = 0; +void +s390_kick_path_verification (unsigned long irq) +{ + long cr6 __attribute__ ((aligned (8))); - } else if (ret == -EAGAIN) { + atomic_set (&ioinfo[irq]->pver_pending, 0); + /* Do not enter path verification if sync_isc is enabled. */ + __ctl_store (cr6, 6, 6); + if (cr6 & 0x04000000) { + s390_schedule_path_verification (irq); + return; + } + ioinfo[irq]->ui.flags.killio = 0; + s390_DevicePathVerification(irq, 0xff); - ret = 0; +} - } else if (ret == -EUSERS) { - -#ifdef CONFIG_DEBUG_IO - printk (KERN_ERR - "PathVerification(%04X) " - "- Device is locked by someone else!\n", - irq); -#endif - CIO_MSG_EVENT(2, - "PathVerification(%04X) " - "- Device is locked by someone else!\n", - irq); - } else if (ret == -ENODEV) { -#ifdef CONFIG_DEBUG_IO - printk (KERN_ERR - "PathVerification(%04X) " - "- Device %04X is no longer there?!?\n", - irq, ioinfo[irq]->schib.pmcw.dev); -#endif - CIO_MSG_EVENT(2, - "PathVerification(%04X) " - "- Device %04X is no longer there?!?\n", - irq, ioinfo[irq]->schib.pmcw.dev); - } else if (ret) { -#ifdef CONFIG_DEBUG_IO - printk (KERN_ERR - "PathVerification(%04X) " - "- Unexpected error %d on device %04X\n", - irq, ret, ioinfo[irq]->schib.pmcw.dev); -#endif - CIO_MSG_EVENT(2, - "PathVerification(%04X) - " - "Unexpected error %d on device %04X\n", - irq, ret, ioinfo[irq]->schib.pmcw.dev); - - ioinfo[irq]->ui.flags.pgid_supp = 0; - } - } +static void +s390_schedule_path_verification(unsigned long irq) +{ + /* Protect against rescheduling, when already running */ + if (atomic_compare_and_swap (0, 1, &ioinfo[irq]->pver_pending)) { + return; } - return ret; + /* + * Call path verification. + * Note this is always called from inside the i/o layer, so we don't + * need to care about the usermask. + */ + INIT_LIST_HEAD (&ioinfo[irq]->pver_bh.list); + ioinfo[irq]->pver_bh.sync = 0; + ioinfo[irq]->pver_bh.routine = (void*) (void*) s390_kick_path_verification; + ioinfo[irq]->pver_bh.data = (void*) irq; + queue_task (&ioinfo[irq]->pver_bh, &tq_immediate); + mark_bh (IMMEDIATE_BH); } /* @@ -6018,10 +6033,15 @@ s390irq_spin_lock_irqsave (irq, flags); if (init_IRQ_complete) { - spid_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA); + spid_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC); } else { spid_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t)); - + } + if (!spid_ccw) { + s390irq_spin_unlock_irqrestore(irq, flags); + if (inlreq) + free_irq(irq, pdevstat); + return -ENOMEM; } spid_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN; @@ -6056,17 +6076,19 @@ printk (KERN_DEBUG "SPID - Device %04X " "on Subchannel %04X " "reports pending status, " + "lpm = %x, " "retry : %d\n", ioinfo[irq]->schib.pmcw.dev, - irq, retry); + irq, lpm, retry); #endif CIO_MSG_EVENT(2, "SPID - Device %04X " "on Subchannel %04X " "reports pending status, " + "lpm = %x, " "retry : %d\n", ioinfo[irq]->schib.pmcw. - dev, irq, retry); + dev, irq, lpm, retry); retry--; irq_ret = -EIO; } @@ -6117,11 +6139,12 @@ "SPID - device %04X," " unit check," " retry %d, cnt %02d," - " sns :" + " lpm %x, sns :" " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", ioinfo[irq]->schib.pmcw. dev, retry, pdevstat->scnt, + lpm, pdevstat->ii.sense. data[0], pdevstat->ii.sense. @@ -6143,11 +6166,12 @@ "SPID - device %04X," " unit check," " retry %d, cnt %02d," - " sns :" + " lpm %x, sns :" " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", ioinfo[irq]->schib. pmcw.dev, retry, pdevstat->scnt, + lpm, pdevstat->ii.sense. data[0], pdevstat->ii.sense. @@ -6174,7 +6198,7 @@ /* don't issue warnings during startup unless requested */ if (init_IRQ_complete || cio_notoper_msg) { - printk (KERN_WARNING + printk (KERN_INFO "SPID - Device %04X " "on Subchannel %04X, " "lpm %02X, " @@ -6214,12 +6238,52 @@ irq); retry--; + } else if (irq_ret == -EBUSY) { +#ifdef CONFIG_DEBUG_IO + printk(KERN_WARNING + "SPID - device %x, irq %x is busy!\n", + ioinfo[irq]->schib.pmcw.dev, irq); +#endif /* CONFIG_DEBUG_IO */ + CIO_MSG_EVENT(2, + "SPID - device %x, irq %x is busy!\n", + ioinfo[irq]->schib.pmcw.dev, irq); + retry = 0; + } else if (irq_ret != -ENODEV) { retry--; irq_ret = -EIO; - } else { + } else if (!pdevstat->flag & DEVSTAT_NOT_OPER) { retry = 0; irq_ret = -ENODEV; + } else { + /* don't issue warnings during startup unless requested */ + if (init_IRQ_complete || cio_notoper_msg) { + + printk (KERN_INFO + "SPID - Device %04X " + "on Subchannel %04X, " + "lpm %02X, " + "became 'not operational'\n", + ioinfo[irq]->schib.pmcw. + dev, irq, + lpm); + CIO_MSG_EVENT(2, + "SPID - Device %04X " + "on Subchannel %04X, " + "lpm %02X, " + "became 'not operational'\n", + ioinfo[irq]->schib. + pmcw.dev, irq, + lpm); + } + + retry = 0; + ioinfo[irq]->opm &= ~lpm; + + if (ioinfo[irq]->opm != 0) + irq_ret = -EAGAIN; + else + irq_ret = -ENODEV; } @@ -6302,13 +6366,32 @@ ioinfo[irq]->ui.flags.unfriendly = 0; /* assume it's friendly... */ if (init_IRQ_complete) { - snid_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA); - tmp_pgid = kmalloc (sizeof (pgid_t), GFP_DMA); + snid_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA | GFP_ATOMIC); + tmp_pgid = kmalloc (sizeof (pgid_t), GFP_DMA | GFP_ATOMIC); } else { snid_ccw = alloc_bootmem_low (sizeof (ccw1_t)); tmp_pgid = alloc_bootmem_low (sizeof (pgid_t)); } + if (!snid_ccw || !tmp_pgid) { + if (snid_ccw) { + if (init_IRQ_complete) + kfree(snid_ccw); + else + free_bootmem((unsigned long) snid_ccw, sizeof(ccw1_t)); + } + if (tmp_pgid) { + if (init_IRQ_complete) + kfree(tmp_pgid); + else + free_bootmem((unsigned long) tmp_pgid, sizeof(pgid_t)); + } + s390irq_spin_unlock_irqrestore(irq, flags); + if (inlreq) + free_irq (irq, pdevstat); + return -ENOMEM; + } + snid_ccw->cmd_code = CCW_CMD_SENSE_PGID; snid_ccw->cda = (__u32) virt_to_phys (tmp_pgid); snid_ccw->count = sizeof (pgid_t); @@ -6335,8 +6418,8 @@ * Sense Path Group ID command * further retries wouldn't help ... */ - if (pdevstat->ii.sense.data[0] & - (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)) { + if (pdevstat->ii.sense.data[0] + & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)) { retry = 0; irq_ret = -EOPNOTSUPP; } else { @@ -6403,7 +6486,7 @@ } else if (pdevstat->flag & DEVSTAT_NOT_OPER) { /* don't issue warnings during startup unless requested */ if (init_IRQ_complete || cio_notoper_msg) { - printk (KERN_WARNING + printk (KERN_INFO "SNID - Device %04X " "on Subchannel %04X, " "lpm %02X, " @@ -6422,7 +6505,8 @@ } retry = 0; - irq_ret = -EIO; + ioinfo[irq]->opm &= ~lpm; + irq_ret = -EAGAIN; } else { retry = 0; /* success ... */ @@ -6432,7 +6516,7 @@ * -- we'll fail other commands if that is * the case */ - if (pgid->inf.ps.state2 == + if (tmp_pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { printk (KERN_WARNING "SNID - Device %04X " @@ -6507,10 +6591,38 @@ ioinfo[irq]->schib.pmcw.dev, irq_ret); retry--; irq_ret = -EIO; - } else { /* -ENODEV ... */ - + } else if (!pdevstat->flag & DEVSTAT_NOT_OPER) { retry = 0; irq_ret = -ENODEV; + } else { + /* don't issue warnings during startup unless requested */ + if (init_IRQ_complete || cio_notoper_msg) { + + printk (KERN_INFO + "SNID - Device %04X " + "on Subchannel %04X, " + "lpm %02X, " + "became 'not operational'\n", + ioinfo[irq]->schib.pmcw. + dev, irq, + lpm); + CIO_MSG_EVENT(2, + "SNID - Device %04X " + "on Subchannel %04X, " + "lpm %02X, " + "became 'not operational'\n", + ioinfo[irq]->schib. + pmcw.dev, irq, + lpm); + } + + retry = 0; + ioinfo[irq]->opm &= ~lpm; + + if (ioinfo[irq]->opm != 0) + irq_ret = -EAGAIN; + else + irq_ret = -ENODEV; } @@ -6650,12 +6762,9 @@ pdevreg = s390_search_devreg (ioinfo[irq]); - if (pdevreg != NULL) { - if (pdevreg->oper_func != NULL) - pdevreg-> - oper_func (irq, pdevreg); + if (pdevreg && pdevreg->oper_func) + pdevreg->oper_func(irq, pdevreg); - } #ifdef CONFIG_PROC_FS /* add new procfs entry */ if (cio_proc_devinfo) @@ -6995,19 +7104,120 @@ __initcall(chsc_get_sch_descriptions); +static int +__check_for_io_and_kill(int irq, __u8 mask, int fatal) +{ + schib_t *schib = &ioinfo[irq]->schib; + int ret = 0; + + if (schib->scsw.actl & SCSW_ACTL_DEVACT) { + if ((ioinfo[irq]->opm != mask) || + (fatal == 0)) { + ret = CIO_PATHGONE_WAIT4INT; + } + if ((schib->scsw.actl & SCSW_ACTL_SCHACT) && + (schib->pmcw.lpum == mask) && + (fatal != 0)) { + int cc; + /* Kill the IO. It won't complete. */ + ioinfo[irq]->ui.flags.noio = 0; + ioinfo[irq]->ui.flags.killio = 1; + cc = clear_IO(irq, 0xD2C9D3D3, 0); + if (cc != 0) { + /* Eek, can't kill io. */ + CIO_CRW_EVENT(0, + "Can't kill io on " + "sch %x, clear_IO " + "returned %d!\n", + irq, cc); + ioinfo[irq]->ui.flags.killio = 0; + s390irq_spin_unlock(irq); + if ((cc == -ENODEV) && + (ioinfo[irq]->nopfunc)) { + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->nopfunc(irq, + DEVSTAT_DEVICE_GONE); + } + ret = CIO_PATHGONE_DEVGONE; + } else { + ret |= CIO_PATHGONE_WAIT4INT; + } + ioinfo[irq]->ui.flags.noio = 1; + ret |= CIO_PATHGONE_IOERR; + } + + } else if (schib->scsw.actl & (SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_RESUME_PEND)) { + if ((schib->pmcw.lpum != mask) || + (fatal == 0)) { + ret = CIO_PATHGONE_WAIT4INT; + } else { + int cc; + /* Cancel the i/o. */ + cc = cancel_IO(irq); + switch (cc) { + case 0: + /* i/o cancelled; we can do path verif. */ + ret = CIO_PATHGONE_IOERR; + break; + case -EBUSY: + /* Status pending, we'll get an interrupt */ + ret = CIO_PATHGONE_WAIT4INT; + break; + case -EINVAL: + /* + * There is either not only the start function + * specified or we are subchannel active. + * Do a clear sch. + */ + ioinfo[irq]->ui.flags.noio = 0; + ioinfo[irq]->ui.flags.killio = 1; + cc = clear_IO(irq, 0xD2C9D3D3, 0); + if (cc != 0) { + /* Eek, can't kill io. */ + CIO_CRW_EVENT(0, + "Can't kill io on " + "sch %x, clear_IO " + "returned %d!\n", + irq, cc); + ioinfo[irq]->ui.flags.killio = 0; + s390irq_spin_unlock(irq); + if ((cc == -ENODEV) && + (ioinfo[irq]->nopfunc)) { + ioinfo[irq]->nopfunc(irq, + DEVSTAT_DEVICE_GONE); + ioinfo[irq]->ui.flags.oper = 0; + } + ret = CIO_PATHGONE_DEVGONE; + } else { + ret = CIO_PATHGONE_WAIT4INT + | CIO_PATHGONE_IOERR; + ioinfo[irq]->ui.flags.noio = 1; + } + break; + default: /* -ENODEV */ + s390irq_spin_unlock(irq); + if (ioinfo[irq]->nopfunc) { + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->nopfunc(irq, + DEVSTAT_DEVICE_GONE); + } + ret = CIO_PATHGONE_DEVGONE; + } + } + } + return ret; +} + void s390_do_chpid_processing( __u8 chpid) { int irq; int j; - int mask; char dbf_txt[15]; - int ccode; - int was_oper; - int chp = 0; - int mask2; - int ret = 0; sprintf(dbf_txt, "chpr%x", chpid); CIO_TRACE_EVENT( 2, dbf_txt); @@ -7017,141 +7227,82 @@ * but the chpid the report came in through. How to handle??? */ clear_bit(chpid, &chpids); - if (!test_and_clear_bit(chpid, &chpids_known)) + if (!test_and_clear_bit(chpid, &chpids_known)) { +#ifdef CONFIG_DEBUG_CHSC + pr_debug(KERN_DEBUG"Got link incident for unknown chpid %x\n", + chpid); +#endif /* CONFIG_DEBUG_CHSC */ return; /* we didn't know the chpid anyway */ + } for (irq=0;irq<=highest_subchannel;irq++) { + schib_t *schib; + if (ioinfo[irq] == INVALID_STORAGE_AREA) continue; /* we don't know the device anyway */ if (ioinfo[irq]->st) continue; /* non-io subchannel */ + schib = &ioinfo[irq]->schib; for (j=0; j<8;j++) { - if (ioinfo[irq]->schib.pmcw.chpid[j] == chpid) { - - /* - * Send nops down each path to find out - * which path is gone (ssch will yield cc=3 - * and the path will be switched off in the opm) - */ - - /* - * Note: irq spinlock is grabbed several times - * in here - */ - for (chp=0;chp<=7;chp++) { - mask2 = 0x80 >> chp; - if (mask2 & ioinfo[irq]->opm) - ret = s390_send_nop (irq, mask2); - } - - s390irq_spin_lock(irq); - - /* - * FIXME: is this neccessary? - */ - ccode = stsch(irq, &ioinfo[irq]->schib); - if (ccode) { -#ifdef CONFIG_DEBUG_CRW - printk( KERN_WARNING - "do_crw_pending: device on " - "sch %x is not operational\n", - irq); -#endif /* CONFIG_DEBUG_CRW */ - CIO_CRW_EVENT( 2, - "device on sch %x is " - "not operational\n", - irq); - ioinfo[irq]->ui.flags.oper = 0; + int mask = 0x80 >> j; + int out = 0; + int err = 0; + + if (schib->pmcw.chpid[j] != chpid) + continue; + + if (stsch(irq, schib) != 0) { + ioinfo[irq]->ui.flags.oper = 0; + if (ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_DEVICE_GONE); + break; + } + + s390irq_spin_lock(irq); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) { + int rck = __check_for_io_and_kill(irq, mask, 1); + if (rck & CIO_PATHGONE_WAIT4INT) + out=1; + if (rck & CIO_PATHGONE_IOERR) + err=1; + if (rck & CIO_PATHGONE_DEVGONE) break; - } - - ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim & - ioinfo[irq]->schib.pmcw.pam & - ioinfo[irq]->schib.pmcw.pom; - - if (ioinfo[irq]->opm) { - for (chp=0;chp<=7;chp++) { - mask2 = 0x80 >> chp; - if (ioinfo[irq]->opm & mask2) { - if (!test_bit - (ioinfo[irq]-> - schib.pmcw.chpid[chp], - &chpids_logical)) { - /* disable using this path */ - ioinfo[irq]->opm - &= ~mask2; - } - } - } - } + } + + s390irq_spin_unlock(irq); + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + schib->pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) { + if (err) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC_ERR); + else + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + } - if (!ioinfo[irq]->opm) { - /* - * sh*t, our last path has gone... - * Set the device status to not operational - * and eventually notify the device driver - */ -#ifdef CONFIG_DEBUG_CRW - printk( KERN_WARNING - "do_crw_pending: Last path gone for " - "device %x, sch %x\n", - ioinfo[irq]->devno, irq); -#endif /* CONFIG_DEBUG_CRW */ - CIO_CRW_EVENT( 2, - "Last path gone for " - "device %x, sch %x\n", - ioinfo[irq]->devno, - irq); - - was_oper = ioinfo[irq]->ui.flags.oper; - - ioinfo[irq]->ui.flags.oper = 0; - - if (was_oper && ioinfo[irq]->ui.flags.ready) { - - not_oper_handler_func_t nopfunc = - ioinfo[irq]->nopfunc; -#ifdef CONFIG_PROC_FS - if (cio_proc_devinfo) - cio_procfs_device_remove - (ioinfo[irq]->devno); -#endif /* CONFIG_PROC_FS */ - free_irq(irq, - ioinfo[irq]->irq_desc.dev_id); - if (nopfunc) - nopfunc(irq, - DEVSTAT_DEVICE_GONE); - } - - } else if (ioinfo[irq]->ui.flags.ready) { - /* - * Re-do path verification for the chpid in question - * FIXME: is this neccessary? - */ - mask = 0x80 >> j; + ioinfo[irq]->opm &= ~mask; + + if (out) + break; - if (!s390_DevicePathVerification(irq,mask)) { -#ifdef CONFIG_DEBUG_CRW - printk( KERN_DEBUG - "DevicePathVerification " - "successful for" - " Subchannel %x, " - "chpid %x\n", - irq, chpid); -#endif /* CONFIG_DEBUG_CRW */ - CIO_CRW_EVENT( 2, - "DevicePathVerification " - "successful for" - " Subchannel %x, " - "chpid %x\n", - irq, chpid); - } - } - - j=8; - s390irq_spin_unlock(irq); + /* + * Always schedule the path verification, even if opm=0. + * Reason: We can't rely on stsch() to return latest&greatest + * values, if a device selections hasn't been performed, and + * we might miss a path we didn't get a mchk for. + */ + if (ioinfo[irq]->ui.flags.ready) + s390_schedule_path_verification(irq); + else { + ioinfo[irq]->ui.flags.noio = 0; + ioinfo[irq]->ui.flags.killio = 0; } - + break; } } } @@ -7163,13 +7314,11 @@ char dbf_txt[15]; int irq = 0; - int ccode; __u32 fla_mask = 0xffff; int chp; - int mask, mask2; - int ret; + int mask; - sprintf(dbf_txt, "accp%x", chpid); + sprintf(dbf_txt, "accpr%x", chpid); CIO_TRACE_EVENT( 2, dbf_txt); if (info != CHSC_SEI_ACC_CHPID) { sprintf(dbf_txt, "fla%x", fla); @@ -7204,8 +7353,13 @@ return; } - if (!test_bit(chpid, &chpids_logical)) + if (!test_bit(chpid, &chpids_logical)) { +#ifdef CONFIG_DEBUG_CHSC + printk(KERN_DEBUG"chpid %x is logically offline, " + "skipping res acc processing\n", chpid); +#endif /* CONFIG_DEBUG_CHSC */ return; /* no need to do the rest */ + } switch (info) { case CHSC_SEI_ACC_CHPID: /* @@ -7216,62 +7370,13 @@ printk( KERN_DEBUG "Looking at chpid %x...\n", chpid); #endif /* CONFIG_DEBUG_CHSC */ - for (irq=0; irq<=__MAX_SUBCHANNELS; irq++) { + for (irq=0; irq<__MAX_SUBCHANNELS; irq++) { if((ioinfo[irq] != INVALID_STORAGE_AREA) - && (!ioinfo[irq]->st)) { + && (ioinfo[irq]->st != 0)) + continue; - /* - * Send nops down each path to find out - * which path is there - */ - - for (chp=0;chp<=7;chp++) { - mask = 0x80 >> chp; - - /* - * check if chpid is in information - * updated by ssd - */ - if ((ioinfo[irq]->ssd_info.valid) && - (ioinfo[irq]->ssd_info.chpid[chp] - == chpid)) - ret = s390_send_nop (irq, mask); - } - - ccode = stsch(irq, &ioinfo[irq]->schib); - if (!ccode) { - - ioinfo[irq]->opm = - ioinfo[irq]->schib.pmcw.pim & - ioinfo[irq]->schib.pmcw.pam & - ioinfo[irq]->schib.pmcw.pom; - - if (ioinfo[irq]->opm) { - for (chp=0;chp<=7;chp++) { - mask = 0x80 >> chp; - if (ioinfo[irq]->opm - & mask) { - if (!test_bit - (ioinfo[irq]-> - schib.pmcw.chpid[chp], - &chpids_logical)) { - /* disable using this path */ - ioinfo[irq]->opm - &= ~mask; - } - } - } - } - - if ((ioinfo[irq]->ui.flags.ready) - && (chpid & ioinfo[irq]->opm)) - s390_DevicePathVerification(irq, chpid); - - } else { - ioinfo[irq]->ui.flags.oper = 0; - } - } else if (ioinfo[irq] == INVALID_STORAGE_AREA) { + if (ioinfo[irq] == INVALID_STORAGE_AREA) { /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -7291,9 +7396,41 @@ highest_subchannel = irq; if (valret == 0) s390_device_recognition_irq(irq); + continue; } - } + for (chp=0;chp<=7;chp++) { + mask = 0x80 >> chp; + + /* + * check if chpid is in information + * updated by ssd + */ + if ((!ioinfo[irq]->ssd_info.valid) || + (ioinfo[irq]->ssd_info.chpid[chp] != chpid)) + continue; + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + ioinfo[irq]->schib.pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) + /* Wait for interrupt. */ + break; + + if (ioinfo[irq]->ui.flags.ready) { + s390_schedule_path_verification(irq); + } else + ioinfo[irq]->ui.flags.noio = 0; + + break; + } + } break; case CHSC_SEI_ACC_LINKADDR: /* @@ -7313,61 +7450,16 @@ chpid, fla); #endif /* CONFIG_DEBUG_CHSC */ - for (irq=0; irq<=__MAX_SUBCHANNELS; irq++) { - + for (irq=0; irq<__MAX_SUBCHANNELS; irq++) { + int j; /* * Walk through all subchannels and * look if our chpid and our (masked) link * address are in somewhere * Do a stsch for the found subchannels and * perform path grouping - */ - if ((ioinfo[irq] != INVALID_STORAGE_AREA) - && (!ioinfo[irq]->st)) { - int j; - - /* Update our ssd_info */ - if (chsc_get_sch_desc_irq(irq)) - break; - - for (j=0;j<8;j++) { - if ((ioinfo[irq]->ssd_info.chpid[j] == chpid) && - ((ioinfo[irq]->ssd_info.fla[j]&fla_mask) == fla)) { - - mask2 = 0x80 >> j; - ret = s390_send_nop (irq, mask2); - - ccode = stsch(irq,&ioinfo[irq]->schib); - if (!ccode) { - ioinfo[irq]->opm = - ioinfo[irq]->schib.pmcw.pim & - ioinfo[irq]->schib.pmcw.pam & - ioinfo[irq]->schib.pmcw.pom; - - if (ioinfo[irq]->opm) { - for (chp=0;chp<=7;chp++) { - mask = 0x80 >> chp; - if (ioinfo[irq]->opm - & mask) { - if (!test_bit - (ioinfo[irq]-> - schib.pmcw.chpid[chp], - &chpids_logical)) { - /* disable using this path */ - ioinfo[irq]->opm - &= ~mask; - } - } - } - } - - if (ioinfo[irq]->ui.flags.ready) - s390_DevicePathVerification(irq, chpid); - } - break; - } - } - } else if (ioinfo[irq] == INVALID_STORAGE_AREA) { + */ + if (ioinfo[irq] == INVALID_STORAGE_AREA) { /* The full program again (see above), grr... */ int valret = 0; @@ -7380,10 +7472,43 @@ highest_subchannel = irq; if (valret == 0) s390_device_recognition_irq(irq); + continue; + } + if (ioinfo[irq]->st != 0) + continue; + + /* Update our ssd_info */ + if (chsc_get_sch_desc_irq(irq)) + break; + + for (j=0;j<8;j++) { + if ((ioinfo[irq]->ssd_info.chpid[j] != chpid) || + ((ioinfo[irq]->ssd_info.fla[j]&fla_mask) != fla)) + continue; + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + ioinfo[irq]->schib.pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) + /* Wait for interrupt. */ + break; + + if (ioinfo[irq]->ui.flags.ready) { + s390_schedule_path_verification(irq); + } else + ioinfo[irq]->ui.flags.noio = 0; + break; } + break; + } - break; default: BUG(); @@ -7394,7 +7519,7 @@ s390_process_css( void ) { - int ccode; + int ccode, do_sei; CIO_TRACE_EVENT( 2, "prcss"); @@ -7414,18 +7539,26 @@ return; } - /* - * build the chsc request block for store event information - * and do the call - */ - memset(chsc_area_sei,0,sizeof(chsc_area_t)); - chsc_area_sei->request_block.command_code1=0x0010; - chsc_area_sei->request_block.command_code2=0x000E; + do_sei = 1; - ccode = chsc(chsc_area_sei); + while (do_sei) { + + do_sei = 0; + /* + * build the chsc request block for store event information + * and do the call + */ + memset(chsc_area_sei,0,sizeof(chsc_area_t)); + chsc_area_sei->request_block.command_code1=0x0010; + chsc_area_sei->request_block.command_code2=0x000E; + + ccode = chsc(chsc_area_sei); + + + if (ccode) + break; - if (!ccode) { /* for debug purposes, check for problems */ if (chsc_area_sei->response_block.response_code == 0x0003) { #ifdef CONFIG_DEBUG_CHSC @@ -7435,8 +7568,9 @@ CIO_CRW_EVENT( 2, "s390_process_css: " "error in chsc request block!\n"); - - } else if (chsc_area_sei->response_block.response_code == 0x0005) { + break; + } + if (chsc_area_sei->response_block.response_code == 0x0005) { #ifdef CONFIG_DEBUG_CHSC printk( KERN_WARNING "s390_process_css: no event information stored\n"); @@ -7444,8 +7578,9 @@ CIO_CRW_EVENT( 2, "s390_process_css: " "no event information stored\n"); - - } else if (chsc_area_sei->response_block.response_code == 0x0002) { + break; + } + if (chsc_area_sei->response_block.response_code == 0x0002) { #ifdef CONFIG_DEBUG_CHSC printk( KERN_WARNING "s390_process_css: invalid command!\n"); @@ -7453,157 +7588,350 @@ CIO_CRW_EVENT( 2, "s390_process_css: " "invalid command!\n"); + break; + } + if (chsc_area_sei->response_block.response_code != 0x0001) { +#ifdef CONFIG_DEBUG_CHSC + printk( KERN_WARNING + "s390_process_css: unknown response code %d\n", + chsc_area_sei->response_block.response_code); +#endif /* CONFIG_DEBUG_CHSC */ + CIO_CRW_EVENT( 2, + "s390_process_css: unknown response " + "code %d\n", + chsc_area_sei->response_block.response_code); + break; + } + /* everything ok */ +#ifdef CONFIG_DEBUG_CHSC + printk( KERN_DEBUG + "s390_process_css: " + "event information successfully stored\n"); +#endif /* CONFIG_DEBUG_CHSC */ + CIO_CRW_EVENT( 4, + "s390_process_css: " + "event information successfully stored\n"); + + /* Check if there is more event information pending. */ + if (chsc_area_sei->response_block.response_block_data. + sei_res.flags & 0x80) { +#ifdef CONFIG_DEBUG_CHSC + printk(KERN_INFO"s390_process_css: further event " + "information pending...\n"); +#endif /* CONFIG_DEBUG_CHSC */ + CIO_CRW_EVENT( 2, "further event information pending\n"); - } else if (chsc_area_sei->response_block.response_code == 0x0001) { - /* everything ok */ + do_sei = 1; + } + + /* Check if we might have lost some information. */ + if (chsc_area_sei->response_block.response_block_data. + sei_res.flags & 0x40) { #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG + printk(KERN_ERR"s390_process_css: Event information has " + "been lost due to overflow!\n"); +#endif /* CONFIG_DEBUG_CHSC */ + CIO_CRW_EVENT( 2, "Event information has " + "been lost due to overflow!\n"); + } + + if (chsc_area_sei->response_block. + response_block_data.sei_res.rs != 4) { +#ifdef CONFIG_DEBUG_CHSC + printk( KERN_ERR "s390_process_css: " - "event information successfully stored\n"); + "reporting source (%04X) isn't a chpid!\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid); #endif /* CONFIG_DEBUG_CHSC */ - CIO_CRW_EVENT( 4, + CIO_CRW_EVENT( 2, "s390_process_css: " - "event information successfully stored\n"); + "reporting source (%04X) isn't a chpid!\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid); + continue; + } - if (chsc_area_sei->response_block. - response_block_data.sei_res.rs != 4) { + /* which kind of information was stored? */ + switch (chsc_area_sei->response_block. + response_block_data.sei_res.cc) { + case 1: /* link incident*/ #ifdef CONFIG_DEBUG_CHSC - printk( KERN_ERR - "s390_process_css: " - "reporting source (%04X) isn't a chpid!" - "Aborting processing of machine check...\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid); + printk( KERN_DEBUG + "s390_process_css: " + "channel subsystem reports link incident," + " source is chpid %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid); #endif /* CONFIG_DEBUG_CHSC */ - CIO_CRW_EVENT( 2, - "s390_process_css: " - "reporting source (%04X) isn't a chpid!" - "Aborting processing of machine check...\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid); - return; - } + CIO_CRW_EVENT( 4, + "s390_process_css: " + "channel subsystem reports " + "link incident, " + "source is chpid %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid); + + s390_do_chpid_processing(chsc_area_sei->response_block. + response_block_data.sei_res.rsid); + + break; - /* which kind of information was stored? */ - switch (chsc_area_sei->response_block. - response_block_data.sei_res.cc) { - case 1: /* link incident*/ + case 2: /* i/o resource accessibiliy */ #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "s390_process_css: " - "channel subsystem reports link incident," - " source is chpid %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid); + printk( KERN_DEBUG + "s390_process_css: channel subsystem " + "reports some I/O devices " + "may have become accessible\n"); #endif /* CONFIG_DEBUG_CHSC */ - CIO_CRW_EVENT( 4, - "s390_process_css: " - "channel subsystem reports " - "link incident, " - "source is chpid %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid); - - s390_do_chpid_processing(chsc_area_sei->response_block. - response_block_data.sei_res.rsid); - - break; - - case 2: /* i/o resource accessibiliy */ + CIO_CRW_EVENT( 4, + "s390_process_css: " + "channel subsystem reports " + "some I/O devices " + "may have become accessible\n"); #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "s390_process_css: channel subsystem " - "reports some I/O devices " - "may have become accessable\n"); + printk( KERN_DEBUG + "Data received after sei: \n"); + printk( KERN_DEBUG + "Validity flags: %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.vf); #endif /* CONFIG_DEBUG_CHSC */ - CIO_CRW_EVENT( 4, - "s390_process_css: " - "channel subsystem reports " - "some I/O devices " - "may have become accessable\n"); + if ((chsc_area_sei->response_block. + response_block_data.sei_res.vf&0x80) + == 0) { #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "Data received after sei: \n"); - printk( KERN_DEBUG - "Validity flags: %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.vf); + printk( KERN_DEBUG "chpid: %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid); #endif /* CONFIG_DEBUG_CHSC */ - if ((chsc_area_sei->response_block. - response_block_data.sei_res.vf&0x80) - == 0) { + s390_do_res_acc_processing + (chsc_area_sei->response_block. + response_block_data.sei_res.rsid, + 0, + CHSC_SEI_ACC_CHPID); + } else if ((chsc_area_sei->response_block. + response_block_data.sei_res.vf&0xc0) + == 0x80) { #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG "chpid: %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid); + printk( KERN_DEBUG + "chpid: %x link addr: %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid, + chsc_area_sei->response_block. + response_block_data.sei_res.fla); #endif /* CONFIG_DEBUG_CHSC */ - s390_do_res_acc_processing - (chsc_area_sei->response_block. - response_block_data.sei_res.rsid, - 0, - CHSC_SEI_ACC_CHPID); - } else if ((chsc_area_sei->response_block. - response_block_data.sei_res.vf&0xc0) - == 0x80) { + s390_do_res_acc_processing + (chsc_area_sei->response_block. + response_block_data.sei_res.rsid, + chsc_area_sei->response_block. + response_block_data.sei_res.fla, + CHSC_SEI_ACC_LINKADDR); + } else if ((chsc_area_sei->response_block. + response_block_data.sei_res.vf & 0xc0) + == 0xc0) { #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "chpid: %x link addr: %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid, - chsc_area_sei->response_block. - response_block_data.sei_res.fla); + printk( KERN_DEBUG + "chpid: %x " + "full link addr: %x\n", + chsc_area_sei->response_block. + response_block_data.sei_res.rsid, + chsc_area_sei->response_block. + response_block_data.sei_res.fla); #endif /* CONFIG_DEBUG_CHSC */ - s390_do_res_acc_processing - (chsc_area_sei->response_block. - response_block_data.sei_res.rsid, - chsc_area_sei->response_block. - response_block_data.sei_res.fla, - CHSC_SEI_ACC_LINKADDR); - } else if ((chsc_area_sei->response_block. - response_block_data.sei_res.vf & 0xc0) - == 0xc0) { + s390_do_res_acc_processing + (chsc_area_sei->response_block. + response_block_data.sei_res.rsid, + chsc_area_sei->response_block. + response_block_data.sei_res.fla, + CHSC_SEI_ACC_FULLLINKADDR); + } #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "chpid: %x " - "full link addr: %x\n", - chsc_area_sei->response_block. - response_block_data.sei_res.rsid, - chsc_area_sei->response_block. - response_block_data.sei_res.fla); + printk( KERN_DEBUG "\n"); #endif /* CONFIG_DEBUG_CHSC */ - s390_do_res_acc_processing - (chsc_area_sei->response_block. - response_block_data.sei_res.rsid, - chsc_area_sei->response_block. - response_block_data.sei_res.fla, - CHSC_SEI_ACC_FULLLINKADDR); - } + + break; + + default: /* other stuff */ #ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG "\n"); + printk( KERN_DEBUG + "s390_process_css: event %d\n", + chsc_area_sei->response_block. + response_block_data.sei_res.cc); #endif /* CONFIG_DEBUG_CHSC */ + CIO_CRW_EVENT( 4, + "s390_process_css: event %d\n", + chsc_area_sei->response_block. + response_block_data.sei_res.cc); + + break; + + } + } + + spin_unlock(&chsc_lock_sei); +} +#endif +static void +__process_chp_gone(int irq, int chpid) +{ + schib_t *schib = &ioinfo[irq]->schib; + int i; + + for (i=0;i<8;i++) { + int mask = 0x80>>i; + int out = 0; + int err = 0; + + if (schib->pmcw.chpid[i] != chpid) + continue; + + if (stsch(irq, schib) != 0) { + ioinfo[irq]->ui.flags.oper = 0; + if (ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_DEVICE_GONE); + break; + } + + s390irq_spin_lock(irq); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) { + int rck = __check_for_io_and_kill(irq, mask, 1); + if (rck & CIO_PATHGONE_WAIT4INT) + out=1; + if (rck & CIO_PATHGONE_IOERR) + err=1; + if (rck & CIO_PATHGONE_DEVGONE) break; + } + + s390irq_spin_unlock(irq); - default: /* other stuff */ -#ifdef CONFIG_DEBUG_CHSC - printk( KERN_DEBUG - "s390_process_css: event %d\n", - chsc_area_sei->response_block. - response_block_data.sei_res.cc); -#endif /* CONFIG_DEBUG_CHSC */ - CIO_CRW_EVENT( 4, - "s390_process_css: event %d\n", - chsc_area_sei->response_block. - response_block_data.sei_res.cc); + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + schib->pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) { + if (err) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC_ERR); + else + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + } + + if (out) + break; + + if (ioinfo[irq]->ui.flags.ready) { + s390_schedule_path_verification(irq); + } else { + ioinfo[irq]->ui.flags.noio = 0; + ioinfo[irq]->ui.flags.killio = 0; + } + break; + } - break; +} - } +static void +__process_chp_come(int irq, int chpid) +{ + schib_t *schib = &ioinfo[irq]->schib; + int i; + + for (i=0;i<8;i++) { + + if (schib->pmcw.chpid[i] != chpid) + continue; + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + schib->pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) + /* Wait for interrupt. */ + break; + + if (ioinfo[irq]->ui.flags.ready) + s390_schedule_path_verification(irq); + else + ioinfo[irq]->ui.flags.noio = 0; + + break; + } +} + +static void +s390_process_chp_source(int chpid, int onoff) +{ + int irq; + int ret; + char dbf_txt[15]; + + sprintf(dbf_txt, "prchp%x", chpid); + CIO_TRACE_EVENT(2, dbf_txt); + +#ifdef CONFIG_CHSC + if (onoff == 0) { + clear_bit(chpid, &chpids); + } else { + set_bit(chpid, &chpids); + set_bit(chpid, &chpids_known); + } +#endif /* CONFIG_CHSC */ + + if (onoff == 0) { + for (irq=0;irq<=highest_subchannel;irq++) { + + if ((ioinfo[irq] == INVALID_STORAGE_AREA) + || (ioinfo[irq]->st != 0)) + continue; + + __process_chp_gone(irq, chpid); } + return; } - spin_unlock(&chsc_lock_sei); + + for (irq=0;irq<__MAX_SUBCHANNELS;irq++) { + + if (ioinfo[irq] == INVALID_STORAGE_AREA) { + ret = s390_validate_subchannel(irq,0); + if (ret == 0) { + if (irq > highest_subchannel) + highest_subchannel = irq; +#ifdef CONFIG_DEBUG_CRW + printk(KERN_DEBUG"process_chp_source: Found " + "device on irq %x\n", irq); +#endif /* CONFIG_DEBUG_CRW */ + CIO_CRW_EVENT(4, "Found device on irq %x\n", + irq); + s390_device_recognition_irq(irq); + } + } else if (ioinfo[irq]->st == 0) { + ret = stsch(irq, &ioinfo[irq]->schib); + if (ret != 0) + ret = -ENXIO; + } else + continue; + + if (ret == -ENXIO) + /* We're through. */ + return; + + if (ret != 0) + continue; + + __process_chp_come(irq, chpid); + } + } -#endif /* * s390_do_crw_pending @@ -7657,7 +7985,24 @@ printk (KERN_NOTICE "do_crw_pending : source is " "channel path %02X\n", chpid); #endif - CIO_CRW_EVENT(2, "source is channel path %02X\n"); + CIO_CRW_EVENT(2, "source is channel path %02X\n", + chpid); + switch (pcrwe->crw.erc) { + case CRW_ERC_IPARM: /* Path has come. */ + s390_process_chp_source(chpid, 1); + break; + case CRW_ERC_PERRI: /* Path has gone. */ + s390_process_chp_source(chpid, 0); + break; + default: +#ifdef CONFIG_DEBUG_CRW + printk(KERN_WARNING"do_crw_pending: don't " + "know how to handle erc=%x\n", + pcrwe->crw.erc); +#endif /* CONFIG_DEBUG_CRW */ + CIO_CRW_EVENT(0, "don't know how to handle " + "erc=%x\n", pcrwe->crw.erc); + } break; case CRW_RSC_CONFIG: @@ -7850,6 +8195,84 @@ } } +static void +__vary_chpid_offline(int irq, int chpid) +{ + schib_t *schib = &ioinfo[irq]->schib; + int i; + + for (i=0;i<8;i++) { + int mask = 0x80>>i; + int out = 0; + unsigned long flags; + + if (ioinfo[irq]->ssd_info.chpid[i] != chpid) + continue; + + s390irq_spin_lock_irqsave(irq, flags); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Hmm, the path is not really gone... */ + if (ioinfo[irq]->ui.flags.busy) { + if (__check_for_io_and_kill(irq, mask, 0) != 0) + out=1; + } + + s390irq_spin_unlock_irqrestore(irq, flags); + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + schib->pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + if (out) + break; + + if (ioinfo[irq]->ui.flags.ready) + s390_schedule_path_verification(irq); + else + ioinfo[irq]->ui.flags.noio = 0; + + break; + } + +} + +static void +__vary_chpid_online(int irq, int chpid) +{ + schib_t *schib = &ioinfo[irq]->schib; + int i; + + for (i=0;i<8;i++) { + + if (schib->pmcw.chpid[i] != chpid) + continue; + + /* Tell the device driver not to disturb us. */ + if (ioinfo[irq]->ui.flags.ready && + schib->pmcw.pim != 0x80 && + ioinfo[irq]->nopfunc) + ioinfo[irq]->nopfunc(irq, DEVSTAT_NOT_ACC); + + ioinfo[irq]->ui.flags.noio = 1; + + /* Do we still expect an interrupt for outstanding io? */ + if (ioinfo[irq]->ui.flags.busy) + /* Wait for interrupt. */ + break; + + if (ioinfo[irq]->ui.flags.ready) + s390_schedule_path_verification(irq); + else + ioinfo[irq]->ui.flags.noio = 0; + + break; + } +} + /* * Function: s390_vary_chpid @@ -7899,34 +8322,20 @@ for (irq=0;irq<=highest_subchannel;irq++) { - /* - * We don't need to adjust the opm, as this will be done in - * DevicePathVerification... - */ - if (ioinfo[irq] == INVALID_STORAGE_AREA) continue; if (ioinfo[irq]->st) continue; + + if (!ioinfo[irq]->ssd_info.valid) + continue; + + if (on) + __vary_chpid_online(irq, chpid); + else + __vary_chpid_offline(irq, chpid); - if (ioinfo[irq]->ssd_info.valid) { - if ((ioinfo[irq]->ssd_info.chpid[0] == chpid) || - (ioinfo[irq]->ssd_info.chpid[1] == chpid) || - (ioinfo[irq]->ssd_info.chpid[2] == chpid) || - (ioinfo[irq]->ssd_info.chpid[3] == chpid) || - (ioinfo[irq]->ssd_info.chpid[4] == chpid) || - (ioinfo[irq]->ssd_info.chpid[5] == chpid) || - (ioinfo[irq]->ssd_info.chpid[6] == chpid) || - (ioinfo[irq]->ssd_info.chpid[7] == chpid)) { -#ifdef CONFIG_DEBUG_CHSC - printk(KERN_DEBUG "Calling " - "DevicePathVerification for irq %d\n", - irq); -#endif /* CONFIG_DEBUG_CHSC */ - s390_DevicePathVerification(irq, 0); - } - } } return 0; @@ -8624,9 +9033,6 @@ #ifdef CONFIG_DEBUG_IO printk (KERN_DEBUG "/proc/cio_ignore: '%s'\n", buffer); #endif /* CONFIG_DEBUG_IO */ - if (cio_debug_initialized) - debug_sprintf_event (cio_debug_msg_id, 2, - "/proc/cio_ignore: '%s'\n", buffer); blacklist_parse_proc_parameters (buffer); @@ -8795,19 +9201,28 @@ chsc_get_sch_descriptions(); if (!cio_chsc_desc_avail) { len += sprintf(info->data+len, "no info available\n"); - } else { - for (i=0;idata+len, + "%02X n/a\n", + i); + else if (test_bit(i, &chpids_logical)) len += sprintf(info->data+len, "%02X online\n", i); - else if (test_bit(i, &chpids_known)) + else len += sprintf(info->data+len, - "%02X logically offline\n", + "%02X logically " + "offline\n", i); } + } + cont: info->len = len; } } @@ -8940,6 +9355,7 @@ EXPORT_SYMBOL (do_IO); EXPORT_SYMBOL (resume_IO); EXPORT_SYMBOL (ioinfo); +EXPORT_SYMBOL (diag210); EXPORT_SYMBOL (get_dev_info_by_irq); EXPORT_SYMBOL (get_dev_info_by_devno); EXPORT_SYMBOL (get_irq_by_devno); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/s390/s390mach.c linux.21rc1-ac2/drivers/s390/s390mach.c --- linux.21rc1/drivers/s390/s390mach.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/s390/s390mach.c 2003-04-25 13:48:57.000000000 +0100 @@ -121,7 +121,7 @@ #if 1 ctl_set_bit( 14, 28 ); // enable channel report MCH #endif -#ifdef CONFIG_MACHCK_WARNING +#ifdef CONFIG_MACHCHK_WARNING ctl_set_bit( 14, 24); /* enable warning MCH */ #endif @@ -152,7 +152,6 @@ return; } - /* * s390_do_machine_check * @@ -327,6 +326,16 @@ } /* endif */ #endif +#ifdef CONFIG_MACHCHK_WARNING + if ( pmache->mcic.mcc.mcd.w ) + { + ctrl_alt_del(); // shutdown NOW! +#ifdef S390_MACHCHK_DEBUG + printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n"); +#endif + } /* endif */ +#endif + s390_enqueue_free_mchchk( pmache ); } else @@ -655,4 +664,3 @@ return ( 1 ); } #endif - diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sbus/char/aurora.c linux.21rc1-ac2/drivers/sbus/char/aurora.c --- linux.21rc1/drivers/sbus/char/aurora.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/sbus/char/aurora.c 2003-04-22 16:44:37.000000000 +0100 @@ -1504,7 +1504,7 @@ } bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (port->count != 1)) { printk(KERN_DEBUG "aurora%d: aurora_close: bad port count; " "tty->count is 1, port count is %d\n", board_No(bp), port->count); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sbus/char/sab82532.c linux.21rc1-ac2/drivers/sbus/char/sab82532.c --- linux.21rc1/drivers/sbus/char/sab82532.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/sbus/char/sab82532.c 2003-04-22 16:44:37.000000000 +0100 @@ -1610,7 +1610,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("sab82532_close ttys%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sbus/char/su.c linux.21rc1-ac2/drivers/sbus/char/su.c --- linux.21rc1/drivers/sbus/char/su.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/sbus/char/su.c 2003-04-22 16:44:37.000000000 +0100 @@ -39,7 +39,7 @@ do { \ printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ kdevname(tty->device), (info->flags), serial_refcount, \ - info->count,tty->count,s); \ + info->count,atomic_read(&tty->count),s); \ } while (0) #else #define DBG_CNT(s) @@ -1756,7 +1756,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("su_close ttys%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sbus/char/zs.c linux.21rc1-ac2/drivers/sbus/char/zs.c --- linux.21rc1/drivers/sbus/char/zs.c 2003-04-22 16:38:49.000000000 +0100 +++ linux.21rc1-ac2/drivers/sbus/char/zs.c 2003-04-22 16:44:37.000000000 +0100 @@ -1547,7 +1547,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("zs_close tty-%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/aha152x.c linux.21rc1-ac2/drivers/scsi/aha152x.c --- linux.21rc1/drivers/scsi/aha152x.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/aha152x.c 2003-04-22 16:44:37.000000000 +0100 @@ -2649,7 +2649,7 @@ static void datai_run(struct Scsi_Host *shpnt) { - unsigned int the_time; + unsigned long the_time; int fifodata, data_count; /* @@ -2791,7 +2791,7 @@ static void datao_run(struct Scsi_Host *shpnt) { - unsigned int the_time; + unsigned long the_time; int data_count; /* until phase changes or all data sent */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/cpqfcTSinit.c linux.21rc1-ac2/drivers/scsi/cpqfcTSinit.c --- linux.21rc1/drivers/scsi/cpqfcTSinit.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/cpqfcTSinit.c 2003-04-22 16:44:37.000000000 +0100 @@ -468,8 +468,10 @@ // Need data from user? // make sure caller's buffer is in kernel space. if ((vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) && vendor_cmd->len) - if (copy_from_user(buf, vendor_cmd->bufp, vendor_cmd->len)) + if (copy_from_user(buf, vendor_cmd->bufp, vendor_cmd->len)) { + kfree(buf); return (-EFAULT); + } // copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below) memcpy(&ScsiPassThruCmnd->cmnd[0], &vendor_cmd->cdb[0], MAX_COMMAND_SIZE); @@ -534,7 +536,7 @@ // need to pass data back to user (space)? if ((vendor_cmd->rw_flag == VENDOR_READ_OPCODE) && vendor_cmd->len) if (copy_to_user(vendor_cmd->bufp, buf, vendor_cmd->len)) - return (-EFAULT); + result = -EFAULT; if (buf) kfree(buf); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/ide-scsi.c linux.21rc1-ac2/drivers/scsi/ide-scsi.c --- linux.21rc1/drivers/scsi/ide-scsi.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/ide-scsi.c 2003-04-22 16:44:37.000000000 +0100 @@ -321,7 +321,7 @@ { idescsi_scsi_t *scsi = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; - idescsi_pc_t *pc = (idescsi_pc_t *) rq->buffer; + idescsi_pc_t *pc = (idescsi_pc_t *) rq->special; int log = test_bit(IDESCSI_LOG_CMD, &scsi->log); u8 *scsi_buf; unsigned long flags; @@ -587,7 +587,7 @@ #endif /* IDESCSI_DEBUG_LOG */ if (rq->cmd == IDESCSI_PC_RQ) { - return idescsi_issue_pc(drive, (idescsi_pc_t *) rq->buffer); + return idescsi_issue_pc(drive, rq->special); } printk(KERN_ERR "ide-scsi: %s: unsupported command in request " "queue (%x)\n", drive->name, rq->cmd); @@ -1083,7 +1083,7 @@ } ide_init_drive_cmd(rq); - rq->buffer = (char *) pc; + rq->special = pc; rq->bh = idescsi_dma_bh(drive, pc); rq->cmd = IDESCSI_PC_RQ; spin_unlock_irq(&io_request_lock); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/ips.c linux.21rc1-ac2/drivers/scsi/ips.c --- linux.21rc1/drivers/scsi/ips.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/ips.c 2003-04-22 16:44:37.000000000 +0100 @@ -246,9 +246,6 @@ #define IPS_SG_ADDRESS(sg) ((sg)->address) #define IPS_LOCK_SAVE(lock,flags) spin_lock_irqsave(&io_request_lock,flags) #define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock_irqrestore(&io_request_lock,flags) - #ifndef __devexit_p - #define __devexit_p(x) x - #endif #else #define IPS_SG_ADDRESS(sg) (page_address((sg)->page) ? \ page_address((sg)->page)+(sg)->offset : 0) @@ -338,48 +335,48 @@ static char ips_hot_plug_name[] = "ips"; static int __devinit ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent); - static void __devexit ips_remove_device(struct pci_dev *pci_dev); + static void ips_remove_device(struct pci_dev *pci_dev); struct pci_driver ips_pci_driver = { name: ips_hot_plug_name, id_table: ips_pci_table, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; struct pci_driver ips_pci_driver_anaconda = { name: ips_hot_plug_name, id_table: ips_pci_table_anaconda, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; struct pci_driver ips_pci_driver_5i = { name: ips_hot_plug_name, id_table: ips_pci_table_5i, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; struct pci_driver ips_pci_driver_6i = { name: ips_hot_plug_name, id_table: ips_pci_table_6i, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; struct pci_driver ips_pci_driver_i960 = { name: ips_hot_plug_name, id_table: ips_pci_table_i960, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; struct pci_driver ips_pci_driver_adaptec = { name: ips_hot_plug_name, id_table: ips_pci_table_adaptec, probe: ips_insert_device, - remove: __devexit_p(ips_remove_device), + remove: ips_remove_device, }; #endif @@ -7346,7 +7343,7 @@ /* Routine Description: */ /* Remove one Adapter ( Hot Plugging ) */ /*---------------------------------------------------------------------------*/ -static void __devexit ips_remove_device(struct pci_dev *pci_dev) +static void ips_remove_device(struct pci_dev *pci_dev) { int i; struct Scsi_Host *sh; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/nsp32.c linux.21rc1-ac2/drivers/scsi/nsp32.c --- linux.21rc1/drivers/scsi/nsp32.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/nsp32.c 2003-04-22 16:44:37.000000000 +0100 @@ -2125,7 +2125,7 @@ .name = "nsp32", .id_table = nsp32_pci_table, .probe = nsp32_probe, - .remove = nsp32_remove, + .remove = __devexit_p(nsp32_remove), #ifdef CONFIG_PM /* .suspend = nsp32_suspend,*/ /* .resume = nsp32_resume,*/ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/qlogicisp.c linux.21rc1-ac2/drivers/scsi/qlogicisp.c --- linux.21rc1/drivers/scsi/qlogicisp.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/qlogicisp.c 2003-04-22 16:44:37.000000000 +0100 @@ -722,8 +722,8 @@ continue; fail_uninit: - iounmap((void *)hostdata->memaddr); - release_region(host->io_port, 0xff); + if (hostdata->memaddr) iounmap((void *)hostdata->memaddr); + if (host->io_port) release_region(host->io_port, 0xff); fail_and_unregister: if (hostdata->res_cpu) pci_free_consistent(hostdata->pci_dev, @@ -755,9 +755,9 @@ isp_outw(0x0, host, PCI_INTF_CTL); free_irq(host->irq, host); - iounmap((void *)hostdata->memaddr); + if (hostdata->memaddr) iounmap((void *)hostdata->memaddr); - release_region(host->io_port, 0xff); + if (host->io_port) release_region(host->io_port, 0xff); LEAVE("isp1020_release"); @@ -1396,13 +1396,6 @@ return 1; } -#ifdef __alpha__ - /* Force ALPHA to use bus I/O and not bus MEM. - This is to avoid having to use HAE_MEM registers, - which is broken on some platforms and with SMP. */ - command &= ~PCI_COMMAND_MEMORY; -#endif - if (!(command & PCI_COMMAND_MASTER)) { printk("qlogicisp : bus mastering is disabled\n"); return 1; @@ -1410,26 +1403,34 @@ sh->io_port = io_base; - if (!request_region(sh->io_port, 0xff, "qlogicisp")) { - printk("qlogicisp : i/o region 0x%lx-0x%lx already " - "in use\n", - sh->io_port, sh->io_port + 0xff); - return 1; - } - + /* + By default, we choose to use PCI memory-mapped registers, + if configured/available. + + NOTE: we only ioremap() if we are going to use PCI + memory-mapped registers, or only request_region() if using + PCI I/O registers; we never do both anymore. + */ if ((command & PCI_COMMAND_MEMORY) && ((mem_flags & 1) == 0)) { mem_base = (u_long) ioremap(mem_base, PAGE_SIZE); if (!mem_base) { printk("qlogicisp : i/o remapping failed.\n"); - goto out_release; + return 1; } hostdata->memaddr = mem_base; + sh->io_port = io_base = 0; } else { if (command & PCI_COMMAND_IO && (io_flags & 3) != 1) { printk("qlogicisp : i/o mapping is disabled\n"); - goto out_release; + return 1; } + if (!request_region(sh->io_port, 0xff, "qlogicisp")) { + printk("qlogicisp : i/o region 0x%lx-0x%lx already " + "in use\n", + sh->io_port, sh->io_port + 0xff); + return 1; + } hostdata->memaddr = 0; /* zero to signify no i/o mapping */ mem_base = 0; } @@ -1477,9 +1478,8 @@ return 0; out_unmap: - iounmap((void *)hostdata->memaddr); -out_release: - release_region(sh->io_port, 0xff); + if (mem_base) iounmap((void *)hostdata->memaddr); + if (io_base) release_region(sh->io_port, 0xff); return 1; } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/README.st linux.21rc1-ac2/drivers/scsi/README.st --- linux.21rc1/drivers/scsi/README.st 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/README.st 2003-04-22 16:44:37.000000000 +0100 @@ -1,8 +1,8 @@ This file contains brief information about the SCSI tape driver. The driver is currently maintained by Kai Mäkisara (email -Kai.Makisara@metla.fi) +Kai.Makisara@kolumbus.fi) -Last modified: Tue Apr 16 22:32:10 2002 by makisara +Last modified: Sun Apr 6 22:44:55 2003 by makisara BASICS @@ -112,7 +112,7 @@ Asynchronous writing. Writing the buffer contents to the tape is started and the write call returns immediately. The status is checked -at the next tape operation. +at the next tape operation. Applies only to variable block mode. Buffered writes and asynchronous writes may in some rare cases cause problems in multivolume operations if there is not enough space on the @@ -147,11 +147,6 @@ supports scatter/gather (the allocation is not limited to "DMA memory" and the buffer can be composed of several fragments). -The threshold for triggering asynchronous write in fixed block mode -is defined by ST_WRITE_THRESHOLD. This may be optimized for each -use pattern. The default triggers asynchronous write after three -default sized writes (10 kB) from tar. - Scatter/gather buffers (buffers that consist of chunks non-contiguous in the physical memory) are used if contiguous buffers can't be allocated. To support all SCSI adapters (including those not @@ -184,7 +179,6 @@ are configurable when the driver is loaded as a module. The keywords are: buffer_kbs=xxx the buffer size in kilobytes is set to xxx -write_threshold_kbs=xxx the write threshold in kilobytes set to xxx max_buffers=xxx the maximum number of tape buffer set to xxx max_sg_segs=xxx the maximum number of scatter/gather segments @@ -213,7 +207,7 @@ where aa is the buffer size in 1024 byte units - bb is the write threshold in 1024 byte units + bb is the write threshold in 1024 byte units (not used any more) cc is the maximum number of tape buffers to allocate (the number of buffers is bounded also by the number of drives detected) dd is the maximum number of scatter/gather segments @@ -321,9 +315,6 @@ MT_ST_SETBOOLEANS MT_ST_CLEARBOOLEANS Sets or clears the option bits. - MT_ST_WRITE_THRESHOLD - Sets the write threshold for this device to kilobytes - specified by the lowest bits. MT_ST_DEF_BLKSIZE Defines the default block size set automatically. Value 0xffffff means that the default is not used any more. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi.c linux.21rc1-ac2/drivers/scsi/scsi.c --- linux.21rc1/drivers/scsi/scsi.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi.c 2003-04-22 16:44:37.000000000 +0100 @@ -352,8 +352,9 @@ int interruptable) { struct Scsi_Host *host; - Scsi_Cmnd *SCpnt = NULL; + Scsi_Cmnd *SCpnt; Scsi_Device *SDpnt; + struct list_head *lp; unsigned long flags; if (!device) @@ -364,7 +365,6 @@ spin_lock_irqsave(&device_request_lock, flags); while (1 == 1) { - SCpnt = NULL; if (!device->device_blocked) { if (device->single_lun) { /* @@ -404,26 +404,21 @@ * If asked to wait, we need to wait, otherwise * return NULL. */ - SCpnt = NULL; goto busy; } } /* - * Now we can check for a free command block for this device. + * Is there a free command block for this device? */ - for (SCpnt = device->device_queue; SCpnt; SCpnt = SCpnt->next) { - if (SCpnt->request.rq_status == RQ_INACTIVE) - break; - } + if (!list_empty(&device->sdev_free_q)) + goto found; } + /* - * If we couldn't find a free command block, and we have been + * Couldn't find a free command block, and we have been * asked to wait, then do so. */ - if (SCpnt) { - break; - } - busy: +busy: /* * If we have been asked to wait for a free block, then * wait here. @@ -475,12 +470,20 @@ return NULL; } } + continue; } else { spin_unlock_irqrestore(&device_request_lock, flags); return NULL; } } +found: + lp = device->sdev_free_q.next; + list_del(lp); + SCpnt = list_entry(lp, Scsi_Cmnd, sc_list); + if (SCpnt->request.rq_status != RQ_INACTIVE) + BUG(); + SCpnt->request.rq_status = RQ_SCSI_BUSY; SCpnt->request.waiting = NULL; /* And no one is waiting for this * to complete */ @@ -526,6 +529,9 @@ SDpnt = SCpnt->device; + /* command is now free - add to list */ + list_add(&SCpnt->sc_list, &SDpnt->sdev_free_q); + SCpnt->request.rq_status = RQ_INACTIVE; SCpnt->state = SCSI_STATE_UNUSED; SCpnt->owner = SCSI_OWNER_NOBODY; @@ -1333,14 +1339,10 @@ */ int scsi_retry_command(Scsi_Cmnd * SCpnt) { - memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; - SCpnt->underflow = SCpnt->old_underflow; + /* + * Restore the SCSI command state. + */ + scsi_setup_cmd_retry(SCpnt); /* * Zero the sense information from the last time we tried @@ -1448,6 +1450,7 @@ spin_lock_irqsave(&device_request_lock, flags); for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { SDpnt->device_queue = SCnext = SCpnt->next; + list_del(&SCpnt->sc_list); kfree((char *) SCpnt); } SDpnt->has_cmdblocks = 0; @@ -1484,6 +1487,7 @@ SDpnt->queue_depth = 1; /* live to fight another day */ } SDpnt->device_queue = NULL; + INIT_LIST_HEAD(&SDpnt->sdev_free_q); for (j = 0; j < SDpnt->queue_depth; j++) { SCpnt = (Scsi_Cmnd *) @@ -1513,6 +1517,7 @@ SDpnt->device_queue = SCpnt; SCpnt->state = SCSI_STATE_UNUSED; SCpnt->owner = SCSI_OWNER_NOBODY; + list_add(&SCpnt->sc_list, &SDpnt->sdev_free_q); } if (j < SDpnt->queue_depth) { /* low on space (D.Gilbert 990424) */ printk(KERN_WARNING "scsi_build_commandblocks: want=%d, space for=%d blocks\n", diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi_error.c linux.21rc1-ac2/drivers/scsi/scsi_error.c --- linux.21rc1/drivers/scsi/scsi_error.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi_error.c 2003-04-22 19:07:25.000000000 +0100 @@ -385,16 +385,10 @@ */ STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt) { - memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; - SCpnt->underflow = SCpnt->old_underflow; - - scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); + do { + scsi_setup_cmd_retry(SCpnt); + scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); + } while (SCpnt->eh_state == NEEDS_RETRY); /* * Hey, we are done. Let's look to see what happened. @@ -425,12 +419,6 @@ ASSERT_LOCK(&io_request_lock, 0); - memcpy((void *) SCpnt->cmnd, (void *) generic_sense, - sizeof(generic_sense)); - - if (SCpnt->device->scsi_level <= SCSI_2) - SCpnt->cmnd[1] = SCpnt->lun << 5; - scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA); @@ -438,24 +426,40 @@ printk("cannot allocate scsi_result in scsi_request_sense.\n"); return FAILED; } - /* - * Zero the sense buffer. Some host adapters automatically always request - * sense, so it is not a good idea that SCpnt->request_buffer and - * SCpnt->sense_buffer point to the same address (DB). - * 0 is not a valid sense code. - */ - memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); - memset((void *) scsi_result, 0, 256); saved_result = SCpnt->result; - SCpnt->request_buffer = scsi_result; - SCpnt->request_bufflen = 256; - SCpnt->use_sg = 0; - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->sc_data_direction = SCSI_DATA_READ; - SCpnt->underflow = 0; - scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + do { + memcpy((void *) SCpnt->cmnd, (void *) generic_sense, + sizeof(generic_sense)); + + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; + + /* + * Zero the sense buffer. Some host adapters automatically + * always request sense, so it is not a good idea that + * SCpnt->request_buffer and SCpnt->sense_buffer point to + * the same address (DB). 0 is not a valid sense code. + */ + memset((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); + memset((void *) scsi_result, 0, 256); + + SCpnt->request_buffer = scsi_result; + SCpnt->request_bufflen = 256; + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->underflow = 0; + + scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + /* + * If the SCSI device responded with "logical unit + * is in process of becoming ready", we need to + * retry this command. + */ + } while (SCpnt->eh_state == NEEDS_RETRY); /* Last chance to have valid sense data */ if (!scsi_sense_valid(SCpnt)) @@ -479,6 +483,7 @@ SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; SCpnt->underflow = SCpnt->old_underflow; + memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); /* * Hey, we are done. Let's look to see what happened. @@ -497,26 +502,34 @@ static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0}; - memcpy((void *) SCpnt->cmnd, (void *) tur_command, - sizeof(tur_command)); + do { + memcpy((void *) SCpnt->cmnd, (void *) tur_command, + sizeof(tur_command)); - if (SCpnt->device->scsi_level <= SCSI_2) - SCpnt->cmnd[1] = SCpnt->lun << 5; + if (SCpnt->device->scsi_level <= SCSI_2) + SCpnt->cmnd[1] = SCpnt->lun << 5; - /* - * Zero the sense buffer. The SCSI spec mandates that any - * untransferred sense data should be interpreted as being zero. - */ - memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); + /* + * Zero the sense buffer. The SCSI spec mandates that any + * untransferred sense data should be interpreted as being zero. + */ + memset((void *) SCpnt->sense_buffer, 0, + sizeof(SCpnt->sense_buffer)); - SCpnt->request_buffer = NULL; - SCpnt->request_bufflen = 0; - SCpnt->use_sg = 0; - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->underflow = 0; - SCpnt->sc_data_direction = SCSI_DATA_NONE; + SCpnt->request_buffer = NULL; + SCpnt->request_bufflen = 0; + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->underflow = 0; + SCpnt->sc_data_direction = SCSI_DATA_NONE; - scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + /* + * If the SCSI device responded with "logical unit + * is in process of becoming ready", we need to + * retry this command. + */ + } while (SCpnt->eh_state == NEEDS_RETRY); /* * When we eventually call scsi_finish, we really wish to complete @@ -589,7 +602,6 @@ host = SCpnt->host; - retry: /* * We will use a queued command if possible, otherwise we will emulate the * queuing and calling of completion function ourselves. @@ -672,14 +684,13 @@ SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret)); switch (ret) { - case SUCCESS: - SCpnt->eh_state = SUCCESS; - break; - case NEEDS_RETRY: - goto retry; - case FAILED: default: - SCpnt->eh_state = FAILED; + ret = FAILED; + /*FALLTHROUGH*/ + case FAILED: + case NEEDS_RETRY: + case SUCCESS: + SCpnt->eh_state = ret; break; } } else { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi.h linux.21rc1-ac2/drivers/scsi/scsi.h --- linux.21rc1/drivers/scsi/scsi.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi.h 2003-04-25 16:05:08.000000000 +0100 @@ -465,6 +465,7 @@ int sectors); extern struct Scsi_Device_Template *scsi_get_request_dev(struct request *); extern int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt); +extern void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt); extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int); extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors, int block_sectors); @@ -558,6 +559,7 @@ int (*scsi_init_io_fn) (Scsi_Cmnd *); /* Used to initialize new request */ Scsi_Cmnd *device_queue; /* queue of SCSI Command structures */ + struct list_head sdev_free_q; /* list of free cmds */ /* public: */ unsigned int id, lun, channel; @@ -775,6 +777,8 @@ * received on original command * (auto-sense) */ + struct list_head sc_list; /* Inactive cmd list linkage, guarded + * by device_request_lock. */ unsigned flags; /* diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi_ioctl.c linux.21rc1-ac2/drivers/scsi/scsi_ioctl.c --- linux.21rc1/drivers/scsi/scsi_ioctl.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi_ioctl.c 2003-04-22 16:44:37.000000000 +0100 @@ -279,6 +279,7 @@ cmd[1] = (cmd[1] & 0x1f) | (dev->lun << 5); switch (opcode) { + case SEND_DIAGNOSTIC: case FORMAT_UNIT: timeout = FORMAT_UNIT_TIMEOUT; retries = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi_lib.c linux.21rc1-ac2/drivers/scsi/scsi_lib.c --- linux.21rc1/drivers/scsi/scsi_lib.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi_lib.c 2003-04-22 16:44:37.000000000 +0100 @@ -208,6 +208,30 @@ } /* + * Function: scsi_setup_cmd_retry() + * + * Purpose: Restore the command state for a retry + * + * Arguments: SCpnt - command to be restored + * + * Returns: Nothing + * + * Notes: Immediately prior to retrying a command, we need + * to restore certain fields that we saved above. + */ +void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt) +{ + memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->request_buffer = SCpnt->buffer; + SCpnt->request_bufflen = SCpnt->bufflen; + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; +} + +/* * Function: scsi_queue_next_request() * * Purpose: Handle post-processing of completed commands. @@ -722,7 +746,7 @@ printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ", SCpnt->host->host_no, (int) SCpnt->channel, (int) SCpnt->target, (int) SCpnt->lun); - print_command(SCpnt->cmnd); + print_command(SCpnt->data_cmnd); print_sense("sd", SCpnt); SCpnt = scsi_end_request(SCpnt, 0, block_sectors); return; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/scsi_scan.c linux.21rc1-ac2/drivers/scsi/scsi_scan.c --- linux.21rc1/drivers/scsi/scsi_scan.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/scsi_scan.c 2003-04-22 16:44:37.000000000 +0100 @@ -608,6 +608,7 @@ } else { /* assume no peripheral if any other sort of error */ scsi_release_request(SRpnt); + scsi_release_commandblocks(SDpnt); return 0; } } @@ -631,6 +632,7 @@ */ if ((scsi_result[0] >> 5) == 3) { scsi_release_request(SRpnt); + scsi_release_commandblocks(SDpnt); return 0; /* assume no peripheral if any sort of error */ } /* The Toshiba ROM was "gender-changed" here as an inline hack. diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/sd.c linux.21rc1-ac2/drivers/scsi/sd.c --- linux.21rc1/drivers/scsi/sd.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/sd.c 2003-04-22 17:04:13.000000000 +0100 @@ -852,10 +852,13 @@ break; } - /* Look for non-removable devices that return NOT_READY. + /* Look for non-removable devices that return NOT_READY, + * and don't require manual intervention. * Issue command to spin up drive for these cases. */ if (the_result && !rscsi_disks[i].device->removable && - SRpnt->sr_sense_buffer[2] == NOT_READY) { + SRpnt->sr_sense_buffer[2] == NOT_READY && + ! ( SRpnt->sr_sense_buffer[12] == 0x04 && /* ASC */ + SRpnt->sr_sense_buffer[13] == 0x03 )) { /* ASCQ */ unsigned long time1; if (!spintime) { printk("%s: Spinning up disk...", nbuff); @@ -1013,7 +1016,7 @@ } printk("SCSI device %s: " - "%u %d-byte hdwr sectors (%u MB)\n", + "%u %d-byte hdwr sectors (%d MB)\n", nbuff, rscsi_disks[i].capacity, hard_sector, (sz - sz/625 + 974)/1950); } diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/sd.h linux.21rc1-ac2/drivers/scsi/sd.h --- linux.21rc1/drivers/scsi/sd.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/sd.h 2003-04-25 16:05:08.000000000 +0100 @@ -24,13 +24,13 @@ #endif typedef struct scsi_disk { - unsigned capacity; /* size in blocks */ Scsi_Device *device; - unsigned char ready; /* flag ready for FLOPTICAL */ - unsigned char write_prot; /* flag write_protect for rmvable dev */ - unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */ - unsigned char sector_bit_shift; /* power of 2 sectors per FS block */ + unsigned capacity; /* size in blocks */ + unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */ + unsigned char sector_bit_shift; /* power of 2 sectors per FS block */ unsigned has_part_table:1; /* has partition table */ + unsigned ready:1; /* flag ready for FLOPTICAL */ + unsigned write_prot:1; /* flag write_protect for rmvable dev */ } Scsi_Disk; extern int revalidate_scsidisk(kdev_t dev, int maxusage); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/sim710_d.h linux.21rc1-ac2/drivers/scsi/sim710_d.h --- linux.21rc1/drivers/scsi/sim710_d.h 2003-04-22 16:38:48.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/sim710_d.h 2003-04-22 16:44:37.000000000 +0100 @@ -18,15 +18,12 @@ ABSOLUTE reselected_identify = 0 ABSOLUTE msgin_buf = 0 +ABSOLUTE msg_reject = 0 +ABSOLUTE test1_src = 0 +ABSOLUTE test1_dst = 0 -ABSOLUTE int_bad_extmsg1a = 0xab930000 -ABSOLUTE int_bad_extmsg1b = 0xab930001 -ABSOLUTE int_bad_extmsg2a = 0xab930002 -ABSOLUTE int_bad_extmsg2b = 0xab930003 -ABSOLUTE int_bad_extmsg3a = 0xab930004 -ABSOLUTE int_bad_extmsg3b = 0xab930005 ABSOLUTE int_bad_msg1 = 0xab930006 ABSOLUTE int_bad_msg2 = 0xab930007 ABSOLUTE int_bad_msg3 = 0xab930008 @@ -50,7 +47,7 @@ ABSOLUTE int_disc2 = 0xab93001a ABSOLUTE int_disc3 = 0xab93001b ABSOLUTE int_not_rej = 0xab93001c - +ABSOLUTE int_test1 = 0xab93001d @@ -65,6 +62,9 @@ +ABSOLUTE did_reject = 0x01 + + @@ -74,1641 +74,1709 @@ at 0x00000000 : */ 0x60000200,0x00000000, /* - MOVE SCRATCH0 & 0 TO SCRATCH0 - -at 0x00000002 : */ 0x7c340000,0x00000000, -/* ; Enable selection timer MOVE CTEST7 & 0xef TO CTEST7 -at 0x00000004 : */ 0x7c1bef00,0x00000000, +at 0x00000002 : */ 0x7c1bef00,0x00000000, /* SELECT ATN FROM dsa_select, reselect -at 0x00000006 : */ 0x43000000,0x00000c48, +at 0x00000004 : */ 0x43000000,0x00000cd0, /* JUMP get_status, WHEN STATUS -at 0x00000008 : */ 0x830b0000,0x000000a0, +at 0x00000006 : */ 0x830b0000,0x00000098, /* ; Disable selection timer MOVE CTEST7 | 0x10 TO CTEST7 -at 0x0000000a : */ 0x7a1b1000,0x00000000, +at 0x00000008 : */ 0x7a1b1000,0x00000000, /* MOVE SCRATCH0 | had_select TO SCRATCH0 -at 0x0000000c : */ 0x7a340100,0x00000000, +at 0x0000000a : */ 0x7a340100,0x00000000, /* INT int_sel_no_ident, IF NOT MSG_OUT -at 0x0000000e : */ 0x9e020000,0xab930013, +at 0x0000000c : */ 0x9e020000,0xab930013, /* MOVE SCRATCH0 | had_msgout TO SCRATCH0 -at 0x00000010 : */ 0x7a340200,0x00000000, +at 0x0000000e : */ 0x7a340200,0x00000000, /* MOVE FROM dsa_msgout, when MSG_OUT -at 0x00000012 : */ 0x1e000000,0x00000008, +at 0x00000010 : */ 0x1e000000,0x00000008, /* ENTRY done_ident done_ident: JUMP get_status, IF STATUS -at 0x00000014 : */ 0x830a0000,0x000000a0, +at 0x00000012 : */ 0x830a0000,0x00000098, /* redo_msgin1: JUMP get_msgin1, WHEN MSG_IN -at 0x00000016 : */ 0x870b0000,0x00000920, +at 0x00000014 : */ 0x870b0000,0x00000918, /* INT int_sel_not_cmd, IF NOT CMD -at 0x00000018 : */ 0x9a020000,0xab930014, +at 0x00000016 : */ 0x9a020000,0xab930014, /* ENTRY resume_cmd resume_cmd: MOVE SCRATCH0 | had_cmdout TO SCRATCH0 -at 0x0000001a : */ 0x7a340400,0x00000000, +at 0x00000018 : */ 0x7a340400,0x00000000, /* MOVE FROM dsa_cmnd, WHEN CMD -at 0x0000001c : */ 0x1a000000,0x00000010, +at 0x0000001a : */ 0x1a000000,0x00000010, /* ENTRY resume_pmm resume_pmm: redo_msgin2: JUMP get_msgin2, WHEN MSG_IN -at 0x0000001e : */ 0x870b0000,0x00000a20, +at 0x0000001c : */ 0x870b0000,0x00000a48, /* JUMP get_status, IF STATUS -at 0x00000020 : */ 0x830a0000,0x000000a0, +at 0x0000001e : */ 0x830a0000,0x00000098, /* JUMP input_data, IF DATA_IN -at 0x00000022 : */ 0x810a0000,0x000000e0, +at 0x00000020 : */ 0x810a0000,0x000000d8, /* JUMP output_data, IF DATA_OUT -at 0x00000024 : */ 0x800a0000,0x000004f8, +at 0x00000022 : */ 0x800a0000,0x000004f0, /* INT int_cmd_bad_phase -at 0x00000026 : */ 0x98080000,0xab930009, +at 0x00000024 : */ 0x98080000,0xab930009, /* get_status: ; Disable selection timer MOVE CTEST7 | 0x10 TO CTEST7 -at 0x00000028 : */ 0x7a1b1000,0x00000000, +at 0x00000026 : */ 0x7a1b1000,0x00000000, /* MOVE FROM dsa_status, WHEN STATUS -at 0x0000002a : */ 0x1b000000,0x00000018, +at 0x00000028 : */ 0x1b000000,0x00000018, /* INT int_status_not_msgin, WHEN NOT MSG_IN -at 0x0000002c : */ 0x9f030000,0xab930015, +at 0x0000002a : */ 0x9f030000,0xab930015, /* MOVE FROM dsa_msgin, WHEN MSG_IN -at 0x0000002e : */ 0x1f000000,0x00000020, +at 0x0000002c : */ 0x1f000000,0x00000020, /* INT int_not_cmd_complete, IF NOT 0x00 -at 0x00000030 : */ 0x98040000,0xab930012, +at 0x0000002e : */ 0x98040000,0xab930012, /* CLEAR ACK -at 0x00000032 : */ 0x60000040,0x00000000, +at 0x00000030 : */ 0x60000040,0x00000000, /* ENTRY wait_disc_complete wait_disc_complete: WAIT DISCONNECT -at 0x00000034 : */ 0x48000000,0x00000000, +at 0x00000032 : */ 0x48000000,0x00000000, /* INT int_cmd_complete -at 0x00000036 : */ 0x98080000,0xab93000a, +at 0x00000034 : */ 0x98080000,0xab93000a, /* input_data: MOVE SCRATCH0 | had_datain TO SCRATCH0 -at 0x00000038 : */ 0x7a340800,0x00000000, +at 0x00000036 : */ 0x7a340800,0x00000000, /* ENTRY patch_input_data patch_input_data: JUMP 0 -at 0x0000003a : */ 0x80080000,0x00000000, +at 0x00000038 : */ 0x80080000,0x00000000, /* MOVE FROM dsa_datain+0x0000, WHEN DATA_IN -at 0x0000003c : */ 0x19000000,0x00000028, +at 0x0000003a : */ 0x19000000,0x00000028, /* MOVE FROM dsa_datain+0x0008, WHEN DATA_IN -at 0x0000003e : */ 0x19000000,0x00000030, +at 0x0000003c : */ 0x19000000,0x00000030, /* MOVE FROM dsa_datain+0x0010, WHEN DATA_IN -at 0x00000040 : */ 0x19000000,0x00000038, +at 0x0000003e : */ 0x19000000,0x00000038, /* MOVE FROM dsa_datain+0x0018, WHEN DATA_IN -at 0x00000042 : */ 0x19000000,0x00000040, +at 0x00000040 : */ 0x19000000,0x00000040, /* MOVE FROM dsa_datain+0x0020, WHEN DATA_IN -at 0x00000044 : */ 0x19000000,0x00000048, +at 0x00000042 : */ 0x19000000,0x00000048, /* MOVE FROM dsa_datain+0x0028, WHEN DATA_IN -at 0x00000046 : */ 0x19000000,0x00000050, +at 0x00000044 : */ 0x19000000,0x00000050, /* MOVE FROM dsa_datain+0x0030, WHEN DATA_IN -at 0x00000048 : */ 0x19000000,0x00000058, +at 0x00000046 : */ 0x19000000,0x00000058, /* MOVE FROM dsa_datain+0x0038, WHEN DATA_IN -at 0x0000004a : */ 0x19000000,0x00000060, +at 0x00000048 : */ 0x19000000,0x00000060, /* MOVE FROM dsa_datain+0x0040, WHEN DATA_IN -at 0x0000004c : */ 0x19000000,0x00000068, +at 0x0000004a : */ 0x19000000,0x00000068, /* MOVE FROM dsa_datain+0x0048, WHEN DATA_IN -at 0x0000004e : */ 0x19000000,0x00000070, +at 0x0000004c : */ 0x19000000,0x00000070, /* MOVE FROM dsa_datain+0x0050, WHEN DATA_IN -at 0x00000050 : */ 0x19000000,0x00000078, +at 0x0000004e : */ 0x19000000,0x00000078, /* MOVE FROM dsa_datain+0x0058, WHEN DATA_IN -at 0x00000052 : */ 0x19000000,0x00000080, +at 0x00000050 : */ 0x19000000,0x00000080, /* MOVE FROM dsa_datain+0x0060, WHEN DATA_IN -at 0x00000054 : */ 0x19000000,0x00000088, +at 0x00000052 : */ 0x19000000,0x00000088, /* MOVE FROM dsa_datain+0x0068, WHEN DATA_IN -at 0x00000056 : */ 0x19000000,0x00000090, +at 0x00000054 : */ 0x19000000,0x00000090, /* MOVE FROM dsa_datain+0x0070, WHEN DATA_IN -at 0x00000058 : */ 0x19000000,0x00000098, +at 0x00000056 : */ 0x19000000,0x00000098, /* MOVE FROM dsa_datain+0x0078, WHEN DATA_IN -at 0x0000005a : */ 0x19000000,0x000000a0, +at 0x00000058 : */ 0x19000000,0x000000a0, /* MOVE FROM dsa_datain+0x0080, WHEN DATA_IN -at 0x0000005c : */ 0x19000000,0x000000a8, +at 0x0000005a : */ 0x19000000,0x000000a8, /* MOVE FROM dsa_datain+0x0088, WHEN DATA_IN -at 0x0000005e : */ 0x19000000,0x000000b0, +at 0x0000005c : */ 0x19000000,0x000000b0, /* MOVE FROM dsa_datain+0x0090, WHEN DATA_IN -at 0x00000060 : */ 0x19000000,0x000000b8, +at 0x0000005e : */ 0x19000000,0x000000b8, /* MOVE FROM dsa_datain+0x0098, WHEN DATA_IN -at 0x00000062 : */ 0x19000000,0x000000c0, +at 0x00000060 : */ 0x19000000,0x000000c0, /* MOVE FROM dsa_datain+0x00a0, WHEN DATA_IN -at 0x00000064 : */ 0x19000000,0x000000c8, +at 0x00000062 : */ 0x19000000,0x000000c8, /* MOVE FROM dsa_datain+0x00a8, WHEN DATA_IN -at 0x00000066 : */ 0x19000000,0x000000d0, +at 0x00000064 : */ 0x19000000,0x000000d0, /* MOVE FROM dsa_datain+0x00b0, WHEN DATA_IN -at 0x00000068 : */ 0x19000000,0x000000d8, +at 0x00000066 : */ 0x19000000,0x000000d8, /* MOVE FROM dsa_datain+0x00b8, WHEN DATA_IN -at 0x0000006a : */ 0x19000000,0x000000e0, +at 0x00000068 : */ 0x19000000,0x000000e0, /* MOVE FROM dsa_datain+0x00c0, WHEN DATA_IN -at 0x0000006c : */ 0x19000000,0x000000e8, +at 0x0000006a : */ 0x19000000,0x000000e8, /* MOVE FROM dsa_datain+0x00c8, WHEN DATA_IN -at 0x0000006e : */ 0x19000000,0x000000f0, +at 0x0000006c : */ 0x19000000,0x000000f0, /* MOVE FROM dsa_datain+0x00d0, WHEN DATA_IN -at 0x00000070 : */ 0x19000000,0x000000f8, +at 0x0000006e : */ 0x19000000,0x000000f8, /* MOVE FROM dsa_datain+0x00d8, WHEN DATA_IN -at 0x00000072 : */ 0x19000000,0x00000100, +at 0x00000070 : */ 0x19000000,0x00000100, /* MOVE FROM dsa_datain+0x00e0, WHEN DATA_IN -at 0x00000074 : */ 0x19000000,0x00000108, +at 0x00000072 : */ 0x19000000,0x00000108, /* MOVE FROM dsa_datain+0x00e8, WHEN DATA_IN -at 0x00000076 : */ 0x19000000,0x00000110, +at 0x00000074 : */ 0x19000000,0x00000110, /* MOVE FROM dsa_datain+0x00f0, WHEN DATA_IN -at 0x00000078 : */ 0x19000000,0x00000118, +at 0x00000076 : */ 0x19000000,0x00000118, /* MOVE FROM dsa_datain+0x00f8, WHEN DATA_IN -at 0x0000007a : */ 0x19000000,0x00000120, +at 0x00000078 : */ 0x19000000,0x00000120, /* MOVE FROM dsa_datain+0x0100, WHEN DATA_IN -at 0x0000007c : */ 0x19000000,0x00000128, +at 0x0000007a : */ 0x19000000,0x00000128, /* MOVE FROM dsa_datain+0x0108, WHEN DATA_IN -at 0x0000007e : */ 0x19000000,0x00000130, +at 0x0000007c : */ 0x19000000,0x00000130, /* MOVE FROM dsa_datain+0x0110, WHEN DATA_IN -at 0x00000080 : */ 0x19000000,0x00000138, +at 0x0000007e : */ 0x19000000,0x00000138, /* MOVE FROM dsa_datain+0x0118, WHEN DATA_IN -at 0x00000082 : */ 0x19000000,0x00000140, +at 0x00000080 : */ 0x19000000,0x00000140, /* MOVE FROM dsa_datain+0x0120, WHEN DATA_IN -at 0x00000084 : */ 0x19000000,0x00000148, +at 0x00000082 : */ 0x19000000,0x00000148, /* MOVE FROM dsa_datain+0x0128, WHEN DATA_IN -at 0x00000086 : */ 0x19000000,0x00000150, +at 0x00000084 : */ 0x19000000,0x00000150, /* MOVE FROM dsa_datain+0x0130, WHEN DATA_IN -at 0x00000088 : */ 0x19000000,0x00000158, +at 0x00000086 : */ 0x19000000,0x00000158, /* MOVE FROM dsa_datain+0x0138, WHEN DATA_IN -at 0x0000008a : */ 0x19000000,0x00000160, +at 0x00000088 : */ 0x19000000,0x00000160, /* MOVE FROM dsa_datain+0x0140, WHEN DATA_IN -at 0x0000008c : */ 0x19000000,0x00000168, +at 0x0000008a : */ 0x19000000,0x00000168, /* MOVE FROM dsa_datain+0x0148, WHEN DATA_IN -at 0x0000008e : */ 0x19000000,0x00000170, +at 0x0000008c : */ 0x19000000,0x00000170, /* MOVE FROM dsa_datain+0x0150, WHEN DATA_IN -at 0x00000090 : */ 0x19000000,0x00000178, +at 0x0000008e : */ 0x19000000,0x00000178, /* MOVE FROM dsa_datain+0x0158, WHEN DATA_IN -at 0x00000092 : */ 0x19000000,0x00000180, +at 0x00000090 : */ 0x19000000,0x00000180, /* MOVE FROM dsa_datain+0x0160, WHEN DATA_IN -at 0x00000094 : */ 0x19000000,0x00000188, +at 0x00000092 : */ 0x19000000,0x00000188, /* MOVE FROM dsa_datain+0x0168, WHEN DATA_IN -at 0x00000096 : */ 0x19000000,0x00000190, +at 0x00000094 : */ 0x19000000,0x00000190, /* MOVE FROM dsa_datain+0x0170, WHEN DATA_IN -at 0x00000098 : */ 0x19000000,0x00000198, +at 0x00000096 : */ 0x19000000,0x00000198, /* MOVE FROM dsa_datain+0x0178, WHEN DATA_IN -at 0x0000009a : */ 0x19000000,0x000001a0, +at 0x00000098 : */ 0x19000000,0x000001a0, /* MOVE FROM dsa_datain+0x0180, WHEN DATA_IN -at 0x0000009c : */ 0x19000000,0x000001a8, +at 0x0000009a : */ 0x19000000,0x000001a8, /* MOVE FROM dsa_datain+0x0188, WHEN DATA_IN -at 0x0000009e : */ 0x19000000,0x000001b0, +at 0x0000009c : */ 0x19000000,0x000001b0, /* MOVE FROM dsa_datain+0x0190, WHEN DATA_IN -at 0x000000a0 : */ 0x19000000,0x000001b8, +at 0x0000009e : */ 0x19000000,0x000001b8, /* MOVE FROM dsa_datain+0x0198, WHEN DATA_IN -at 0x000000a2 : */ 0x19000000,0x000001c0, +at 0x000000a0 : */ 0x19000000,0x000001c0, /* MOVE FROM dsa_datain+0x01a0, WHEN DATA_IN -at 0x000000a4 : */ 0x19000000,0x000001c8, +at 0x000000a2 : */ 0x19000000,0x000001c8, /* MOVE FROM dsa_datain+0x01a8, WHEN DATA_IN -at 0x000000a6 : */ 0x19000000,0x000001d0, +at 0x000000a4 : */ 0x19000000,0x000001d0, /* MOVE FROM dsa_datain+0x01b0, WHEN DATA_IN -at 0x000000a8 : */ 0x19000000,0x000001d8, +at 0x000000a6 : */ 0x19000000,0x000001d8, /* MOVE FROM dsa_datain+0x01b8, WHEN DATA_IN -at 0x000000aa : */ 0x19000000,0x000001e0, +at 0x000000a8 : */ 0x19000000,0x000001e0, /* MOVE FROM dsa_datain+0x01c0, WHEN DATA_IN -at 0x000000ac : */ 0x19000000,0x000001e8, +at 0x000000aa : */ 0x19000000,0x000001e8, /* MOVE FROM dsa_datain+0x01c8, WHEN DATA_IN -at 0x000000ae : */ 0x19000000,0x000001f0, +at 0x000000ac : */ 0x19000000,0x000001f0, /* MOVE FROM dsa_datain+0x01d0, WHEN DATA_IN -at 0x000000b0 : */ 0x19000000,0x000001f8, +at 0x000000ae : */ 0x19000000,0x000001f8, /* MOVE FROM dsa_datain+0x01d8, WHEN DATA_IN -at 0x000000b2 : */ 0x19000000,0x00000200, +at 0x000000b0 : */ 0x19000000,0x00000200, /* MOVE FROM dsa_datain+0x01e0, WHEN DATA_IN -at 0x000000b4 : */ 0x19000000,0x00000208, +at 0x000000b2 : */ 0x19000000,0x00000208, /* MOVE FROM dsa_datain+0x01e8, WHEN DATA_IN -at 0x000000b6 : */ 0x19000000,0x00000210, +at 0x000000b4 : */ 0x19000000,0x00000210, /* MOVE FROM dsa_datain+0x01f0, WHEN DATA_IN -at 0x000000b8 : */ 0x19000000,0x00000218, +at 0x000000b6 : */ 0x19000000,0x00000218, /* MOVE FROM dsa_datain+0x01f8, WHEN DATA_IN -at 0x000000ba : */ 0x19000000,0x00000220, +at 0x000000b8 : */ 0x19000000,0x00000220, /* MOVE FROM dsa_datain+0x0200, WHEN DATA_IN -at 0x000000bc : */ 0x19000000,0x00000228, +at 0x000000ba : */ 0x19000000,0x00000228, /* MOVE FROM dsa_datain+0x0208, WHEN DATA_IN -at 0x000000be : */ 0x19000000,0x00000230, +at 0x000000bc : */ 0x19000000,0x00000230, /* MOVE FROM dsa_datain+0x0210, WHEN DATA_IN -at 0x000000c0 : */ 0x19000000,0x00000238, +at 0x000000be : */ 0x19000000,0x00000238, /* MOVE FROM dsa_datain+0x0218, WHEN DATA_IN -at 0x000000c2 : */ 0x19000000,0x00000240, +at 0x000000c0 : */ 0x19000000,0x00000240, /* MOVE FROM dsa_datain+0x0220, WHEN DATA_IN -at 0x000000c4 : */ 0x19000000,0x00000248, +at 0x000000c2 : */ 0x19000000,0x00000248, /* MOVE FROM dsa_datain+0x0228, WHEN DATA_IN -at 0x000000c6 : */ 0x19000000,0x00000250, +at 0x000000c4 : */ 0x19000000,0x00000250, /* MOVE FROM dsa_datain+0x0230, WHEN DATA_IN -at 0x000000c8 : */ 0x19000000,0x00000258, +at 0x000000c6 : */ 0x19000000,0x00000258, /* MOVE FROM dsa_datain+0x0238, WHEN DATA_IN -at 0x000000ca : */ 0x19000000,0x00000260, +at 0x000000c8 : */ 0x19000000,0x00000260, /* MOVE FROM dsa_datain+0x0240, WHEN DATA_IN -at 0x000000cc : */ 0x19000000,0x00000268, +at 0x000000ca : */ 0x19000000,0x00000268, /* MOVE FROM dsa_datain+0x0248, WHEN DATA_IN -at 0x000000ce : */ 0x19000000,0x00000270, +at 0x000000cc : */ 0x19000000,0x00000270, /* MOVE FROM dsa_datain+0x0250, WHEN DATA_IN -at 0x000000d0 : */ 0x19000000,0x00000278, +at 0x000000ce : */ 0x19000000,0x00000278, /* MOVE FROM dsa_datain+0x0258, WHEN DATA_IN -at 0x000000d2 : */ 0x19000000,0x00000280, +at 0x000000d0 : */ 0x19000000,0x00000280, /* MOVE FROM dsa_datain+0x0260, WHEN DATA_IN -at 0x000000d4 : */ 0x19000000,0x00000288, +at 0x000000d2 : */ 0x19000000,0x00000288, /* MOVE FROM dsa_datain+0x0268, WHEN DATA_IN -at 0x000000d6 : */ 0x19000000,0x00000290, +at 0x000000d4 : */ 0x19000000,0x00000290, /* MOVE FROM dsa_datain+0x0270, WHEN DATA_IN -at 0x000000d8 : */ 0x19000000,0x00000298, +at 0x000000d6 : */ 0x19000000,0x00000298, /* MOVE FROM dsa_datain+0x0278, WHEN DATA_IN -at 0x000000da : */ 0x19000000,0x000002a0, +at 0x000000d8 : */ 0x19000000,0x000002a0, /* MOVE FROM dsa_datain+0x0280, WHEN DATA_IN -at 0x000000dc : */ 0x19000000,0x000002a8, +at 0x000000da : */ 0x19000000,0x000002a8, /* MOVE FROM dsa_datain+0x0288, WHEN DATA_IN -at 0x000000de : */ 0x19000000,0x000002b0, +at 0x000000dc : */ 0x19000000,0x000002b0, /* MOVE FROM dsa_datain+0x0290, WHEN DATA_IN -at 0x000000e0 : */ 0x19000000,0x000002b8, +at 0x000000de : */ 0x19000000,0x000002b8, /* MOVE FROM dsa_datain+0x0298, WHEN DATA_IN -at 0x000000e2 : */ 0x19000000,0x000002c0, +at 0x000000e0 : */ 0x19000000,0x000002c0, /* MOVE FROM dsa_datain+0x02a0, WHEN DATA_IN -at 0x000000e4 : */ 0x19000000,0x000002c8, +at 0x000000e2 : */ 0x19000000,0x000002c8, /* MOVE FROM dsa_datain+0x02a8, WHEN DATA_IN -at 0x000000e6 : */ 0x19000000,0x000002d0, +at 0x000000e4 : */ 0x19000000,0x000002d0, /* MOVE FROM dsa_datain+0x02b0, WHEN DATA_IN -at 0x000000e8 : */ 0x19000000,0x000002d8, +at 0x000000e6 : */ 0x19000000,0x000002d8, /* MOVE FROM dsa_datain+0x02b8, WHEN DATA_IN -at 0x000000ea : */ 0x19000000,0x000002e0, +at 0x000000e8 : */ 0x19000000,0x000002e0, /* MOVE FROM dsa_datain+0x02c0, WHEN DATA_IN -at 0x000000ec : */ 0x19000000,0x000002e8, +at 0x000000ea : */ 0x19000000,0x000002e8, /* MOVE FROM dsa_datain+0x02c8, WHEN DATA_IN -at 0x000000ee : */ 0x19000000,0x000002f0, +at 0x000000ec : */ 0x19000000,0x000002f0, /* MOVE FROM dsa_datain+0x02d0, WHEN DATA_IN -at 0x000000f0 : */ 0x19000000,0x000002f8, +at 0x000000ee : */ 0x19000000,0x000002f8, /* MOVE FROM dsa_datain+0x02d8, WHEN DATA_IN -at 0x000000f2 : */ 0x19000000,0x00000300, +at 0x000000f0 : */ 0x19000000,0x00000300, /* MOVE FROM dsa_datain+0x02e0, WHEN DATA_IN -at 0x000000f4 : */ 0x19000000,0x00000308, +at 0x000000f2 : */ 0x19000000,0x00000308, /* MOVE FROM dsa_datain+0x02e8, WHEN DATA_IN -at 0x000000f6 : */ 0x19000000,0x00000310, +at 0x000000f4 : */ 0x19000000,0x00000310, /* MOVE FROM dsa_datain+0x02f0, WHEN DATA_IN -at 0x000000f8 : */ 0x19000000,0x00000318, +at 0x000000f6 : */ 0x19000000,0x00000318, /* MOVE FROM dsa_datain+0x02f8, WHEN DATA_IN -at 0x000000fa : */ 0x19000000,0x00000320, +at 0x000000f8 : */ 0x19000000,0x00000320, /* MOVE FROM dsa_datain+0x0300, WHEN DATA_IN -at 0x000000fc : */ 0x19000000,0x00000328, +at 0x000000fa : */ 0x19000000,0x00000328, /* MOVE FROM dsa_datain+0x0308, WHEN DATA_IN -at 0x000000fe : */ 0x19000000,0x00000330, +at 0x000000fc : */ 0x19000000,0x00000330, /* MOVE FROM dsa_datain+0x0310, WHEN DATA_IN -at 0x00000100 : */ 0x19000000,0x00000338, +at 0x000000fe : */ 0x19000000,0x00000338, /* MOVE FROM dsa_datain+0x0318, WHEN DATA_IN -at 0x00000102 : */ 0x19000000,0x00000340, +at 0x00000100 : */ 0x19000000,0x00000340, /* MOVE FROM dsa_datain+0x0320, WHEN DATA_IN -at 0x00000104 : */ 0x19000000,0x00000348, +at 0x00000102 : */ 0x19000000,0x00000348, /* MOVE FROM dsa_datain+0x0328, WHEN DATA_IN -at 0x00000106 : */ 0x19000000,0x00000350, +at 0x00000104 : */ 0x19000000,0x00000350, /* MOVE FROM dsa_datain+0x0330, WHEN DATA_IN -at 0x00000108 : */ 0x19000000,0x00000358, +at 0x00000106 : */ 0x19000000,0x00000358, /* MOVE FROM dsa_datain+0x0338, WHEN DATA_IN -at 0x0000010a : */ 0x19000000,0x00000360, +at 0x00000108 : */ 0x19000000,0x00000360, /* MOVE FROM dsa_datain+0x0340, WHEN DATA_IN -at 0x0000010c : */ 0x19000000,0x00000368, +at 0x0000010a : */ 0x19000000,0x00000368, /* MOVE FROM dsa_datain+0x0348, WHEN DATA_IN -at 0x0000010e : */ 0x19000000,0x00000370, +at 0x0000010c : */ 0x19000000,0x00000370, /* MOVE FROM dsa_datain+0x0350, WHEN DATA_IN -at 0x00000110 : */ 0x19000000,0x00000378, +at 0x0000010e : */ 0x19000000,0x00000378, /* MOVE FROM dsa_datain+0x0358, WHEN DATA_IN -at 0x00000112 : */ 0x19000000,0x00000380, +at 0x00000110 : */ 0x19000000,0x00000380, /* MOVE FROM dsa_datain+0x0360, WHEN DATA_IN -at 0x00000114 : */ 0x19000000,0x00000388, +at 0x00000112 : */ 0x19000000,0x00000388, /* MOVE FROM dsa_datain+0x0368, WHEN DATA_IN -at 0x00000116 : */ 0x19000000,0x00000390, +at 0x00000114 : */ 0x19000000,0x00000390, /* MOVE FROM dsa_datain+0x0370, WHEN DATA_IN -at 0x00000118 : */ 0x19000000,0x00000398, +at 0x00000116 : */ 0x19000000,0x00000398, /* MOVE FROM dsa_datain+0x0378, WHEN DATA_IN -at 0x0000011a : */ 0x19000000,0x000003a0, +at 0x00000118 : */ 0x19000000,0x000003a0, /* MOVE FROM dsa_datain+0x0380, WHEN DATA_IN -at 0x0000011c : */ 0x19000000,0x000003a8, +at 0x0000011a : */ 0x19000000,0x000003a8, /* MOVE FROM dsa_datain+0x0388, WHEN DATA_IN -at 0x0000011e : */ 0x19000000,0x000003b0, +at 0x0000011c : */ 0x19000000,0x000003b0, /* MOVE FROM dsa_datain+0x0390, WHEN DATA_IN -at 0x00000120 : */ 0x19000000,0x000003b8, +at 0x0000011e : */ 0x19000000,0x000003b8, /* MOVE FROM dsa_datain+0x0398, WHEN DATA_IN -at 0x00000122 : */ 0x19000000,0x000003c0, +at 0x00000120 : */ 0x19000000,0x000003c0, /* MOVE FROM dsa_datain+0x03a0, WHEN DATA_IN -at 0x00000124 : */ 0x19000000,0x000003c8, +at 0x00000122 : */ 0x19000000,0x000003c8, /* MOVE FROM dsa_datain+0x03a8, WHEN DATA_IN -at 0x00000126 : */ 0x19000000,0x000003d0, +at 0x00000124 : */ 0x19000000,0x000003d0, /* MOVE FROM dsa_datain+0x03b0, WHEN DATA_IN -at 0x00000128 : */ 0x19000000,0x000003d8, +at 0x00000126 : */ 0x19000000,0x000003d8, /* MOVE FROM dsa_datain+0x03b8, WHEN DATA_IN -at 0x0000012a : */ 0x19000000,0x000003e0, +at 0x00000128 : */ 0x19000000,0x000003e0, /* MOVE FROM dsa_datain+0x03c0, WHEN DATA_IN -at 0x0000012c : */ 0x19000000,0x000003e8, +at 0x0000012a : */ 0x19000000,0x000003e8, /* MOVE FROM dsa_datain+0x03c8, WHEN DATA_IN -at 0x0000012e : */ 0x19000000,0x000003f0, +at 0x0000012c : */ 0x19000000,0x000003f0, /* MOVE FROM dsa_datain+0x03d0, WHEN DATA_IN -at 0x00000130 : */ 0x19000000,0x000003f8, +at 0x0000012e : */ 0x19000000,0x000003f8, /* MOVE FROM dsa_datain+0x03d8, WHEN DATA_IN -at 0x00000132 : */ 0x19000000,0x00000400, +at 0x00000130 : */ 0x19000000,0x00000400, /* MOVE FROM dsa_datain+0x03e0, WHEN DATA_IN -at 0x00000134 : */ 0x19000000,0x00000408, +at 0x00000132 : */ 0x19000000,0x00000408, /* MOVE FROM dsa_datain+0x03e8, WHEN DATA_IN -at 0x00000136 : */ 0x19000000,0x00000410, +at 0x00000134 : */ 0x19000000,0x00000410, /* MOVE FROM dsa_datain+0x03f0, WHEN DATA_IN -at 0x00000138 : */ 0x19000000,0x00000418, +at 0x00000136 : */ 0x19000000,0x00000418, /* MOVE FROM dsa_datain+0x03f8, WHEN DATA_IN -at 0x0000013a : */ 0x19000000,0x00000420, +at 0x00000138 : */ 0x19000000,0x00000420, /* JUMP end_data_trans -at 0x0000013c : */ 0x80080000,0x00000908, +at 0x0000013a : */ 0x80080000,0x00000900, /* output_data: MOVE SCRATCH0 | had_dataout TO SCRATCH0 -at 0x0000013e : */ 0x7a341000,0x00000000, +at 0x0000013c : */ 0x7a341000,0x00000000, /* ENTRY patch_output_data patch_output_data: JUMP 0 -at 0x00000140 : */ 0x80080000,0x00000000, +at 0x0000013e : */ 0x80080000,0x00000000, /* MOVE FROM dsa_dataout+0x0000, WHEN DATA_OUT -at 0x00000142 : */ 0x18000000,0x00000428, +at 0x00000140 : */ 0x18000000,0x00000428, /* MOVE FROM dsa_dataout+0x0008, WHEN DATA_OUT -at 0x00000144 : */ 0x18000000,0x00000430, +at 0x00000142 : */ 0x18000000,0x00000430, /* MOVE FROM dsa_dataout+0x0010, WHEN DATA_OUT -at 0x00000146 : */ 0x18000000,0x00000438, +at 0x00000144 : */ 0x18000000,0x00000438, /* MOVE FROM dsa_dataout+0x0018, WHEN DATA_OUT -at 0x00000148 : */ 0x18000000,0x00000440, +at 0x00000146 : */ 0x18000000,0x00000440, /* MOVE FROM dsa_dataout+0x0020, WHEN DATA_OUT -at 0x0000014a : */ 0x18000000,0x00000448, +at 0x00000148 : */ 0x18000000,0x00000448, /* MOVE FROM dsa_dataout+0x0028, WHEN DATA_OUT -at 0x0000014c : */ 0x18000000,0x00000450, +at 0x0000014a : */ 0x18000000,0x00000450, /* MOVE FROM dsa_dataout+0x0030, WHEN DATA_OUT -at 0x0000014e : */ 0x18000000,0x00000458, +at 0x0000014c : */ 0x18000000,0x00000458, /* MOVE FROM dsa_dataout+0x0038, WHEN DATA_OUT -at 0x00000150 : */ 0x18000000,0x00000460, +at 0x0000014e : */ 0x18000000,0x00000460, /* MOVE FROM dsa_dataout+0x0040, WHEN DATA_OUT -at 0x00000152 : */ 0x18000000,0x00000468, +at 0x00000150 : */ 0x18000000,0x00000468, /* MOVE FROM dsa_dataout+0x0048, WHEN DATA_OUT -at 0x00000154 : */ 0x18000000,0x00000470, +at 0x00000152 : */ 0x18000000,0x00000470, /* MOVE FROM dsa_dataout+0x0050, WHEN DATA_OUT -at 0x00000156 : */ 0x18000000,0x00000478, +at 0x00000154 : */ 0x18000000,0x00000478, /* MOVE FROM dsa_dataout+0x0058, WHEN DATA_OUT -at 0x00000158 : */ 0x18000000,0x00000480, +at 0x00000156 : */ 0x18000000,0x00000480, /* MOVE FROM dsa_dataout+0x0060, WHEN DATA_OUT -at 0x0000015a : */ 0x18000000,0x00000488, +at 0x00000158 : */ 0x18000000,0x00000488, /* MOVE FROM dsa_dataout+0x0068, WHEN DATA_OUT -at 0x0000015c : */ 0x18000000,0x00000490, +at 0x0000015a : */ 0x18000000,0x00000490, /* MOVE FROM dsa_dataout+0x0070, WHEN DATA_OUT -at 0x0000015e : */ 0x18000000,0x00000498, +at 0x0000015c : */ 0x18000000,0x00000498, /* MOVE FROM dsa_dataout+0x0078, WHEN DATA_OUT -at 0x00000160 : */ 0x18000000,0x000004a0, +at 0x0000015e : */ 0x18000000,0x000004a0, /* MOVE FROM dsa_dataout+0x0080, WHEN DATA_OUT -at 0x00000162 : */ 0x18000000,0x000004a8, +at 0x00000160 : */ 0x18000000,0x000004a8, /* MOVE FROM dsa_dataout+0x0088, WHEN DATA_OUT -at 0x00000164 : */ 0x18000000,0x000004b0, +at 0x00000162 : */ 0x18000000,0x000004b0, /* MOVE FROM dsa_dataout+0x0090, WHEN DATA_OUT -at 0x00000166 : */ 0x18000000,0x000004b8, +at 0x00000164 : */ 0x18000000,0x000004b8, /* MOVE FROM dsa_dataout+0x0098, WHEN DATA_OUT -at 0x00000168 : */ 0x18000000,0x000004c0, +at 0x00000166 : */ 0x18000000,0x000004c0, /* MOVE FROM dsa_dataout+0x00a0, WHEN DATA_OUT -at 0x0000016a : */ 0x18000000,0x000004c8, +at 0x00000168 : */ 0x18000000,0x000004c8, /* MOVE FROM dsa_dataout+0x00a8, WHEN DATA_OUT -at 0x0000016c : */ 0x18000000,0x000004d0, +at 0x0000016a : */ 0x18000000,0x000004d0, /* MOVE FROM dsa_dataout+0x00b0, WHEN DATA_OUT -at 0x0000016e : */ 0x18000000,0x000004d8, +at 0x0000016c : */ 0x18000000,0x000004d8, /* MOVE FROM dsa_dataout+0x00b8, WHEN DATA_OUT -at 0x00000170 : */ 0x18000000,0x000004e0, +at 0x0000016e : */ 0x18000000,0x000004e0, /* MOVE FROM dsa_dataout+0x00c0, WHEN DATA_OUT -at 0x00000172 : */ 0x18000000,0x000004e8, +at 0x00000170 : */ 0x18000000,0x000004e8, /* MOVE FROM dsa_dataout+0x00c8, WHEN DATA_OUT -at 0x00000174 : */ 0x18000000,0x000004f0, +at 0x00000172 : */ 0x18000000,0x000004f0, /* MOVE FROM dsa_dataout+0x00d0, WHEN DATA_OUT -at 0x00000176 : */ 0x18000000,0x000004f8, +at 0x00000174 : */ 0x18000000,0x000004f8, /* MOVE FROM dsa_dataout+0x00d8, WHEN DATA_OUT -at 0x00000178 : */ 0x18000000,0x00000500, +at 0x00000176 : */ 0x18000000,0x00000500, /* MOVE FROM dsa_dataout+0x00e0, WHEN DATA_OUT -at 0x0000017a : */ 0x18000000,0x00000508, +at 0x00000178 : */ 0x18000000,0x00000508, /* MOVE FROM dsa_dataout+0x00e8, WHEN DATA_OUT -at 0x0000017c : */ 0x18000000,0x00000510, +at 0x0000017a : */ 0x18000000,0x00000510, /* MOVE FROM dsa_dataout+0x00f0, WHEN DATA_OUT -at 0x0000017e : */ 0x18000000,0x00000518, +at 0x0000017c : */ 0x18000000,0x00000518, /* MOVE FROM dsa_dataout+0x00f8, WHEN DATA_OUT -at 0x00000180 : */ 0x18000000,0x00000520, +at 0x0000017e : */ 0x18000000,0x00000520, /* MOVE FROM dsa_dataout+0x0100, WHEN DATA_OUT -at 0x00000182 : */ 0x18000000,0x00000528, +at 0x00000180 : */ 0x18000000,0x00000528, /* MOVE FROM dsa_dataout+0x0108, WHEN DATA_OUT -at 0x00000184 : */ 0x18000000,0x00000530, +at 0x00000182 : */ 0x18000000,0x00000530, /* MOVE FROM dsa_dataout+0x0110, WHEN DATA_OUT -at 0x00000186 : */ 0x18000000,0x00000538, +at 0x00000184 : */ 0x18000000,0x00000538, /* MOVE FROM dsa_dataout+0x0118, WHEN DATA_OUT -at 0x00000188 : */ 0x18000000,0x00000540, +at 0x00000186 : */ 0x18000000,0x00000540, /* MOVE FROM dsa_dataout+0x0120, WHEN DATA_OUT -at 0x0000018a : */ 0x18000000,0x00000548, +at 0x00000188 : */ 0x18000000,0x00000548, /* MOVE FROM dsa_dataout+0x0128, WHEN DATA_OUT -at 0x0000018c : */ 0x18000000,0x00000550, +at 0x0000018a : */ 0x18000000,0x00000550, /* MOVE FROM dsa_dataout+0x0130, WHEN DATA_OUT -at 0x0000018e : */ 0x18000000,0x00000558, +at 0x0000018c : */ 0x18000000,0x00000558, /* MOVE FROM dsa_dataout+0x0138, WHEN DATA_OUT -at 0x00000190 : */ 0x18000000,0x00000560, +at 0x0000018e : */ 0x18000000,0x00000560, /* MOVE FROM dsa_dataout+0x0140, WHEN DATA_OUT -at 0x00000192 : */ 0x18000000,0x00000568, +at 0x00000190 : */ 0x18000000,0x00000568, /* MOVE FROM dsa_dataout+0x0148, WHEN DATA_OUT -at 0x00000194 : */ 0x18000000,0x00000570, +at 0x00000192 : */ 0x18000000,0x00000570, /* MOVE FROM dsa_dataout+0x0150, WHEN DATA_OUT -at 0x00000196 : */ 0x18000000,0x00000578, +at 0x00000194 : */ 0x18000000,0x00000578, /* MOVE FROM dsa_dataout+0x0158, WHEN DATA_OUT -at 0x00000198 : */ 0x18000000,0x00000580, +at 0x00000196 : */ 0x18000000,0x00000580, /* MOVE FROM dsa_dataout+0x0160, WHEN DATA_OUT -at 0x0000019a : */ 0x18000000,0x00000588, +at 0x00000198 : */ 0x18000000,0x00000588, /* MOVE FROM dsa_dataout+0x0168, WHEN DATA_OUT -at 0x0000019c : */ 0x18000000,0x00000590, +at 0x0000019a : */ 0x18000000,0x00000590, /* MOVE FROM dsa_dataout+0x0170, WHEN DATA_OUT -at 0x0000019e : */ 0x18000000,0x00000598, +at 0x0000019c : */ 0x18000000,0x00000598, /* MOVE FROM dsa_dataout+0x0178, WHEN DATA_OUT -at 0x000001a0 : */ 0x18000000,0x000005a0, +at 0x0000019e : */ 0x18000000,0x000005a0, /* MOVE FROM dsa_dataout+0x0180, WHEN DATA_OUT -at 0x000001a2 : */ 0x18000000,0x000005a8, +at 0x000001a0 : */ 0x18000000,0x000005a8, /* MOVE FROM dsa_dataout+0x0188, WHEN DATA_OUT -at 0x000001a4 : */ 0x18000000,0x000005b0, +at 0x000001a2 : */ 0x18000000,0x000005b0, /* MOVE FROM dsa_dataout+0x0190, WHEN DATA_OUT -at 0x000001a6 : */ 0x18000000,0x000005b8, +at 0x000001a4 : */ 0x18000000,0x000005b8, /* MOVE FROM dsa_dataout+0x0198, WHEN DATA_OUT -at 0x000001a8 : */ 0x18000000,0x000005c0, +at 0x000001a6 : */ 0x18000000,0x000005c0, /* MOVE FROM dsa_dataout+0x01a0, WHEN DATA_OUT -at 0x000001aa : */ 0x18000000,0x000005c8, +at 0x000001a8 : */ 0x18000000,0x000005c8, /* MOVE FROM dsa_dataout+0x01a8, WHEN DATA_OUT -at 0x000001ac : */ 0x18000000,0x000005d0, +at 0x000001aa : */ 0x18000000,0x000005d0, /* MOVE FROM dsa_dataout+0x01b0, WHEN DATA_OUT -at 0x000001ae : */ 0x18000000,0x000005d8, +at 0x000001ac : */ 0x18000000,0x000005d8, /* MOVE FROM dsa_dataout+0x01b8, WHEN DATA_OUT -at 0x000001b0 : */ 0x18000000,0x000005e0, +at 0x000001ae : */ 0x18000000,0x000005e0, /* MOVE FROM dsa_dataout+0x01c0, WHEN DATA_OUT -at 0x000001b2 : */ 0x18000000,0x000005e8, +at 0x000001b0 : */ 0x18000000,0x000005e8, /* MOVE FROM dsa_dataout+0x01c8, WHEN DATA_OUT -at 0x000001b4 : */ 0x18000000,0x000005f0, +at 0x000001b2 : */ 0x18000000,0x000005f0, /* MOVE FROM dsa_dataout+0x01d0, WHEN DATA_OUT -at 0x000001b6 : */ 0x18000000,0x000005f8, +at 0x000001b4 : */ 0x18000000,0x000005f8, /* MOVE FROM dsa_dataout+0x01d8, WHEN DATA_OUT -at 0x000001b8 : */ 0x18000000,0x00000600, +at 0x000001b6 : */ 0x18000000,0x00000600, /* MOVE FROM dsa_dataout+0x01e0, WHEN DATA_OUT -at 0x000001ba : */ 0x18000000,0x00000608, +at 0x000001b8 : */ 0x18000000,0x00000608, /* MOVE FROM dsa_dataout+0x01e8, WHEN DATA_OUT -at 0x000001bc : */ 0x18000000,0x00000610, +at 0x000001ba : */ 0x18000000,0x00000610, /* MOVE FROM dsa_dataout+0x01f0, WHEN DATA_OUT -at 0x000001be : */ 0x18000000,0x00000618, +at 0x000001bc : */ 0x18000000,0x00000618, /* MOVE FROM dsa_dataout+0x01f8, WHEN DATA_OUT -at 0x000001c0 : */ 0x18000000,0x00000620, +at 0x000001be : */ 0x18000000,0x00000620, /* MOVE FROM dsa_dataout+0x0200, WHEN DATA_OUT -at 0x000001c2 : */ 0x18000000,0x00000628, +at 0x000001c0 : */ 0x18000000,0x00000628, /* MOVE FROM dsa_dataout+0x0208, WHEN DATA_OUT -at 0x000001c4 : */ 0x18000000,0x00000630, +at 0x000001c2 : */ 0x18000000,0x00000630, /* MOVE FROM dsa_dataout+0x0210, WHEN DATA_OUT -at 0x000001c6 : */ 0x18000000,0x00000638, +at 0x000001c4 : */ 0x18000000,0x00000638, /* MOVE FROM dsa_dataout+0x0218, WHEN DATA_OUT -at 0x000001c8 : */ 0x18000000,0x00000640, +at 0x000001c6 : */ 0x18000000,0x00000640, /* MOVE FROM dsa_dataout+0x0220, WHEN DATA_OUT -at 0x000001ca : */ 0x18000000,0x00000648, +at 0x000001c8 : */ 0x18000000,0x00000648, /* MOVE FROM dsa_dataout+0x0228, WHEN DATA_OUT -at 0x000001cc : */ 0x18000000,0x00000650, +at 0x000001ca : */ 0x18000000,0x00000650, /* MOVE FROM dsa_dataout+0x0230, WHEN DATA_OUT -at 0x000001ce : */ 0x18000000,0x00000658, +at 0x000001cc : */ 0x18000000,0x00000658, /* MOVE FROM dsa_dataout+0x0238, WHEN DATA_OUT -at 0x000001d0 : */ 0x18000000,0x00000660, +at 0x000001ce : */ 0x18000000,0x00000660, /* MOVE FROM dsa_dataout+0x0240, WHEN DATA_OUT -at 0x000001d2 : */ 0x18000000,0x00000668, +at 0x000001d0 : */ 0x18000000,0x00000668, /* MOVE FROM dsa_dataout+0x0248, WHEN DATA_OUT -at 0x000001d4 : */ 0x18000000,0x00000670, +at 0x000001d2 : */ 0x18000000,0x00000670, /* MOVE FROM dsa_dataout+0x0250, WHEN DATA_OUT -at 0x000001d6 : */ 0x18000000,0x00000678, +at 0x000001d4 : */ 0x18000000,0x00000678, /* MOVE FROM dsa_dataout+0x0258, WHEN DATA_OUT -at 0x000001d8 : */ 0x18000000,0x00000680, +at 0x000001d6 : */ 0x18000000,0x00000680, /* MOVE FROM dsa_dataout+0x0260, WHEN DATA_OUT -at 0x000001da : */ 0x18000000,0x00000688, +at 0x000001d8 : */ 0x18000000,0x00000688, /* MOVE FROM dsa_dataout+0x0268, WHEN DATA_OUT -at 0x000001dc : */ 0x18000000,0x00000690, +at 0x000001da : */ 0x18000000,0x00000690, /* MOVE FROM dsa_dataout+0x0270, WHEN DATA_OUT -at 0x000001de : */ 0x18000000,0x00000698, +at 0x000001dc : */ 0x18000000,0x00000698, /* MOVE FROM dsa_dataout+0x0278, WHEN DATA_OUT -at 0x000001e0 : */ 0x18000000,0x000006a0, +at 0x000001de : */ 0x18000000,0x000006a0, /* MOVE FROM dsa_dataout+0x0280, WHEN DATA_OUT -at 0x000001e2 : */ 0x18000000,0x000006a8, +at 0x000001e0 : */ 0x18000000,0x000006a8, /* MOVE FROM dsa_dataout+0x0288, WHEN DATA_OUT -at 0x000001e4 : */ 0x18000000,0x000006b0, +at 0x000001e2 : */ 0x18000000,0x000006b0, /* MOVE FROM dsa_dataout+0x0290, WHEN DATA_OUT -at 0x000001e6 : */ 0x18000000,0x000006b8, +at 0x000001e4 : */ 0x18000000,0x000006b8, /* MOVE FROM dsa_dataout+0x0298, WHEN DATA_OUT -at 0x000001e8 : */ 0x18000000,0x000006c0, +at 0x000001e6 : */ 0x18000000,0x000006c0, /* MOVE FROM dsa_dataout+0x02a0, WHEN DATA_OUT -at 0x000001ea : */ 0x18000000,0x000006c8, +at 0x000001e8 : */ 0x18000000,0x000006c8, /* MOVE FROM dsa_dataout+0x02a8, WHEN DATA_OUT -at 0x000001ec : */ 0x18000000,0x000006d0, +at 0x000001ea : */ 0x18000000,0x000006d0, /* MOVE FROM dsa_dataout+0x02b0, WHEN DATA_OUT -at 0x000001ee : */ 0x18000000,0x000006d8, +at 0x000001ec : */ 0x18000000,0x000006d8, /* MOVE FROM dsa_dataout+0x02b8, WHEN DATA_OUT -at 0x000001f0 : */ 0x18000000,0x000006e0, +at 0x000001ee : */ 0x18000000,0x000006e0, /* MOVE FROM dsa_dataout+0x02c0, WHEN DATA_OUT -at 0x000001f2 : */ 0x18000000,0x000006e8, +at 0x000001f0 : */ 0x18000000,0x000006e8, /* MOVE FROM dsa_dataout+0x02c8, WHEN DATA_OUT -at 0x000001f4 : */ 0x18000000,0x000006f0, +at 0x000001f2 : */ 0x18000000,0x000006f0, /* MOVE FROM dsa_dataout+0x02d0, WHEN DATA_OUT -at 0x000001f6 : */ 0x18000000,0x000006f8, +at 0x000001f4 : */ 0x18000000,0x000006f8, /* MOVE FROM dsa_dataout+0x02d8, WHEN DATA_OUT -at 0x000001f8 : */ 0x18000000,0x00000700, +at 0x000001f6 : */ 0x18000000,0x00000700, /* MOVE FROM dsa_dataout+0x02e0, WHEN DATA_OUT -at 0x000001fa : */ 0x18000000,0x00000708, +at 0x000001f8 : */ 0x18000000,0x00000708, /* MOVE FROM dsa_dataout+0x02e8, WHEN DATA_OUT -at 0x000001fc : */ 0x18000000,0x00000710, +at 0x000001fa : */ 0x18000000,0x00000710, /* MOVE FROM dsa_dataout+0x02f0, WHEN DATA_OUT -at 0x000001fe : */ 0x18000000,0x00000718, +at 0x000001fc : */ 0x18000000,0x00000718, /* MOVE FROM dsa_dataout+0x02f8, WHEN DATA_OUT -at 0x00000200 : */ 0x18000000,0x00000720, +at 0x000001fe : */ 0x18000000,0x00000720, /* MOVE FROM dsa_dataout+0x0300, WHEN DATA_OUT -at 0x00000202 : */ 0x18000000,0x00000728, +at 0x00000200 : */ 0x18000000,0x00000728, /* MOVE FROM dsa_dataout+0x0308, WHEN DATA_OUT -at 0x00000204 : */ 0x18000000,0x00000730, +at 0x00000202 : */ 0x18000000,0x00000730, /* MOVE FROM dsa_dataout+0x0310, WHEN DATA_OUT -at 0x00000206 : */ 0x18000000,0x00000738, +at 0x00000204 : */ 0x18000000,0x00000738, /* MOVE FROM dsa_dataout+0x0318, WHEN DATA_OUT -at 0x00000208 : */ 0x18000000,0x00000740, +at 0x00000206 : */ 0x18000000,0x00000740, /* MOVE FROM dsa_dataout+0x0320, WHEN DATA_OUT -at 0x0000020a : */ 0x18000000,0x00000748, +at 0x00000208 : */ 0x18000000,0x00000748, /* MOVE FROM dsa_dataout+0x0328, WHEN DATA_OUT -at 0x0000020c : */ 0x18000000,0x00000750, +at 0x0000020a : */ 0x18000000,0x00000750, /* MOVE FROM dsa_dataout+0x0330, WHEN DATA_OUT -at 0x0000020e : */ 0x18000000,0x00000758, +at 0x0000020c : */ 0x18000000,0x00000758, /* MOVE FROM dsa_dataout+0x0338, WHEN DATA_OUT -at 0x00000210 : */ 0x18000000,0x00000760, +at 0x0000020e : */ 0x18000000,0x00000760, /* MOVE FROM dsa_dataout+0x0340, WHEN DATA_OUT -at 0x00000212 : */ 0x18000000,0x00000768, +at 0x00000210 : */ 0x18000000,0x00000768, /* MOVE FROM dsa_dataout+0x0348, WHEN DATA_OUT -at 0x00000214 : */ 0x18000000,0x00000770, +at 0x00000212 : */ 0x18000000,0x00000770, /* MOVE FROM dsa_dataout+0x0350, WHEN DATA_OUT -at 0x00000216 : */ 0x18000000,0x00000778, +at 0x00000214 : */ 0x18000000,0x00000778, /* MOVE FROM dsa_dataout+0x0358, WHEN DATA_OUT -at 0x00000218 : */ 0x18000000,0x00000780, +at 0x00000216 : */ 0x18000000,0x00000780, /* MOVE FROM dsa_dataout+0x0360, WHEN DATA_OUT -at 0x0000021a : */ 0x18000000,0x00000788, +at 0x00000218 : */ 0x18000000,0x00000788, /* MOVE FROM dsa_dataout+0x0368, WHEN DATA_OUT -at 0x0000021c : */ 0x18000000,0x00000790, +at 0x0000021a : */ 0x18000000,0x00000790, /* MOVE FROM dsa_dataout+0x0370, WHEN DATA_OUT -at 0x0000021e : */ 0x18000000,0x00000798, +at 0x0000021c : */ 0x18000000,0x00000798, /* MOVE FROM dsa_dataout+0x0378, WHEN DATA_OUT -at 0x00000220 : */ 0x18000000,0x000007a0, +at 0x0000021e : */ 0x18000000,0x000007a0, /* MOVE FROM dsa_dataout+0x0380, WHEN DATA_OUT -at 0x00000222 : */ 0x18000000,0x000007a8, +at 0x00000220 : */ 0x18000000,0x000007a8, /* MOVE FROM dsa_dataout+0x0388, WHEN DATA_OUT -at 0x00000224 : */ 0x18000000,0x000007b0, +at 0x00000222 : */ 0x18000000,0x000007b0, /* MOVE FROM dsa_dataout+0x0390, WHEN DATA_OUT -at 0x00000226 : */ 0x18000000,0x000007b8, +at 0x00000224 : */ 0x18000000,0x000007b8, /* MOVE FROM dsa_dataout+0x0398, WHEN DATA_OUT -at 0x00000228 : */ 0x18000000,0x000007c0, +at 0x00000226 : */ 0x18000000,0x000007c0, /* MOVE FROM dsa_dataout+0x03a0, WHEN DATA_OUT -at 0x0000022a : */ 0x18000000,0x000007c8, +at 0x00000228 : */ 0x18000000,0x000007c8, /* MOVE FROM dsa_dataout+0x03a8, WHEN DATA_OUT -at 0x0000022c : */ 0x18000000,0x000007d0, +at 0x0000022a : */ 0x18000000,0x000007d0, /* MOVE FROM dsa_dataout+0x03b0, WHEN DATA_OUT -at 0x0000022e : */ 0x18000000,0x000007d8, +at 0x0000022c : */ 0x18000000,0x000007d8, /* MOVE FROM dsa_dataout+0x03b8, WHEN DATA_OUT -at 0x00000230 : */ 0x18000000,0x000007e0, +at 0x0000022e : */ 0x18000000,0x000007e0, /* MOVE FROM dsa_dataout+0x03c0, WHEN DATA_OUT -at 0x00000232 : */ 0x18000000,0x000007e8, +at 0x00000230 : */ 0x18000000,0x000007e8, /* MOVE FROM dsa_dataout+0x03c8, WHEN DATA_OUT -at 0x00000234 : */ 0x18000000,0x000007f0, +at 0x00000232 : */ 0x18000000,0x000007f0, /* MOVE FROM dsa_dataout+0x03d0, WHEN DATA_OUT -at 0x00000236 : */ 0x18000000,0x000007f8, +at 0x00000234 : */ 0x18000000,0x000007f8, /* MOVE FROM dsa_dataout+0x03d8, WHEN DATA_OUT -at 0x00000238 : */ 0x18000000,0x00000800, +at 0x00000236 : */ 0x18000000,0x00000800, /* MOVE FROM dsa_dataout+0x03e0, WHEN DATA_OUT -at 0x0000023a : */ 0x18000000,0x00000808, +at 0x00000238 : */ 0x18000000,0x00000808, /* MOVE FROM dsa_dataout+0x03e8, WHEN DATA_OUT -at 0x0000023c : */ 0x18000000,0x00000810, +at 0x0000023a : */ 0x18000000,0x00000810, /* MOVE FROM dsa_dataout+0x03f0, WHEN DATA_OUT -at 0x0000023e : */ 0x18000000,0x00000818, +at 0x0000023c : */ 0x18000000,0x00000818, /* MOVE FROM dsa_dataout+0x03f8, WHEN DATA_OUT -at 0x00000240 : */ 0x18000000,0x00000820, +at 0x0000023e : */ 0x18000000,0x00000820, /* ENTRY end_data_trans end_data_trans: redo_msgin3: JUMP get_status, WHEN STATUS -at 0x00000242 : */ 0x830b0000,0x000000a0, +at 0x00000240 : */ 0x830b0000,0x00000098, /* JUMP get_msgin3, WHEN MSG_IN -at 0x00000244 : */ 0x870b0000,0x00000b20, +at 0x00000242 : */ 0x870b0000,0x00000b78, /* INT int_data_bad_phase -at 0x00000246 : */ 0x98080000,0xab93000b, +at 0x00000244 : */ 0x98080000,0xab93000b, /* get_msgin1: MOVE SCRATCH0 | had_msgin TO SCRATCH0 -at 0x00000248 : */ 0x7a344000,0x00000000, +at 0x00000246 : */ 0x7a344000,0x00000000, /* MOVE 1, msgin_buf, WHEN MSG_IN -at 0x0000024a : */ 0x0f000001,0x00000000, +at 0x00000248 : */ 0x0f000001,0x00000000, /* JUMP ext_msg1, IF 0x01 ; Extended Message -at 0x0000024c : */ 0x800c0001,0x00000968, +at 0x0000024a : */ 0x800c0001,0x00000960, /* JUMP ignore_msg1, IF 0x02 ; Save Data Pointers -at 0x0000024e : */ 0x800c0002,0x00000958, +at 0x0000024c : */ 0x800c0002,0x00000950, /* JUMP ignore_msg1, IF 0x03 ; Save Restore Pointers -at 0x00000250 : */ 0x800c0003,0x00000958, +at 0x0000024e : */ 0x800c0003,0x00000950, /* JUMP disc1, IF 0x04 ; Disconnect -at 0x00000252 : */ 0x800c0004,0x000009c8, +at 0x00000250 : */ 0x800c0004,0x000009f0, /* INT int_bad_msg1 -at 0x00000254 : */ 0x98080000,0xab930006, +at 0x00000252 : */ 0x98080000,0xab930006, /* ignore_msg1: CLEAR ACK -at 0x00000256 : */ 0x60000040,0x00000000, +at 0x00000254 : */ 0x60000040,0x00000000, /* JUMP redo_msgin1 -at 0x00000258 : */ 0x80080000,0x00000058, +at 0x00000256 : */ 0x80080000,0x00000050, /* ext_msg1: MOVE SCRATCH0 | had_extmsg TO SCRATCH0 -at 0x0000025a : */ 0x7a348000,0x00000000, +at 0x00000258 : */ 0x7a348000,0x00000000, /* CLEAR ACK -at 0x0000025c : */ 0x60000040,0x00000000, +at 0x0000025a : */ 0x60000040,0x00000000, /* MOVE 1, msgin_buf + 1, WHEN MSG_IN -at 0x0000025e : */ 0x0f000001,0x00000001, +at 0x0000025c : */ 0x0f000001,0x00000001, /* - JUMP ext_msg1a, IF 0x03 + JUMP reject_msg1, IF NOT 0x03 ; Only handle SDTR -at 0x00000260 : */ 0x800c0003,0x00000990, +at 0x0000025e : */ 0x80040003,0x000009b0, /* - INT int_bad_extmsg1a + CLEAR ACK -at 0x00000262 : */ 0x98080000,0xab930000, +at 0x00000260 : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 2, WHEN MSG_IN + +at 0x00000262 : */ 0x0f000001,0x00000002, +/* + JUMP reject_msg1, IF NOT 0x01 ; Only handle SDTR + +at 0x00000264 : */ 0x80040001,0x000009b0, /* -ext_msg1a: CLEAR ACK -at 0x00000264 : */ 0x60000040,0x00000000, +at 0x00000266 : */ 0x60000040,0x00000000, /* - MOVE 1, msgin_buf + 2, WHEN MSG_IN + MOVE 2, msgin_buf + 3, WHEN MSG_IN -at 0x00000266 : */ 0x0f000001,0x00000002, +at 0x00000268 : */ 0x0f000002,0x00000003, /* - JUMP ext_msg1b, IF 0x01 ; Must be SDTR + INT int_msg_sdtr1 + +at 0x0000026a : */ 0x98080000,0xab93000c, +/* +reject_msg1: + MOVE SCRATCH1 | did_reject TO SCRATCH1 -at 0x00000268 : */ 0x800c0001,0x000009b0, +at 0x0000026c : */ 0x7a350100,0x00000000, /* - INT int_bad_extmsg1b + SET ATN -at 0x0000026a : */ 0x98080000,0xab930001, +at 0x0000026e : */ 0x58000008,0x00000000, /* -ext_msg1b: CLEAR ACK -at 0x0000026c : */ 0x60000040,0x00000000, +at 0x00000270 : */ 0x60000040,0x00000000, /* - MOVE 2, msgin_buf + 3, WHEN MSG_IN + JUMP reject_msg1a, WHEN NOT MSG_IN -at 0x0000026e : */ 0x0f000002,0x00000003, +at 0x00000272 : */ 0x87030000,0x000009e0, /* - INT int_msg_sdtr1 + MOVE 1, msgin_buf + 7, WHEN MSG_IN -at 0x00000270 : */ 0x98080000,0xab93000c, +at 0x00000274 : */ 0x0f000001,0x00000007, +/* + JUMP reject_msg1 + +at 0x00000276 : */ 0x80080000,0x000009b0, +/* +reject_msg1a: + MOVE 1, msg_reject, WHEN MSG_OUT + +at 0x00000278 : */ 0x0e000001,0x00000000, +/* + JUMP redo_msgin1 + +at 0x0000027a : */ 0x80080000,0x00000050, /* disc1: CLEAR ACK -at 0x00000272 : */ 0x60000040,0x00000000, +at 0x0000027c : */ 0x60000040,0x00000000, /* ENTRY wait_disc1 wait_disc1: WAIT DISCONNECT -at 0x00000274 : */ 0x48000000,0x00000000, +at 0x0000027e : */ 0x48000000,0x00000000, /* INT int_disc1 -at 0x00000276 : */ 0x98080000,0xab930019, +at 0x00000280 : */ 0x98080000,0xab930019, /* ENTRY resume_msgin1a resume_msgin1a: CLEAR ACK -at 0x00000278 : */ 0x60000040,0x00000000, +at 0x00000282 : */ 0x60000040,0x00000000, /* JUMP redo_msgin1 -at 0x0000027a : */ 0x80080000,0x00000058, +at 0x00000284 : */ 0x80080000,0x00000050, /* ENTRY resume_msgin1b resume_msgin1b: SET ATN -at 0x0000027c : */ 0x58000008,0x00000000, +at 0x00000286 : */ 0x58000008,0x00000000, /* CLEAR ACK -at 0x0000027e : */ 0x60000040,0x00000000, +at 0x00000288 : */ 0x60000040,0x00000000, /* INT int_no_msgout1, WHEN NOT MSG_OUT -at 0x00000280 : */ 0x9e030000,0xab93000f, +at 0x0000028a : */ 0x9e030000,0xab93000f, /* MOVE SCRATCH0 | had_msgout TO SCRATCH0 -at 0x00000282 : */ 0x7a340200,0x00000000, +at 0x0000028c : */ 0x7a340200,0x00000000, /* MOVE FROM dsa_msgout, when MSG_OUT -at 0x00000284 : */ 0x1e000000,0x00000008, +at 0x0000028e : */ 0x1e000000,0x00000008, /* JUMP redo_msgin1 -at 0x00000286 : */ 0x80080000,0x00000058, +at 0x00000290 : */ 0x80080000,0x00000050, /* get_msgin2: MOVE SCRATCH0 | had_msgin TO SCRATCH0 -at 0x00000288 : */ 0x7a344000,0x00000000, +at 0x00000292 : */ 0x7a344000,0x00000000, /* MOVE 1, msgin_buf, WHEN MSG_IN -at 0x0000028a : */ 0x0f000001,0x00000000, +at 0x00000294 : */ 0x0f000001,0x00000000, /* JUMP ext_msg2, IF 0x01 ; Extended Message -at 0x0000028c : */ 0x800c0001,0x00000a68, +at 0x00000296 : */ 0x800c0001,0x00000a90, /* JUMP ignore_msg2, IF 0x02 ; Save Data Pointers -at 0x0000028e : */ 0x800c0002,0x00000a58, +at 0x00000298 : */ 0x800c0002,0x00000a80, /* JUMP ignore_msg2, IF 0x03 ; Save Restore Pointers -at 0x00000290 : */ 0x800c0003,0x00000a58, +at 0x0000029a : */ 0x800c0003,0x00000a80, /* JUMP disc2, IF 0x04 ; Disconnect -at 0x00000292 : */ 0x800c0004,0x00000ac8, +at 0x0000029c : */ 0x800c0004,0x00000b20, /* INT int_bad_msg2 -at 0x00000294 : */ 0x98080000,0xab930007, +at 0x0000029e : */ 0x98080000,0xab930007, /* ignore_msg2: CLEAR ACK -at 0x00000296 : */ 0x60000040,0x00000000, +at 0x000002a0 : */ 0x60000040,0x00000000, /* JUMP redo_msgin2 -at 0x00000298 : */ 0x80080000,0x00000078, +at 0x000002a2 : */ 0x80080000,0x00000070, /* ext_msg2: MOVE SCRATCH0 | had_extmsg TO SCRATCH0 -at 0x0000029a : */ 0x7a348000,0x00000000, +at 0x000002a4 : */ 0x7a348000,0x00000000, /* CLEAR ACK -at 0x0000029c : */ 0x60000040,0x00000000, +at 0x000002a6 : */ 0x60000040,0x00000000, /* MOVE 1, msgin_buf + 1, WHEN MSG_IN -at 0x0000029e : */ 0x0f000001,0x00000001, +at 0x000002a8 : */ 0x0f000001,0x00000001, +/* + JUMP reject_msg2, IF NOT 0x03 ; Only handle SDTR + +at 0x000002aa : */ 0x80040003,0x00000ae0, +/* + CLEAR ACK + +at 0x000002ac : */ 0x60000040,0x00000000, /* - JUMP ext_msg2a, IF 0x03 + MOVE 1, msgin_buf + 2, WHEN MSG_IN -at 0x000002a0 : */ 0x800c0003,0x00000a90, +at 0x000002ae : */ 0x0f000001,0x00000002, /* - INT int_bad_extmsg2a + JUMP reject_msg2, IF NOT 0x01 ; Only handle SDTR -at 0x000002a2 : */ 0x98080000,0xab930002, +at 0x000002b0 : */ 0x80040001,0x00000ae0, /* -ext_msg2a: CLEAR ACK -at 0x000002a4 : */ 0x60000040,0x00000000, +at 0x000002b2 : */ 0x60000040,0x00000000, /* - MOVE 1, msgin_buf + 2, WHEN MSG_IN + MOVE 2, msgin_buf + 3, WHEN MSG_IN + +at 0x000002b4 : */ 0x0f000002,0x00000003, +/* + INT int_msg_sdtr2 -at 0x000002a6 : */ 0x0f000001,0x00000002, +at 0x000002b6 : */ 0x98080000,0xab93000d, /* - JUMP ext_msg2b, IF 0x01 ; Must be SDTR +reject_msg2: + MOVE SCRATCH1 | did_reject TO SCRATCH1 -at 0x000002a8 : */ 0x800c0001,0x00000ab0, +at 0x000002b8 : */ 0x7a350100,0x00000000, /* - INT int_bad_extmsg2b + SET ATN -at 0x000002aa : */ 0x98080000,0xab930003, +at 0x000002ba : */ 0x58000008,0x00000000, /* -ext_msg2b: CLEAR ACK -at 0x000002ac : */ 0x60000040,0x00000000, +at 0x000002bc : */ 0x60000040,0x00000000, /* - MOVE 2, msgin_buf + 3, WHEN MSG_IN + JUMP reject_msg2a, WHEN NOT MSG_IN -at 0x000002ae : */ 0x0f000002,0x00000003, +at 0x000002be : */ 0x87030000,0x00000b10, /* - INT int_msg_sdtr2 + MOVE 1, msgin_buf + 7, WHEN MSG_IN + +at 0x000002c0 : */ 0x0f000001,0x00000007, +/* + JUMP reject_msg2 + +at 0x000002c2 : */ 0x80080000,0x00000ae0, +/* +reject_msg2a: + MOVE 1, msg_reject, WHEN MSG_OUT + +at 0x000002c4 : */ 0x0e000001,0x00000000, +/* + JUMP redo_msgin2 -at 0x000002b0 : */ 0x98080000,0xab93000d, +at 0x000002c6 : */ 0x80080000,0x00000070, /* disc2: CLEAR ACK -at 0x000002b2 : */ 0x60000040,0x00000000, +at 0x000002c8 : */ 0x60000040,0x00000000, /* ENTRY wait_disc2 wait_disc2: WAIT DISCONNECT -at 0x000002b4 : */ 0x48000000,0x00000000, +at 0x000002ca : */ 0x48000000,0x00000000, /* INT int_disc2 -at 0x000002b6 : */ 0x98080000,0xab93001a, +at 0x000002cc : */ 0x98080000,0xab93001a, /* ENTRY resume_msgin2a resume_msgin2a: CLEAR ACK -at 0x000002b8 : */ 0x60000040,0x00000000, +at 0x000002ce : */ 0x60000040,0x00000000, /* JUMP redo_msgin2 -at 0x000002ba : */ 0x80080000,0x00000078, +at 0x000002d0 : */ 0x80080000,0x00000070, /* ENTRY resume_msgin2b resume_msgin2b: SET ATN -at 0x000002bc : */ 0x58000008,0x00000000, +at 0x000002d2 : */ 0x58000008,0x00000000, /* CLEAR ACK -at 0x000002be : */ 0x60000040,0x00000000, +at 0x000002d4 : */ 0x60000040,0x00000000, /* INT int_no_msgout2, WHEN NOT MSG_OUT -at 0x000002c0 : */ 0x9e030000,0xab930010, +at 0x000002d6 : */ 0x9e030000,0xab930010, /* MOVE SCRATCH0 | had_msgout TO SCRATCH0 -at 0x000002c2 : */ 0x7a340200,0x00000000, +at 0x000002d8 : */ 0x7a340200,0x00000000, /* MOVE FROM dsa_msgout, when MSG_OUT -at 0x000002c4 : */ 0x1e000000,0x00000008, +at 0x000002da : */ 0x1e000000,0x00000008, /* JUMP redo_msgin2 -at 0x000002c6 : */ 0x80080000,0x00000078, +at 0x000002dc : */ 0x80080000,0x00000070, /* get_msgin3: MOVE SCRATCH0 | had_msgin TO SCRATCH0 -at 0x000002c8 : */ 0x7a344000,0x00000000, +at 0x000002de : */ 0x7a344000,0x00000000, /* MOVE 1, msgin_buf, WHEN MSG_IN -at 0x000002ca : */ 0x0f000001,0x00000000, +at 0x000002e0 : */ 0x0f000001,0x00000000, /* JUMP ext_msg3, IF 0x01 ; Extended Message -at 0x000002cc : */ 0x800c0001,0x00000b68, +at 0x000002e2 : */ 0x800c0001,0x00000bc0, /* JUMP ignore_msg3, IF 0x02 ; Save Data Pointers -at 0x000002ce : */ 0x800c0002,0x00000b58, +at 0x000002e4 : */ 0x800c0002,0x00000bb0, /* JUMP ignore_msg3, IF 0x03 ; Save Restore Pointers -at 0x000002d0 : */ 0x800c0003,0x00000b58, +at 0x000002e6 : */ 0x800c0003,0x00000bb0, /* JUMP disc3, IF 0x04 ; Disconnect -at 0x000002d2 : */ 0x800c0004,0x00000bc8, +at 0x000002e8 : */ 0x800c0004,0x00000c50, /* INT int_bad_msg3 -at 0x000002d4 : */ 0x98080000,0xab930008, +at 0x000002ea : */ 0x98080000,0xab930008, /* ignore_msg3: CLEAR ACK -at 0x000002d6 : */ 0x60000040,0x00000000, +at 0x000002ec : */ 0x60000040,0x00000000, /* JUMP redo_msgin3 -at 0x000002d8 : */ 0x80080000,0x00000908, +at 0x000002ee : */ 0x80080000,0x00000900, /* ext_msg3: MOVE SCRATCH0 | had_extmsg TO SCRATCH0 -at 0x000002da : */ 0x7a348000,0x00000000, +at 0x000002f0 : */ 0x7a348000,0x00000000, /* CLEAR ACK -at 0x000002dc : */ 0x60000040,0x00000000, +at 0x000002f2 : */ 0x60000040,0x00000000, /* MOVE 1, msgin_buf + 1, WHEN MSG_IN -at 0x000002de : */ 0x0f000001,0x00000001, +at 0x000002f4 : */ 0x0f000001,0x00000001, /* - JUMP ext_msg3a, IF 0x03 + JUMP reject_msg3, IF NOT 0x03 ; Only handle SDTR -at 0x000002e0 : */ 0x800c0003,0x00000b90, +at 0x000002f6 : */ 0x80040003,0x00000c10, /* - INT int_bad_extmsg3a + CLEAR ACK -at 0x000002e2 : */ 0x98080000,0xab930004, +at 0x000002f8 : */ 0x60000040,0x00000000, +/* + MOVE 1, msgin_buf + 2, WHEN MSG_IN + +at 0x000002fa : */ 0x0f000001,0x00000002, +/* + JUMP reject_msg3, IF NOT 0x01 ; Only handle SDTR + +at 0x000002fc : */ 0x80040001,0x00000c10, /* -ext_msg3a: CLEAR ACK -at 0x000002e4 : */ 0x60000040,0x00000000, +at 0x000002fe : */ 0x60000040,0x00000000, /* - MOVE 1, msgin_buf + 2, WHEN MSG_IN + MOVE 2, msgin_buf + 3, WHEN MSG_IN + +at 0x00000300 : */ 0x0f000002,0x00000003, +/* + INT int_msg_sdtr3 -at 0x000002e6 : */ 0x0f000001,0x00000002, +at 0x00000302 : */ 0x98080000,0xab93000e, /* - JUMP ext_msg3b, IF 0x01 ; Must be SDTR +reject_msg3: + MOVE SCRATCH1 | did_reject TO SCRATCH1 -at 0x000002e8 : */ 0x800c0001,0x00000bb0, +at 0x00000304 : */ 0x7a350100,0x00000000, /* - INT int_bad_extmsg3b + SET ATN -at 0x000002ea : */ 0x98080000,0xab930005, +at 0x00000306 : */ 0x58000008,0x00000000, /* -ext_msg3b: CLEAR ACK -at 0x000002ec : */ 0x60000040,0x00000000, +at 0x00000308 : */ 0x60000040,0x00000000, /* - MOVE 2, msgin_buf + 3, WHEN MSG_IN + JUMP reject_msg3a, WHEN NOT MSG_IN -at 0x000002ee : */ 0x0f000002,0x00000003, +at 0x0000030a : */ 0x87030000,0x00000c40, /* - INT int_msg_sdtr3 + MOVE 1, msgin_buf + 7, WHEN MSG_IN + +at 0x0000030c : */ 0x0f000001,0x00000007, +/* + JUMP reject_msg3 + +at 0x0000030e : */ 0x80080000,0x00000c10, +/* +reject_msg3a: + MOVE 1, msg_reject, WHEN MSG_OUT -at 0x000002f0 : */ 0x98080000,0xab93000e, +at 0x00000310 : */ 0x0e000001,0x00000000, +/* + JUMP redo_msgin3 + +at 0x00000312 : */ 0x80080000,0x00000900, /* disc3: CLEAR ACK -at 0x000002f2 : */ 0x60000040,0x00000000, +at 0x00000314 : */ 0x60000040,0x00000000, /* ENTRY wait_disc3 wait_disc3: WAIT DISCONNECT -at 0x000002f4 : */ 0x48000000,0x00000000, +at 0x00000316 : */ 0x48000000,0x00000000, /* INT int_disc3 -at 0x000002f6 : */ 0x98080000,0xab93001b, +at 0x00000318 : */ 0x98080000,0xab93001b, /* ENTRY resume_msgin3a resume_msgin3a: CLEAR ACK -at 0x000002f8 : */ 0x60000040,0x00000000, +at 0x0000031a : */ 0x60000040,0x00000000, /* JUMP redo_msgin3 -at 0x000002fa : */ 0x80080000,0x00000908, +at 0x0000031c : */ 0x80080000,0x00000900, /* ENTRY resume_msgin3b resume_msgin3b: SET ATN -at 0x000002fc : */ 0x58000008,0x00000000, +at 0x0000031e : */ 0x58000008,0x00000000, /* CLEAR ACK -at 0x000002fe : */ 0x60000040,0x00000000, +at 0x00000320 : */ 0x60000040,0x00000000, /* INT int_no_msgout3, WHEN NOT MSG_OUT -at 0x00000300 : */ 0x9e030000,0xab930011, +at 0x00000322 : */ 0x9e030000,0xab930011, /* MOVE SCRATCH0 | had_msgout TO SCRATCH0 -at 0x00000302 : */ 0x7a340200,0x00000000, +at 0x00000324 : */ 0x7a340200,0x00000000, /* MOVE FROM dsa_msgout, when MSG_OUT -at 0x00000304 : */ 0x1e000000,0x00000008, +at 0x00000326 : */ 0x1e000000,0x00000008, /* JUMP redo_msgin3 -at 0x00000306 : */ 0x80080000,0x00000908, +at 0x00000328 : */ 0x80080000,0x00000900, /* ENTRY resume_rej_ident resume_rej_ident: CLEAR ATN -at 0x00000308 : */ 0x60000008,0x00000000, +at 0x0000032a : */ 0x60000008,0x00000000, /* MOVE 1, msgin_buf, WHEN MSG_IN -at 0x0000030a : */ 0x0f000001,0x00000000, +at 0x0000032c : */ 0x0f000001,0x00000000, /* INT int_not_rej, IF NOT 0x07 ; Reject -at 0x0000030c : */ 0x98040007,0xab93001c, +at 0x0000032e : */ 0x98040007,0xab93001c, /* CLEAR ACK -at 0x0000030e : */ 0x60000040,0x00000000, +at 0x00000330 : */ 0x60000040,0x00000000, /* JUMP done_ident -at 0x00000310 : */ 0x80080000,0x00000050, +at 0x00000332 : */ 0x80080000,0x00000048, /* ENTRY reselect @@ -1716,73 +1784,92 @@ ; Disable selection timer MOVE CTEST7 | 0x10 TO CTEST7 -at 0x00000312 : */ 0x7a1b1000,0x00000000, +at 0x00000334 : */ 0x7a1b1000,0x00000000, /* WAIT RESELECT resel_err -at 0x00000314 : */ 0x50000000,0x00000c70, +at 0x00000336 : */ 0x50000000,0x00000cf8, /* INT int_resel_not_msgin, WHEN NOT MSG_IN -at 0x00000316 : */ 0x9f030000,0xab930016, +at 0x00000338 : */ 0x9f030000,0xab930016, /* MOVE 1, reselected_identify, WHEN MSG_IN -at 0x00000318 : */ 0x0f000001,0x00000000, +at 0x0000033a : */ 0x0f000001,0x00000000, /* INT int_reselected -at 0x0000031a : */ 0x98080000,0xab930017, +at 0x0000033c : */ 0x98080000,0xab930017, /* resel_err: MOVE CTEST2 & 0x40 TO SFBR -at 0x0000031c : */ 0x74164000,0x00000000, +at 0x0000033e : */ 0x74164000,0x00000000, /* JUMP selected, IF 0x00 -at 0x0000031e : */ 0x800c0000,0x00000cb0, +at 0x00000340 : */ 0x800c0000,0x00000d38, /* MOVE SFBR & 0 TO SFBR -at 0x00000320 : */ 0x7c080000,0x00000000, +at 0x00000342 : */ 0x7c080000,0x00000000, /* ENTRY patch_new_dsa patch_new_dsa: MOVE SFBR | 0x11 TO DSA0 -at 0x00000322 : */ 0x6a101100,0x00000000, +at 0x00000344 : */ 0x6a101100,0x00000000, /* MOVE SFBR | 0x22 TO DSA1 -at 0x00000324 : */ 0x6a112200,0x00000000, +at 0x00000346 : */ 0x6a112200,0x00000000, /* MOVE SFBR | 0x33 TO DSA2 -at 0x00000326 : */ 0x6a123300,0x00000000, +at 0x00000348 : */ 0x6a123300,0x00000000, /* MOVE SFBR | 0x44 TO DSA3 -at 0x00000328 : */ 0x6a134400,0x00000000, +at 0x0000034a : */ 0x6a134400,0x00000000, /* JUMP do_select -at 0x0000032a : */ 0x80080000,0x00000000, +at 0x0000034c : */ 0x80080000,0x00000000, /* selected: INT int_selected -at 0x0000032c : */ 0x98080000,0xab930018, +at 0x0000034e : */ 0x98080000,0xab930018, +/* + +ENTRY test1 +test1: + MOVE MEMORY 4, test1_src, test1_dst + +at 0x00000350 : */ 0xc0000004,0x00000000,0x00000000, +/* + INT int_test1 + +at 0x00000353 : */ 0x98080000,0xab93001d, +}; + +#define A_did_reject 0x00000001 +static u32 A_did_reject_used[] __attribute((unused)) = { + 0x0000026c, + 0x000002b8, + 0x00000304, }; #define A_dsa_cmnd 0x00000010 static u32 A_dsa_cmnd_used[] __attribute((unused)) = { - 0x0000001d, + 0x0000001b, }; #define A_dsa_datain 0x00000028 static u32 A_dsa_datain_used[] __attribute((unused)) = { + 0x0000003b, 0x0000003d, 0x0000003f, 0x00000041, @@ -1910,11 +1997,11 @@ 0x00000135, 0x00000137, 0x00000139, - 0x0000013b, }; #define A_dsa_dataout 0x00000428 static u32 A_dsa_dataout_used[] __attribute((unused)) = { + 0x00000141, 0x00000143, 0x00000145, 0x00000147, @@ -2042,25 +2129,24 @@ 0x0000023b, 0x0000023d, 0x0000023f, - 0x00000241, }; #define A_dsa_msgin 0x00000020 static u32 A_dsa_msgin_used[] __attribute((unused)) = { - 0x0000002f, + 0x0000002d, }; #define A_dsa_msgout 0x00000008 static u32 A_dsa_msgout_used[] __attribute((unused)) = { - 0x00000013, - 0x00000285, - 0x000002c5, - 0x00000305, + 0x00000011, + 0x0000028f, + 0x000002db, + 0x00000327, }; #define A_dsa_select 0x00000000 static u32 A_dsa_select_used[] __attribute((unused)) = { - 0x00000006, + 0x00000004, }; #define A_dsa_size 0x00000828 @@ -2069,285 +2155,290 @@ #define A_dsa_status 0x00000018 static u32 A_dsa_status_used[] __attribute((unused)) = { - 0x0000002b, + 0x00000029, }; #define A_had_cmdout 0x00000004 static u32 A_had_cmdout_used[] __attribute((unused)) = { - 0x0000001a, + 0x00000018, }; #define A_had_datain 0x00000008 static u32 A_had_datain_used[] __attribute((unused)) = { - 0x00000038, + 0x00000036, }; #define A_had_dataout 0x00000010 static u32 A_had_dataout_used[] __attribute((unused)) = { - 0x0000013e, + 0x0000013c, }; #define A_had_extmsg 0x00000080 static u32 A_had_extmsg_used[] __attribute((unused)) = { - 0x0000025a, - 0x0000029a, - 0x000002da, + 0x00000258, + 0x000002a4, + 0x000002f0, }; #define A_had_msgin 0x00000040 static u32 A_had_msgin_used[] __attribute((unused)) = { - 0x00000248, - 0x00000288, - 0x000002c8, + 0x00000246, + 0x00000292, + 0x000002de, }; #define A_had_msgout 0x00000002 static u32 A_had_msgout_used[] __attribute((unused)) = { - 0x00000010, - 0x00000282, - 0x000002c2, - 0x00000302, + 0x0000000e, + 0x0000028c, + 0x000002d8, + 0x00000324, }; #define A_had_select 0x00000001 static u32 A_had_select_used[] __attribute((unused)) = { - 0x0000000c, + 0x0000000a, }; #define A_had_status 0x00000020 static u32 A_had_status_used[] __attribute((unused)) = { }; -#define A_int_bad_extmsg1a 0xab930000 -static u32 A_int_bad_extmsg1a_used[] __attribute((unused)) = { - 0x00000263, -}; - -#define A_int_bad_extmsg1b 0xab930001 -static u32 A_int_bad_extmsg1b_used[] __attribute((unused)) = { - 0x0000026b, -}; - -#define A_int_bad_extmsg2a 0xab930002 -static u32 A_int_bad_extmsg2a_used[] __attribute((unused)) = { - 0x000002a3, -}; - -#define A_int_bad_extmsg2b 0xab930003 -static u32 A_int_bad_extmsg2b_used[] __attribute((unused)) = { - 0x000002ab, -}; - -#define A_int_bad_extmsg3a 0xab930004 -static u32 A_int_bad_extmsg3a_used[] __attribute((unused)) = { - 0x000002e3, -}; - -#define A_int_bad_extmsg3b 0xab930005 -static u32 A_int_bad_extmsg3b_used[] __attribute((unused)) = { - 0x000002eb, -}; - #define A_int_bad_msg1 0xab930006 static u32 A_int_bad_msg1_used[] __attribute((unused)) = { - 0x00000255, + 0x00000253, }; #define A_int_bad_msg2 0xab930007 static u32 A_int_bad_msg2_used[] __attribute((unused)) = { - 0x00000295, + 0x0000029f, }; #define A_int_bad_msg3 0xab930008 static u32 A_int_bad_msg3_used[] __attribute((unused)) = { - 0x000002d5, + 0x000002eb, }; #define A_int_cmd_bad_phase 0xab930009 static u32 A_int_cmd_bad_phase_used[] __attribute((unused)) = { - 0x00000027, + 0x00000025, }; #define A_int_cmd_complete 0xab93000a static u32 A_int_cmd_complete_used[] __attribute((unused)) = { - 0x00000037, + 0x00000035, }; #define A_int_data_bad_phase 0xab93000b static u32 A_int_data_bad_phase_used[] __attribute((unused)) = { - 0x00000247, + 0x00000245, }; #define A_int_disc1 0xab930019 static u32 A_int_disc1_used[] __attribute((unused)) = { - 0x00000277, + 0x00000281, }; #define A_int_disc2 0xab93001a static u32 A_int_disc2_used[] __attribute((unused)) = { - 0x000002b7, + 0x000002cd, }; #define A_int_disc3 0xab93001b static u32 A_int_disc3_used[] __attribute((unused)) = { - 0x000002f7, + 0x00000319, }; #define A_int_msg_sdtr1 0xab93000c static u32 A_int_msg_sdtr1_used[] __attribute((unused)) = { - 0x00000271, + 0x0000026b, }; #define A_int_msg_sdtr2 0xab93000d static u32 A_int_msg_sdtr2_used[] __attribute((unused)) = { - 0x000002b1, + 0x000002b7, }; #define A_int_msg_sdtr3 0xab93000e static u32 A_int_msg_sdtr3_used[] __attribute((unused)) = { - 0x000002f1, + 0x00000303, }; #define A_int_no_msgout1 0xab93000f static u32 A_int_no_msgout1_used[] __attribute((unused)) = { - 0x00000281, + 0x0000028b, }; #define A_int_no_msgout2 0xab930010 static u32 A_int_no_msgout2_used[] __attribute((unused)) = { - 0x000002c1, + 0x000002d7, }; #define A_int_no_msgout3 0xab930011 static u32 A_int_no_msgout3_used[] __attribute((unused)) = { - 0x00000301, + 0x00000323, }; #define A_int_not_cmd_complete 0xab930012 static u32 A_int_not_cmd_complete_used[] __attribute((unused)) = { - 0x00000031, + 0x0000002f, }; #define A_int_not_rej 0xab93001c static u32 A_int_not_rej_used[] __attribute((unused)) = { - 0x0000030d, + 0x0000032f, }; #define A_int_resel_not_msgin 0xab930016 static u32 A_int_resel_not_msgin_used[] __attribute((unused)) = { - 0x00000317, + 0x00000339, }; #define A_int_reselected 0xab930017 static u32 A_int_reselected_used[] __attribute((unused)) = { - 0x0000031b, + 0x0000033d, }; #define A_int_sel_no_ident 0xab930013 static u32 A_int_sel_no_ident_used[] __attribute((unused)) = { - 0x0000000f, + 0x0000000d, }; #define A_int_sel_not_cmd 0xab930014 static u32 A_int_sel_not_cmd_used[] __attribute((unused)) = { - 0x00000019, + 0x00000017, }; #define A_int_selected 0xab930018 static u32 A_int_selected_used[] __attribute((unused)) = { - 0x0000032d, + 0x0000034f, }; #define A_int_status_not_msgin 0xab930015 static u32 A_int_status_not_msgin_used[] __attribute((unused)) = { - 0x0000002d, + 0x0000002b, +}; + +#define A_int_test1 0xab93001d +static u32 A_int_test1_used[] __attribute((unused)) = { + 0x00000354, +}; + +#define A_msg_reject 0x00000000 +static u32 A_msg_reject_used[] __attribute((unused)) = { + 0x00000279, + 0x000002c5, + 0x00000311, }; #define A_msgin_buf 0x00000000 static u32 A_msgin_buf_used[] __attribute((unused)) = { - 0x0000024b, - 0x0000025f, - 0x00000267, - 0x0000026f, - 0x0000028b, - 0x0000029f, - 0x000002a7, + 0x00000249, + 0x0000025d, + 0x00000263, + 0x00000269, + 0x00000275, + 0x00000295, + 0x000002a9, 0x000002af, - 0x000002cb, - 0x000002df, - 0x000002e7, - 0x000002ef, - 0x0000030b, + 0x000002b5, + 0x000002c1, + 0x000002e1, + 0x000002f5, + 0x000002fb, + 0x00000301, + 0x0000030d, + 0x0000032d, }; #define A_reselected_identify 0x00000000 static u32 A_reselected_identify_used[] __attribute((unused)) = { - 0x00000319, + 0x0000033b, +}; + +#define A_test1_dst 0x00000000 +static u32 A_test1_dst_used[] __attribute((unused)) = { + 0x00000352, +}; + +#define A_test1_src 0x00000000 +static u32 A_test1_src_used[] __attribute((unused)) = { + 0x00000351, }; #define Ent_do_select 0x00000000 -#define Ent_done_ident 0x00000050 -#define Ent_end_data_trans 0x00000908 -#define Ent_patch_input_data 0x000000e8 -#define Ent_patch_new_dsa 0x00000c88 -#define Ent_patch_output_data 0x00000500 -#define Ent_reselect 0x00000c48 -#define Ent_resume_cmd 0x00000068 -#define Ent_resume_msgin1a 0x000009e0 -#define Ent_resume_msgin1b 0x000009f0 -#define Ent_resume_msgin2a 0x00000ae0 -#define Ent_resume_msgin2b 0x00000af0 -#define Ent_resume_msgin3a 0x00000be0 -#define Ent_resume_msgin3b 0x00000bf0 -#define Ent_resume_pmm 0x00000078 -#define Ent_resume_rej_ident 0x00000c20 -#define Ent_wait_disc1 0x000009d0 -#define Ent_wait_disc2 0x00000ad0 -#define Ent_wait_disc3 0x00000bd0 -#define Ent_wait_disc_complete 0x000000d0 +#define Ent_done_ident 0x00000048 +#define Ent_end_data_trans 0x00000900 +#define Ent_patch_input_data 0x000000e0 +#define Ent_patch_new_dsa 0x00000d10 +#define Ent_patch_output_data 0x000004f8 +#define Ent_reselect 0x00000cd0 +#define Ent_resume_cmd 0x00000060 +#define Ent_resume_msgin1a 0x00000a08 +#define Ent_resume_msgin1b 0x00000a18 +#define Ent_resume_msgin2a 0x00000b38 +#define Ent_resume_msgin2b 0x00000b48 +#define Ent_resume_msgin3a 0x00000c68 +#define Ent_resume_msgin3b 0x00000c78 +#define Ent_resume_pmm 0x00000070 +#define Ent_resume_rej_ident 0x00000ca8 +#define Ent_test1 0x00000d40 +#define Ent_wait_disc1 0x000009f8 +#define Ent_wait_disc2 0x00000b28 +#define Ent_wait_disc3 0x00000c58 +#define Ent_wait_disc_complete 0x000000c8 static u32 LABELPATCHES[] __attribute((unused)) = { + 0x00000005, 0x00000007, - 0x00000009, + 0x00000013, 0x00000015, - 0x00000017, + 0x0000001d, 0x0000001f, 0x00000021, 0x00000023, - 0x00000025, - 0x0000013d, + 0x0000013b, + 0x00000241, 0x00000243, - 0x00000245, + 0x0000024b, 0x0000024d, 0x0000024f, 0x00000251, - 0x00000253, - 0x00000259, - 0x00000261, - 0x00000269, + 0x00000257, + 0x0000025f, + 0x00000265, + 0x00000273, + 0x00000277, 0x0000027b, - 0x00000287, - 0x0000028d, - 0x0000028f, + 0x00000285, 0x00000291, - 0x00000293, + 0x00000297, 0x00000299, - 0x000002a1, - 0x000002a9, - 0x000002bb, + 0x0000029b, + 0x0000029d, + 0x000002a3, + 0x000002ab, + 0x000002b1, + 0x000002bf, + 0x000002c3, 0x000002c7, - 0x000002cd, - 0x000002cf, 0x000002d1, - 0x000002d3, - 0x000002d9, - 0x000002e1, + 0x000002dd, + 0x000002e3, + 0x000002e5, + 0x000002e7, 0x000002e9, - 0x000002fb, - 0x00000307, - 0x00000311, - 0x00000315, - 0x0000031f, - 0x0000032b, + 0x000002ef, + 0x000002f7, + 0x000002fd, + 0x0000030b, + 0x0000030f, + 0x00000313, + 0x0000031d, + 0x00000329, + 0x00000333, + 0x00000337, + 0x00000341, + 0x0000034d, }; static struct { @@ -2356,6 +2447,6 @@ } EXTERNAL_PATCHES[] __attribute((unused)) = { }; -static u32 INSTRUCTIONS __attribute((unused)) = 407; -static u32 PATCHES __attribute((unused)) = 42; +static u32 INSTRUCTIONS __attribute((unused)) = 426; +static u32 PATCHES __attribute((unused)) = 51; static u32 EXTERNAL_PATCHES_LEN __attribute((unused)) = 0; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/st.c linux.21rc1-ac2/drivers/scsi/st.c --- linux.21rc1/drivers/scsi/st.c 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/st.c 2003-04-22 16:44:37.000000000 +0100 @@ -9,10 +9,10 @@ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and Eric Youngdale. - Copyright 1992 - 2002 Kai Makisara - email Kai.Makisara@metla.fi + Copyright 1992 - 2003 Kai Makisara + email Kai.Makisara@kolumbus.fi - Last modified: Mon Aug 5 22:54:13 2002 by makisara + Last modified: Sun Apr 6 22:44:42 2003 by makisara Some small formal changes - aeb, 950809 Last modified: 18-JAN-1998 Richard Gooch Devfs support @@ -21,7 +21,7 @@ error handling will be discarded. */ -static char *verstr = "20020805"; +static char *verstr = "20030406"; #include @@ -72,7 +72,6 @@ #include "constants.h" static int buffer_kbs; -static int write_threshold_kbs; static int max_buffers = (-1); static int max_sg_segs; static int blocking_open = ST_BLOCKING_OPEN; @@ -84,8 +83,6 @@ MODULE_PARM(buffer_kbs, "i"); MODULE_PARM_DESC(buffer_kbs, "Default driver buffer size (KB; 32)"); -MODULE_PARM(write_threshold_kbs, "i"); -MODULE_PARM_DESC(write_threshold_kbs, "Asynchronous write threshold (KB; 30)"); MODULE_PARM(max_buffers, "i"); MODULE_PARM_DESC(max_buffers, "Maximum number of buffer allocated at initialisation (4)"); MODULE_PARM(max_sg_segs, "i"); @@ -103,8 +100,8 @@ { "buffer_kbs", &buffer_kbs }, - { - "write_threshold_kbs", &write_threshold_kbs + { /* Retained for compatibility */ + "write_threshold_kbs", NULL }, { "max_buffers", &max_buffers @@ -122,7 +119,6 @@ /* The default definitions have been moved to st_options.h */ #define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_KILOBYTE) -#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE) /* The buffer size should fit into the 24 bits for length in the 6-byte SCSI read and write commands. */ @@ -153,7 +149,6 @@ static int st_nbr_buffers; static ST_buffer **st_buffers = NULL; static int st_buffer_size = ST_BUFFER_SIZE; -static int st_write_threshold = ST_WRITE_THRESHOLD; static int st_max_buffers = ST_MAX_BUFFERS; static int st_max_sg_segs = ST_MAX_SG; @@ -167,6 +162,7 @@ static int set_sg_lengths(ST_buffer *, unsigned int); static int append_to_buffer(const char *, ST_buffer *, int); static int from_buffer(ST_buffer *, char *, int); +static void move_buffer_data(ST_buffer *, int); static int st_init(void); static int st_attach(Scsi_Device *); @@ -1196,6 +1192,7 @@ ssize_t total; ssize_t i, do_count, blks, transfer; ssize_t retval = 0; + int residual, retry_eot = 0, scode; int write_threshold; int doing_write = 0; unsigned char cmd[MAX_COMMAND_SIZE]; @@ -1367,7 +1364,7 @@ write_threshold = 1; } else write_threshold = (STp->buffer)->buffer_blocks * STp->block_size; - if (!STm->do_async_writes) + if (!STm->do_async_writes || STp->block_size > 0) write_threshold--; total = count; @@ -1381,7 +1378,7 @@ b_point = buf; while ((STp->block_size == 0 && !STm->do_async_writes && count > 0) || (STp->block_size != 0 && - (STp->buffer)->buffer_bytes + count > write_threshold)) { + (STp->buffer)->buffer_bytes + count > write_threshold && !retry_eot)) { doing_write = 1; if (STp->block_size == 0) do_count = count; @@ -1398,6 +1395,7 @@ goto out; } + retry_write: if (STp->block_size == 0) blks = transfer = do_count; else { @@ -1420,46 +1418,66 @@ DEBC(printk(ST_DEB_MSG "st%d: Error on write:\n", dev)); if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && (SRpnt->sr_sense_buffer[2] & 0x40)) { + scode = SRpnt->sr_sense_buffer[2] & 0x0f; if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0) - transfer = (SRpnt->sr_sense_buffer[3] << 24) | + residual = (SRpnt->sr_sense_buffer[3] << 24) | (SRpnt->sr_sense_buffer[4] << 16) | (SRpnt->sr_sense_buffer[5] << 8) | SRpnt->sr_sense_buffer[6]; else if (STp->block_size == 0 && - (SRpnt->sr_sense_buffer[2] & 0x0f) == - VOLUME_OVERFLOW) - transfer = do_count; + scode == VOLUME_OVERFLOW) + residual = do_count; else - transfer = 0; + residual = 0; if (STp->block_size != 0) - transfer *= STp->block_size; - if (transfer <= do_count) { - filp->f_pos += do_count - transfer; - count -= do_count - transfer; + residual *= STp->block_size; + if (residual <= do_count) { + /* Within the data in this write() */ + filp->f_pos += do_count - residual; + count -= do_count - residual; if (STps->drv_block >= 0) { if (STp->block_size == 0 && - transfer < do_count) + residual < do_count) STps->drv_block++; else if (STp->block_size != 0) STps->drv_block += - (do_count - transfer) / + (transfer - residual) / STp->block_size; } STps->eof = ST_EOM_OK; retval = (-ENOSPC); /* EOM within current request */ DEBC(printk(ST_DEB_MSG "st%d: EOM with %d bytes unwritten.\n", - dev, transfer)); + dev, count)); } else { - STps->eof = ST_EOM_ERROR; - STps->drv_block = (-1); /* Too cautious? */ - retval = (-EIO); /* EOM for old data */ - DEBC(printk(ST_DEB_MSG - "st%d: EOM with lost data.\n", - dev)); + /* EOT in within data buffered earlier */ + if (!retry_eot && (SRpnt->sr_sense_buffer[0] & 1) == 0 && + (scode == NO_SENSE || scode == RECOVERED_ERROR)) { + move_buffer_data(STp->buffer, transfer - residual); + retry_eot = TRUE; + if (STps->drv_block >= 0) { + STps->drv_block += (transfer - residual) / + STp->block_size; + } + STps->eof = ST_EOM_OK; + DEBC(printk(ST_DEB_MSG + "st%d: Retry write of %d bytes at EOM.\n", + dev, do_count)); + goto retry_write; + } + else { + /* Either error within data buffered by driver or failed retry */ + STps->eof = ST_EOM_ERROR; + STps->drv_block = (-1); /* Too cautious? */ + retval = (-EIO); /* EOM for old data */ + DEBC(printk(ST_DEB_MSG + "st%d: EOM with lost data.\n", + dev)); + } } } else { STps->drv_block = (-1); /* Too cautious? */ + retry_eot = FALSE; retval = (-EIO); } @@ -1483,7 +1501,7 @@ (STp->buffer)->buffer_bytes = 0; STp->dirty = 0; } - if (count != 0) { + if (count != 0 && !retry_eot) { STp->dirty = 1; i = append_to_buffer(b_point, STp->buffer, count); if (i) { @@ -1499,26 +1517,14 @@ goto out; } - if (STm->do_async_writes && - (((STp->buffer)->buffer_bytes >= STp->write_threshold && - (STp->buffer)->buffer_bytes >= STp->block_size) || - STp->block_size == 0)) { + if (STm->do_async_writes && STp->block_size == 0) { /* Schedule an asynchronous write */ - if (STp->block_size == 0) - (STp->buffer)->writing = (STp->buffer)->buffer_bytes; - else - (STp->buffer)->writing = ((STp->buffer)->buffer_bytes / - STp->block_size) * STp->block_size; - STp->dirty = !((STp->buffer)->writing == - (STp->buffer)->buffer_bytes); - - if (STp->block_size == 0) - blks = (STp->buffer)->writing; - else - blks = (STp->buffer)->writing / STp->block_size; - cmd[2] = blks >> 16; - cmd[3] = blks >> 8; - cmd[4] = blks; + (STp->buffer)->writing = (STp->buffer)->buffer_bytes; + STp->dirty = FALSE; + residual = (STp->buffer)->writing; + cmd[2] = residual >> 16; + cmd[3] = residual >> 8; + cmd[4] = residual; DEB( STp->write_pending = 1; ) SRpnt = st_do_scsi(SRpnt, STp, cmd, (STp->buffer)->writing, @@ -1532,9 +1538,9 @@ } STps->at_sm &= (total == 0); - if (total > 0) + if (total > 0 && !retry_eot) STps->eof = ST_NOEOF; - retval = total; + retval = total - count; out: if (SRpnt != NULL) @@ -1625,9 +1631,14 @@ if (SRpnt->sr_sense_buffer[2] & 0x20) { /* ILI */ if (STp->block_size == 0) { - if (transfer < 0) { + if (transfer <= 0) { + if (transfer < 0) + printk(KERN_NOTICE + "st%d: Failed to read %d byte block with %d byte read.\n", + dev, bytes - transfer, bytes); if (STps->drv_block >= 0) STps->drv_block += 1; + (STp->buffer)->buffer_bytes = 0; return (-ENOMEM); } (STp->buffer)->buffer_bytes = bytes - transfer; @@ -1694,6 +1705,9 @@ } else /* Some other extended sense code */ retval = (-EIO); } + + if ((STp->buffer)->buffer_bytes < 0) /* Caused by bogus sense data */ + (STp->buffer)->buffer_bytes = 0; } /* End of extended sense test */ else { /* Non-extended sense */ @@ -2004,16 +2018,7 @@ debugging = value; ) st_log_options(STp, STm, dev); } else if (code == MT_ST_WRITE_THRESHOLD) { - value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE; - if (value < 1 || value > st_buffer_size) { - printk(KERN_WARNING - "st%d: Write threshold %d too small or too large.\n", - dev, value); - return (-EIO); - } - STp->write_threshold = value; - printk(KERN_INFO "st%d: Write threshold set to %d bytes.\n", - dev, value); + /* Retained for compatibility */ } else if (code == MT_ST_DEF_BLKSIZE) { value = (options & ~MT_ST_OPTIONS); if (value == ~MT_ST_OPTIONS) { @@ -2155,7 +2160,7 @@ /* Send the mode page in the tape buffer to the drive. Assumes that the mode data in the buffer is correctly formatted. */ -static int write_mode_page(Scsi_Tape *STp, int page) +static int write_mode_page(Scsi_Tape *STp, int page, int slow) { int pgo; unsigned char cmd[MAX_COMMAND_SIZE]; @@ -2174,7 +2179,7 @@ (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR; SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, - STp->timeout, 0, TRUE); + (slow ? STp->long_timeout : STp->timeout), 0, TRUE); if (SRpnt == NULL) return (STp->buffer)->syscall_result; @@ -2241,7 +2246,7 @@ b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */ } - retval = write_mode_page(STp, COMPRESSION_PAGE); + retval = write_mode_page(STp, COMPRESSION_PAGE, FALSE); if (retval) { DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev)); return (-EIO); @@ -3055,7 +3060,7 @@ bp[pgo + PP_OFF_RESERVED] = 0; bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | PP_MSK_PSUM_MB; - result = write_mode_page(STp, PART_PAGE); + result = write_mode_page(STp, PART_PAGE, TRUE); if (result) { printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev); result = (-EIO); @@ -3644,6 +3649,50 @@ } +/* Move data towards start of buffer */ +static void move_buffer_data(ST_buffer * st_bp, int offset) +{ + int src_seg, dst_seg, src_offset = 0, dst_offset; + int count, total; + + if (offset == 0) + return; + + total=st_bp->buffer_bytes - offset; + for (src_seg=0; src_seg < st_bp->sg_segs; src_seg++) { + src_offset = offset; + if (src_offset < st_bp->sg_lengths[src_seg]) + break; + offset -= st_bp->sg_lengths[src_seg]; + } + if (src_seg == st_bp->sg_segs) { /* Should never happen */ + printk(KERN_WARNING "st: zap_buffer offset overflow.\n"); + return; + } + + st_bp->buffer_bytes = st_bp->read_pointer = total; + for (dst_seg=dst_offset=0; total > 0; ) { + count = min(st_bp->sg_lengths[dst_seg] - dst_offset, + st_bp->sg_lengths[src_seg] - src_offset); + memmove(st_bp->sg[dst_seg].address + dst_offset, + st_bp->sg[src_seg].address + src_offset, count); + printk("st: move (%d,%d) -> (%d,%d) count %d\n", + src_seg, src_offset, dst_seg, dst_offset, count); + src_offset += count; + if (src_offset >= st_bp->sg_lengths[src_seg]) { + src_seg++; + src_offset = 0; + } + dst_offset += count; + if (dst_offset >= st_bp->sg_lengths[dst_seg]) { + dst_seg++; + dst_offset = 0; + } + total -= count; + } +} + + /* Set the scatter/gather list length fields to sum up to the transfer length. Return the number of segments being used. */ static int set_sg_lengths(ST_buffer *st_bp, unsigned int length) @@ -3668,15 +3717,6 @@ { if (buffer_kbs > 0) st_buffer_size = buffer_kbs * ST_KILOBYTE; - if (write_threshold_kbs > 0) - st_write_threshold = write_threshold_kbs * ST_KILOBYTE; - else if (buffer_kbs > 0) - st_write_threshold = st_buffer_size - 2048; - if (st_write_threshold > st_buffer_size) { - st_write_threshold = st_buffer_size; - printk(KERN_WARNING "st: write_threshold limited to %d bytes.\n", - st_write_threshold); - } if (max_buffers >= 0) st_max_buffers = max_buffers; if (max_sg_segs >= ST_FIRST_SG) @@ -3695,13 +3735,15 @@ if (ints[0] > 0) { for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++) - *parms[i].val = ints[i + 1]; + if (parms[i].val) + *parms[i].val = ints[i + 1]; } else { while (stp != NULL) { for (i = 0; i < ARRAY_SIZE(parms); i++) { len = strlen(parms[i].name); if (!strncmp(stp, parms[i].name, len) && - (*(stp + len) == ':' || *(stp + len) == '=')) { + (*(stp + len) == ':' || *(stp + len) == '=') && + parms[i].val) { *parms[i].val = simple_strtoul(stp + len + 1, NULL, 0); break; @@ -3863,7 +3905,6 @@ tpnt->fast_mteom = ST_FAST_MTEOM; tpnt->scsi2_logical = ST_SCSI2LOGICAL; tpnt->immediate = ST_NOWAIT; - tpnt->write_threshold = st_write_threshold; tpnt->default_drvbuffer = 0xff; /* No forced buffering */ tpnt->partition = 0; tpnt->new_partition = 0; @@ -3941,8 +3982,8 @@ return 0; printk(KERN_INFO - "st: Version %s, bufsize %d, wrt %d, max init. bufs %d, s/g segs %d\n", - verstr, st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs); + "st: Version %s, bufsize %d, max init. bufs %d, s/g segs %d\n", + verstr, st_buffer_size, st_max_buffers, st_max_sg_segs); write_lock_irqsave(&st_dev_arr_lock, flags); if (!st_registered) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/st.h linux.21rc1-ac2/drivers/scsi/st.h --- linux.21rc1/drivers/scsi/st.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/st.h 2003-04-25 16:05:08.000000000 +0100 @@ -86,7 +86,6 @@ unsigned char use_pf; /* Set Page Format bit in all mode selects? */ unsigned char c_algo; /* compression algorithm */ int tape_type; - int write_threshold; int timeout; /* timeout for normal commands */ int long_timeout; /* timeout for commands known to take long time */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/scsi/st_options.h linux.21rc1-ac2/drivers/scsi/st_options.h --- linux.21rc1/drivers/scsi/st_options.h 2003-04-22 16:38:47.000000000 +0100 +++ linux.21rc1-ac2/drivers/scsi/st_options.h 2003-04-22 16:44:37.000000000 +0100 @@ -1,9 +1,9 @@ /* The compile-time configurable defaults for the Linux SCSI tape driver. - Copyright 1995-2002 Kai Makisara. + Copyright 1995-2003 Kai Makisara. - Last modified: Wed May 1 11:06:47 2002 by makisara + Last modified: Sun Apr 6 22:45:15 2003 by makisara */ #ifndef _ST_OPTIONS_H @@ -33,11 +33,6 @@ /* The tape driver buffer size in kilobytes. Must be non-zero. */ #define ST_BUFFER_BLOCKS 32 -/* The number of kilobytes of data in the buffer that triggers an - asynchronous write in fixed block mode. See also ST_ASYNC_WRITES - below. */ -#define ST_WRITE_THRESHOLD_BLOCKS 30 - /* The maximum number of tape buffers the driver tries to allocate at driver initialisation. The number is also constrained by the number of drives detected. If more buffers are needed, they are allocated diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sgi/char/sgiserial.c linux.21rc1-ac2/drivers/sgi/char/sgiserial.c --- linux.21rc1/drivers/sgi/char/sgiserial.c 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/sgi/char/sgiserial.c 2003-04-22 16:44:37.000000000 +0100 @@ -1489,7 +1489,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/ad1848.c linux.21rc1-ac2/drivers/sound/ad1848.c --- linux.21rc1/drivers/sound/ad1848.c 2003-04-22 16:38:48.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/ad1848.c 2003-04-22 16:44:37.000000000 +0100 @@ -2969,6 +2969,10 @@ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 1, 0, 0, 1, 1}, + {"AZT1008", + ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','Z','T'), ISAPNP_FUNCTION(0x0001), + 1, 0, 0, 1, 1}, {0} }; @@ -2981,6 +2985,8 @@ ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 }, { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('A','Z','T'), ISAPNP_FUNCTION(0x0001), 0 }, {0} }; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/ad1889.c linux.21rc1-ac2/drivers/sound/ad1889.c --- linux.21rc1/drivers/sound/ad1889.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/ad1889.c 2003-04-22 16:44:37.000000000 +0100 @@ -1059,7 +1059,7 @@ name: DEVNAME, id_table: ad1889_id_tbl, probe: ad1889_probe, - remove: ad1889_remove, + remove: __devexit_p(ad1889_remove), }; static int __init ad1889_init_module(void) diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/awe_wave.c linux.21rc1-ac2/drivers/sound/awe_wave.c --- linux.21rc1/drivers/sound/awe_wave.c 2003-04-22 16:38:48.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/awe_wave.c 2003-04-22 16:44:37.000000000 +0100 @@ -2051,9 +2051,9 @@ awe_info.nr_voices = awe_max_voices; else awe_info.nr_voices = AWE_MAX_CHANNELS; - memcpy((char*)arg, &awe_info, sizeof(awe_info)); + if(copy_to_user(arg, &awe_info, sizeof(awe_info))) + return -EFAULT; return 0; - break; case SNDCTL_SEQ_RESETSAMPLES: awe_reset(dev); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/cmpci.c linux.21rc1-ac2/drivers/sound/cmpci.c --- linux.21rc1/drivers/sound/cmpci.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/cmpci.c 2003-04-22 16:44:37.000000000 +0100 @@ -588,7 +588,8 @@ unsigned short *src = (unsigned short *)source; do { - data = (unsigned long) *src++; + __get_user(data, src); + src++; data <<= 12; // ok for 16-bit data if (s->spdif_counter == 2 || s->spdif_counter == 3) data |= 0x40000000; // indicate AC-3 raw data @@ -1599,9 +1600,9 @@ return -ENXIO; if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; } + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; ret = 0; while (count > 0) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/via82cxxx_audio.c linux.21rc1-ac2/drivers/sound/via82cxxx_audio.c --- linux.21rc1/drivers/sound/via82cxxx_audio.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/via82cxxx_audio.c 2003-04-23 16:43:10.000000000 +0100 @@ -1345,7 +1345,7 @@ /** * via_chan_flush_frag - Flush partially-full playback buffer to hardware - * @chan: Channel whose DMA table will be displayed + * @chan: Channel whose DMA table will be flushed * * Flushes partially-full playback buffer to hardware. */ @@ -1877,8 +1877,7 @@ chan->bytes = chan->frag_size; /* wake up anyone listening to see when interrupts occur */ - if (waitqueue_active (&chan->wait)) - wake_up_all (&chan->wait); + wake_up_all (&chan->wait); DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", chan->name, status, (long) inl (chan->iobase + 0x04), diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/sound/vidc.c linux.21rc1-ac2/drivers/sound/vidc.c --- linux.21rc1/drivers/sound/vidc.c 2003-04-22 16:38:48.000000000 +0100 +++ linux.21rc1-ac2/drivers/sound/vidc.c 2003-04-22 16:44:37.000000000 +0100 @@ -224,9 +224,11 @@ newsize = 208; if (newsize > 4096) newsize = 4096; - for (new2size = 128; new2size < newsize; new2size <<= 1); - if (new2size - newsize > newsize - (new2size >> 1)) - new2size >>= 1; + for (new2size = 128; new2size < newsize; new2size <<= 1) + /* loop */; + + if (new2size - newsize > newsize - (new2size >> 1)) + new2size >>= 1; if (new2size > 4096) { printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", newsize, new2size); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/tc/zs.c linux.21rc1-ac2/drivers/tc/zs.c --- linux.21rc1/drivers/tc/zs.c 2003-04-22 16:38:53.000000000 +0100 +++ linux.21rc1-ac2/drivers/tc/zs.c 2003-04-22 16:44:37.000000000 +0100 @@ -1374,7 +1374,7 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttyS%02d, count = %d\n", info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((atomic_read(&tty->count) == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/devio.c linux.21rc1-ac2/drivers/usb/devio.c --- linux.21rc1/drivers/usb/devio.c 2003-04-22 16:39:40.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/devio.c 2003-04-22 16:44:37.000000000 +0100 @@ -43,7 +43,7 @@ #include #include #include - +#include struct async { struct list_head asynclist; @@ -1078,6 +1078,8 @@ int size; void *buf = 0; int retval = 0; + struct usb_interface *ifp = 0; + struct usb_driver *driver = 0; /* get input parameters and alloc buffer */ if (copy_from_user(&ctrl, (void *) arg, sizeof (ctrl))) @@ -1095,32 +1097,55 @@ } } - /* ioctl to device */ - if (ctrl.ifno < 0) { - switch (ctrl.ioctl_code) { - /* access/release token for issuing control messages - * ask a particular driver to bind/unbind, ... etc - */ - } - retval = -ENOSYS; - - /* ioctl to the driver which has claimed a given interface */ - } else { - struct usb_interface *ifp = 0; - if (!ps->dev) - retval = -ENODEV; - else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces) + if (!ps->dev) + retval = -ENODEV; + else if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + retval = -EINVAL; + else switch (ctrl.ioctl_code) { + + /* disconnect kernel driver from interface, leaving it unbound */ + case USBDEVFS_DISCONNECT: + driver = ifp->driver; + if (driver) { + down (&driver->serialize); + dbg ("disconnect '%s' from dev %d interface %d", + driver->name, ps->dev->devnum, ctrl.ifno); + driver->disconnect (ps->dev, ifp->private_data); + usb_driver_release_interface (driver, ifp); + up (&driver->serialize); + } else retval = -EINVAL; - else { - if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) - retval = -EINVAL; - else if (ifp->driver == 0 || ifp->driver->ioctl == 0) - retval = -ENOSYS; - } - if (retval == 0) + break; + + /* let kernel drivers try to (re)bind to the interface */ + case USBDEVFS_CONNECT: + usb_find_interface_driver_for_ifnum (ps->dev, ctrl.ifno); + break; + + /* talk directly to the interface's driver */ + default: + lock_kernel(); /* against module unload */ + driver = ifp->driver; + if (driver == 0 || driver->ioctl == 0) { + unlock_kernel(); + retval = -ENOSYS; + } else { + if (ifp->driver->owner) { + __MOD_INC_USE_COUNT(ifp->driver->owner); + unlock_kernel(); + } /* ifno might usefully be passed ... */ - retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf); + retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf); /* size = min_t(int, size, retval)? */ + if (ifp->driver->owner) { + __MOD_DEC_USE_COUNT(ifp->driver->owner); + } else { + unlock_kernel(); + } + } + + if (retval == -ENOIOCTLCMD) + retval = -ENOTTY; } /* cleanup and return */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/hid-core.c linux.21rc1-ac2/drivers/usb/hid-core.c --- linux.21rc1/drivers/usb/hid-core.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/hid-core.c 2003-04-22 16:44:37.000000000 +0100 @@ -1099,6 +1099,9 @@ #define USB_VENDOR_ID_TANGTOP 0x0d3d #define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001 +#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f +#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 + #define USB_VENDOR_ID_OKI 0x070a #define USB_VENDOR_ID_OKI_MULITI 0x0007 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/host/usb-ohci.c linux.21rc1-ac2/drivers/usb/host/usb-ohci.c --- linux.21rc1/drivers/usb/host/usb-ohci.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/host/usb-ohci.c 2003-04-22 18:51:18.000000000 +0100 @@ -490,12 +490,17 @@ usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - urb->complete (urb); + if (urb->interval) { + urb->complete (urb); - /* implicitly requeued */ - urb->actual_length = 0; - urb->status = -EINPROGRESS; - td_submit_urb (urb); + /* implicitly requeued */ + urb->actual_length = 0; + urb->status = -EINPROGRESS; + td_submit_urb (urb); + } else { + urb_rm_priv(urb); + urb->complete (urb); + } break; case PIPE_ISOCHRONOUS: diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/pegasus.c linux.21rc1-ac2/drivers/usb/pegasus.c --- linux.21rc1/drivers/usb/pegasus.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/pegasus.c 2003-04-22 17:04:40.000000000 +0100 @@ -230,7 +230,7 @@ pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; - pegasus->dr.wValue = cpu_to_le16p(&data); + pegasus->dr.wValue = cpu_to_le16(data); pegasus->dr.wIndex = cpu_to_le16p(&indx); pegasus->dr.wLength = cpu_to_le16(1); pegasus->ctrl_urb->transfer_buffer_length = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/storage/unusual_devs.h linux.21rc1-ac2/drivers/usb/storage/unusual_devs.h --- linux.21rc1/drivers/usb/storage/unusual_devs.h 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/storage/unusual_devs.h 2003-04-22 17:37:24.000000000 +0100 @@ -229,6 +229,13 @@ US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ), +/* This entry is needed because the device reports Sub=ff */ +UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0432, + "Sony", + "DSC-F707/U10/U20", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ), + /* Reported by wim@geeks.nl */ UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100, "Sony", @@ -267,6 +274,12 @@ "PEG Mass Storage", US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY ), + +UNUSUAL_DEV( 0x054c, 0x0058, 0x0000, 0x9999, + "Sony", + "PEG-N760C Mass Storage", + US_SC_8070, US_PR_CBI, NULL, + US_FL_FIX_INQUIRY ), UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", @@ -364,6 +377,12 @@ US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY ), +UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, + "Vivitar", + "Vivicam 35Xx", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_START_STOP | US_FL_FIX_INQUIRY | US_FL_MODE_XLATE), + UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100, "TEAC", "Floppy Drive", @@ -435,6 +454,12 @@ US_FL_SINGLE_LUN | US_FL_START_STOP ), #endif +UNUSUAL_DEV( 0x0784, 0x1688, 0x0000, 0x9999, + "Vivitar", + "Vivicam 36xx", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_START_STOP | US_FL_FIX_INQUIRY | US_FL_MODE_XLATE), + #ifdef CONFIG_USB_STORAGE_FREECOM UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999, "Freecom", @@ -518,6 +543,19 @@ US_FL_MODE_XLATE ), #endif +/* Datafab KECF-USB Ver A /Jenoptik Jenreader + * Note: there seem to be two versions of the KECF-USB device. + * The other version (see below) needs only US_FL_INQUIRY, + * whereas this version will not mount without also having + * US_FL_START_STOP. + * Submitted by Chris Clayton (chris@theclaytons.freeserve.co.uk) + */ +UNUSUAL_DEV( 0x07c4, 0xb000, 0x0000, 0xffff, + "Datafab", + "KECF-USB Ver A", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_FIX_INQUIRY | US_FL_START_STOP ), + /* Datafab KECF-USB / Sagatek DCS-CF / Simpletech Flashlink UCF-100 * Only revision 1.13 tested (same for all of the above devices, * based on the Datafab DF-UG-07 chip). Needed for US_FL_FIX_INQUIRY. @@ -594,3 +632,31 @@ US_SC_SCSI, US_PR_SDDR55, NULL, US_FL_SINGLE_LUN), #endif + +/* + * Panasonic/OEMs compact USB CDROMs status + * KXL-840(CD-ROM11): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN + * KXL-RW11(CDRRW02): usb_stor_Bulk_max_lun() is danger, need US_FL_SINGLE_LUN + * KXL-RW20(CDRRW03): original IClass is 0xFF, use US_PR_CB and need init reset + * KXL-RW21(CDRRW06): original IClass is 0xFF, use US_PR_CB and need init reset + * KXL-RW31(CDRRW05): work fine with current code + * KXL-RW32(CDRRW09): work fine with current code + * Checked: Sun Feb 9 JST 2003 Go Taniguchi + */ +UNUSUAL_DEV( 0x04da, 0x0d01, 0x0000, 0xffff, + "MATSHITA", + "CD-ROM11", + US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), +UNUSUAL_DEV( 0x04da, 0x0d02, 0x0000, 0xffff, + "MATSHITA", + "CDRRW02", + US_SC_8020, US_PR_BULK, NULL, US_FL_SINGLE_LUN), +UNUSUAL_DEV( 0x04da, 0x0d03, 0x0000, 0xffff, + "MATSHITA", + "CDRRW03", + US_SC_8020, US_PR_CB, NULL, US_FL_INIT_RESET), +UNUSUAL_DEV( 0x04da, 0x0d06, 0x0000, 0xffff, + "MATSHITA", + "CDRRW06", + US_SC_8020, US_PR_CB, NULL, US_FL_INIT_RESET), + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/storage/usb.c linux.21rc1-ac2/drivers/usb/storage/usb.c --- linux.21rc1/drivers/usb/storage/usb.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/storage/usb.c 2003-04-22 16:44:37.000000000 +0100 @@ -848,7 +848,8 @@ ss->transport_name = "Bulk"; ss->transport = usb_stor_Bulk_transport; ss->transport_reset = usb_stor_Bulk_reset; - ss->max_lun = usb_stor_Bulk_max_lun(ss); + if (!(ss->flags & US_FL_SINGLE_LUN)) + ss->max_lun = usb_stor_Bulk_max_lun(ss); break; #ifdef CONFIG_USB_STORAGE_HP8200e @@ -1027,6 +1028,11 @@ /* now register - our detect function will be called */ ss->htmplt.module = THIS_MODULE; + + /* some device need reset process */ + if (ss->flags & US_FL_INIT_RESET) + ss->transport_reset(ss); + scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt)); /* lock access to the data structures */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/storage/usb.h linux.21rc1-ac2/drivers/usb/storage/usb.h --- linux.21rc1/drivers/usb/storage/usb.h 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/storage/usb.h 2003-04-25 16:05:08.000000000 +0100 @@ -103,6 +103,7 @@ #define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */ #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs fixing */ +#define US_FL_INIT_RESET 0x00000080 /* reset process when initialize */ #define USB_STOR_STRING_LEN 32 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/usb/usb.c linux.21rc1-ac2/drivers/usb/usb.c --- linux.21rc1/drivers/usb/usb.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/usb/usb.c 2003-04-22 17:05:08.000000000 +0100 @@ -164,6 +164,26 @@ } } +/* + * usb_ifnum_to_ifpos - convert the interface _number_ (as in interface.bInterfaceNumber) + * to the interface _position_ (as in dev->actconfig->interface + position) + * @dev: the device to use + * @ifnum: the interface number (bInterfaceNumber); not interface position + * + * Note that the number is the same as the position for all interfaces _except_ + * devices with interfaces not sequentially numbered (e.g., 0, 2, 3, etc). + */ +int usb_ifnum_to_ifpos(struct usb_device *dev, unsigned ifnum) +{ + int i; + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) + if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum) + return i; + + return -EINVAL; +} + /** * usb_deregister - unregister a USB driver * @driver: USB operations of the driver to unregister @@ -546,7 +566,6 @@ iface->private_data = NULL; } - /** * usb_match_id - find first usb_device_id matching device or interface * @dev: the device whose descriptors are considered when matching @@ -759,6 +778,23 @@ return -1; } +/* + * usb_find_interface_driver_for_ifnum - convert ifnum to ifpos via + * usb_ifnum_to_ifpos and call usb_find_interface_driver(). + * @dev: the device to use + * @ifnum: the interface number (bInterfaceNumber); not interface position! + * + * Note usb_find_interface_driver's ifnum parameter is actually interface position. + */ +int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum) +{ + int ifpos = usb_ifnum_to_ifpos(dev, ifnum); + + if (0 > ifpos) + return -EINVAL; + + return usb_find_interface_driver(dev, ifpos); +} #ifdef CONFIG_HOTPLUG @@ -2384,6 +2420,7 @@ * into the kernel, and other device drivers are built as modules, * then these symbols need to be exported for the modules to use. */ +EXPORT_SYMBOL(usb_ifnum_to_ifpos); EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_epnum_to_ep_desc); @@ -2398,6 +2435,7 @@ EXPORT_SYMBOL(usb_free_dev); EXPORT_SYMBOL(usb_inc_dev_use); +EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum); EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_interface_claimed); EXPORT_SYMBOL(usb_driver_release_interface); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/aty/atyfb_base.c linux.21rc1-ac2/drivers/video/aty/atyfb_base.c --- linux.21rc1/drivers/video/aty/atyfb_base.c 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/aty/atyfb_base.c 2003-04-22 16:44:37.000000000 +0100 @@ -360,6 +360,7 @@ /* 3D RAGE Mobility */ { 0x4c4d, 0x4c4d, 0x00, 0x00, m64n_mob_p, 230, 50, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS }, + { 0x4c52, 0x4c52, 0x00, 0x00, m64n_mob_p, 230, 40, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS | M64F_MAGIC_POSTDIV | M64F_SDRAM_MAGIC_PLL | M64F_XL_DLL }, { 0x4c4e, 0x4c4e, 0x00, 0x00, m64n_mob_a, 230, 50, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_MOBIL_BUS }, #endif /* CONFIG_FB_ATY_CT */ }; @@ -438,7 +439,7 @@ #endif /* defined(CONFIG_PPC) */ -#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_PMAC_BACKLIGHT) +#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_CT_VAIO_LCD) static void aty_st_lcd(int index, u32 val, const struct fb_info_aty *info) { unsigned long temp; @@ -460,7 +461,7 @@ /* read the register value */ return aty_ld_le32(LCD_DATA, info); } -#endif /* CONFIG_PMAC_PBOOK || CONFIG_PMAC_BACKLIGHT */ +#endif /* CONFIG_PMAC_PBOOK || CONFIG_PMAC_BACKLIGHT || CONFIG_FB_ATY_CT_VAIO_LCD */ /* ------------------------------------------------------------------------- */ @@ -1772,6 +1773,9 @@ #if defined(CONFIG_PPC) int sense; #endif +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + u32 pm, hs; +#endif u8 pll_ref_div; info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0); @@ -2089,6 +2093,35 @@ var = default_var; #endif /* !__sparc__ */ #endif /* !CONFIG_PPC */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + /* Power Management */ + pm=aty_ld_lcd(POWER_MANAGEMENT, info); + pm=(pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_PCI; + pm|=PWR_MGT_ON; + aty_st_lcd(POWER_MANAGEMENT, pm, info); + udelay(10); + + /* OVR_WID_LEFT_RIGHT */ + hs=aty_ld_le32(OVR_WID_LEFT_RIGHT,info); + hs= 0x00000000; + aty_st_le32(OVR_WID_LEFT_RIGHT, hs, info); + udelay(10); + + /* CONFIG_PANEL */ + hs=aty_ld_lcd(CONFIG_PANEL,info); + hs|=DONT_SHADOW_HEND ; + aty_st_lcd(CONFIG_PANEL, hs, info); + udelay(10); + +#if defined(DEBUG) + printk("LCD_INDEX CONFIG_PANEL LCD_GEN_CTRL POWER_MANAGEMENT\n" + "%08x %08x %08x %08x\n", + aty_ld_le32(LCD_INDEX, info), + aty_ld_lcd(CONFIG_PANEL, info), + aty_ld_lcd(LCD_GEN_CTRL, info), + aty_ld_lcd(POWER_MANAGEMENT, info), +#endif /* DEBUG */ +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ #endif /* !MODULE */ if (noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; @@ -2714,6 +2747,23 @@ /* * Blank the display. */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) +static int set_backlight_enable(int on, struct fb_info_aty *info) +{ + unsigned int reg = aty_ld_lcd(POWER_MANAGEMENT, info); + if(on) { + reg=(reg & ~SUSPEND_NOW) | PWR_BLON; + } else { + reg=(reg & ~PWR_BLON) | SUSPEND_NOW; + } + aty_st_lcd(POWER_MANAGEMENT, reg, info); + udelay(10); +#ifdef DEBUG + printk(KERN_INFO "set_backlight_enable(%i): %08x\n", on, aty_ld_lcd(POWER_MANAGEMENT, info) ); +#endif + return 0; +} +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ static void atyfbcon_blank(int blank, struct fb_info *fb) { @@ -2725,6 +2775,9 @@ set_backlight_enable(0); #endif /* CONFIG_PMAC_BACKLIGHT */ +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + set_backlight_enable(!blank, info); +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ gen_cntl = aty_ld_8(CRTC_GEN_CNTL, info); if (blank > 0) switch (blank-1) { diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/aty/mach64_ct.c linux.21rc1-ac2/drivers/video/aty/mach64_ct.c --- linux.21rc1/drivers/video/aty/mach64_ct.c 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/aty/mach64_ct.c 2003-04-22 16:44:37.000000000 +0100 @@ -178,11 +178,14 @@ } pll->pll_gen_cntl |= mpostdiv<<4; /* mclk */ - if (M64_HAS(MAGIC_POSTDIV)) - pll->pll_ext_cntl = 0; - else +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */ - +#else + if ( M64_HAS(MAGIC_POSTDIV) ) + pll->pll_ext_cntl = 0; + else + pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */ +#endif switch (pll->vclk_post_div_real) { case 2: vpostdiv = 1; diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/aty/mach64.h linux.21rc1-ac2/drivers/video/aty/mach64.h --- linux.21rc1/drivers/video/aty/mach64.h 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/aty/mach64.h 2003-04-22 16:44:37.000000000 +0100 @@ -1148,6 +1148,8 @@ #define APC_LUT_MN 0x39 #define APC_LUT_OP 0x3A +/* Values in CONFIG_PANEL */ +#define DONT_SHADOW_HEND 0x00004000 /* Values in LCD_MISC_CNTL */ #define BIAS_MOD_LEVEL_MASK 0x0000ff00 diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/Config.in linux.21rc1-ac2/drivers/video/Config.in --- linux.21rc1/drivers/video/Config.in 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/Config.in 2003-04-22 16:44:37.000000000 +0100 @@ -144,13 +144,20 @@ if [ "$CONFIG_FB_ATY" != "n" ]; then bool ' Mach64 GX support (EXPERIMENTAL)' CONFIG_FB_ATY_GX bool ' Mach64 CT/VT/GT/LT (incl. 3D RAGE) support' CONFIG_FB_ATY_CT + if [ "$CONFIG_FB_ATY_CT" = "y" ]; then + bool ' Sony Vaio C1VE 1024x480 LCD support' CONFIG_FB_ATY_CT_VAIO_LCD + fi fi + tristate ' Intel 830M/845G/852GM/855GM/865G display support (EXPERIMENTAL)' CONFIG_FB_INTEL tristate ' ATI Radeon display support (EXPERIMENTAL)' CONFIG_FB_RADEON +# if [ "$CONFIG_FB_RADEON" = "y" ]; then +# bool ' Sony Vaio C1MV 1280x600 LCD support' CONFIG_FB_RADEON_VAIO_LCD +# fi tristate ' ATI Rage128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 tristate ' SIS acceleration (EXPERIMENTAL)' CONFIG_FB_SIS if [ "$CONFIG_FB_SIS" != "n" ]; then - bool ' SIS 630/540/730 support' CONFIG_FB_SIS_300 - bool ' SIS 315H/315 support' CONFIG_FB_SIS_315 + bool ' SIS 300/305/540/630/730 support' CONFIG_FB_SIS_300 + bool ' SIS 315/650/M650/651/740/Xabre support' CONFIG_FB_SIS_315 fi tristate ' NeoMagic display support (EXPERIMENTAL)' CONFIG_FB_NEOMAGIC tristate ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX @@ -293,6 +300,7 @@ "$CONFIG_FB_PM3" = "y" -o "$CONFIG_FB_TRIDENT" = "y" -o \ "$CONFIG_FB_P9100" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ + "$CONFIG_FB_INTEL" = "y" -o \ "$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ "$CONFIG_FB_PMAG_BA" = "y" -o "$CONFIG_FB_PMAGB_B" = "y" -o \ @@ -312,12 +320,13 @@ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "y" -o \ + "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "m" -o \ "$CONFIG_FB_P9100" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_3DFX" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \ "$CONFIG_FB_PMAG_BA" = "m" -o "$CONFIG_FB_PMAGB_B" = "m" -o \ "$CONFIG_FB_MAXINE" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ + "$CONFIG_FB_INTEL" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_NEOMAGIC" = "m" -o \ "$CONFIG_FB_STI" = "m" ]; then @@ -328,6 +337,7 @@ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" -o \ "$CONFIG_FB_Q40" = "y" -o "$CONFIG_FB_RADEON" = "y" -o \ + "$CONFIG_FB_INTEL" = "y" -o \ "$CONFIG_FB_CONTROL" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ @@ -350,10 +360,11 @@ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ "$CONFIG_FB_PM2" = "m" -o "$CONFIG_FB_SGIVW" = "m" -o \ - "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "y" -o \ + "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ + "$CONFIG_FB_INTEL" = "m" -o \ "$CONFIG_FB_PVR2" = "m" -o "$CONFIG_FB_VOODOO1" = "m" -o \ "$CONFIG_FB_NEOMAGIC" = "m" ]; then define_tristate CONFIG_FBCON_CFB16 m @@ -385,6 +396,7 @@ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \ "$CONFIG_FB_FM2" = "y" -o "$CONFIG_FB_SGIVW" = "y" -o \ "$CONFIG_FB_RADEON" = "y" -o "$CONFIG_FB_PVR2" = "y" -o \ + "$CONFIG_FB_INTEL" = "y" -o \ "$CONFIG_FB_3DFX" = "y" -o "$CONFIG_FB_SIS" = "y" -o \ "$CONFIG_FB_VOODOO1" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ "$CONFIG_FB_STI" = "y" ]; then @@ -395,12 +407,13 @@ "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ - "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "y" -o \ + "$CONFIG_FB_PM3" = "m" -o "$CONFIG_FB_TRIDENT" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ "$CONFIG_FB_3DFX" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ + "$CONFIG_FB_INTEL" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ "$CONFIG_FB_PVR2" = "m" -o "$CONFIG_FB_VOODOO1" = "m" -o \ - "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_STI" = "y" ]; then + "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_STI" = "m" ]; then define_tristate CONFIG_FBCON_CFB32 m fi fi @@ -447,9 +460,9 @@ define_tristate CONFIG_FBCON_HGA m fi fi - if [ "$CONFIG_FB_STI" = "y" ]; then - define_tristate CONFIG_FBCON_STI y - fi + fi + if [ "$CONFIG_FB_STI" = "y" ]; then + define_tristate CONFIG_FBCON_STI y fi bool ' Support only 8 pixels wide fonts' CONFIG_FBCON_FONTWIDTH8_ONLY if [ "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" ]; then diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/modedb.c linux.21rc1-ac2/drivers/video/modedb.c --- linux.21rc1/drivers/video/modedb.c 2003-04-22 16:39:41.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/modedb.c 2003-04-22 17:04:40.000000000 +0100 @@ -43,6 +43,20 @@ #define DEFAULT_MODEDB_INDEX 0 static struct fb_videomode modedb[] __initdata = { +#if defined(CONFIG_FB_ATY_CT_VAIO_LCD) + { + /* 1024x480 @ 65 Hz */ + NULL, 65, 1024, 480, 25203, 24, 24, 1, 17, 144, 4, + 0, FB_VMODE_NONINTERLACED + }, +#endif /* CONFIG_FB_ATY_CT_VAIO_LCD */ +#if defined(CONFIG_FB_RADEON_VAIO_LCD) + { + /* 1280x600 @ 72 Hz, 45.288 kHz hsync */ + NULL, 72, 1280, 600, 13940, 24, 24, 23, 1, 256, 5, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED + }, +#endif /* CONFIG_FB_RADEON_VAIO_LCD */ { /* 640x400 @ 70 Hz, 31.5 kHz hsync */ NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2, diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/sis/300vtbl.h linux.21rc1-ac2/drivers/video/sis/300vtbl.h --- linux.21rc1/drivers/video/sis/300vtbl.h 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/sis/300vtbl.h 2003-04-22 16:44:37.000000000 +0100 @@ -15,7 +15,7 @@ UCHAR VB_StTVYFilterIndex; } SiS300_StStruct; -static const SiS300_StStruct SiS300_SModeIDTable[]= +static const SiS300_StStruct SiS300_SModeIDTable[] = { {0x01,0x9208,0x01,0x00,0x00,0x00,0x00,0x00}, {0x01,0x1210,0x14,0x01,0x01,0x00,0x00,0x00}, @@ -34,10 +34,9 @@ {0x11,0x0212,0x1a,0x04,0x04,0x00,0x00,0x00}, {0x12,0x0212,0x1b,0x04,0x04,0x00,0x00,0x00}, {0x13,0x021b,0x1c,0x00,0x00,0x00,0x00,0x00}, - /* {0x12,0x0210,0x18,0x00,0x00,0x00,0x00,0x00}, */ /* <--- Different in BIOS */ {0x12,0x0010,0x18,0x02,0x02,0x00,0x00,0x00}, {0x12,0x0210,0x18,0x01,0x01,0x00,0x00,0x00}, - {0xff,0,0,0,0,0,0,0} + {0xff, 0, 0, 0, 0, 0, 0, 0} }; typedef struct _SiS300_StandTableStruct @@ -53,9 +52,9 @@ UCHAR GRC[9]; } SiS300_StandTableStruct; -static const SiS300_StandTableStruct SiS300_StandTable[]= -{ /* TW: @ 0x38d4 in BIOS */ - {0x28,0x18,0x08,0x0800, +static const SiS300_StandTableStruct SiS300_StandTable[] = +{ + {0x28,0x18,0x08,0x0800, /* 0x00 */ {0x09,0x03,0x00,0x02}, 0x63, {0x2d,0x27,0x28,0x90,0x2b,0xa0,0xbf,0x1f, @@ -67,7 +66,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x28,0x18,0x08,0x0800, + {0x28,0x18,0x08,0x0800, /* 0x01 */ {0x09,0x03,0x00,0x02}, 0x63, {0x2d,0x27,0x28,0x90,0x2b,0xa0,0xbf,0x1f, @@ -79,7 +78,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x08,0x1000, + {0x50,0x18,0x08,0x1000, /* 0x02 */ {0x01,0x03,0x00,0x02}, 0x63, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -91,7 +90,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x08,0x1000, + {0x50,0x18,0x08,0x1000, /* 0x03 */ {0x01,0x03,0x00,0x02}, 0x63, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -103,7 +102,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x28,0x18,0x08,0x4000, + {0x28,0x18,0x08,0x4000, /* 0x04 */ {0x09,0x03,0x00,0x02}, 0x63, {0x2d,0x27,0x28,0x90,0x2b,0x80,0xbf,0x1f, @@ -115,7 +114,7 @@ 0x01,0x00,0x03,0x00}, {0x00,0x00,0x00,0x00,0x00,0x30,0x0f,0x00, 0xff} }, - {0x28,0x18,0x08,0x4000, + {0x28,0x18,0x08,0x4000, /* 0x05 */ {0x09,0x03,0x00,0x02}, 0x63, {0x2d,0x27,0x28,0x90,0x2b,0x80,0xbf,0x1f, @@ -127,7 +126,7 @@ 0x01,0x00,0x03,0x00}, {0x00,0x00,0x00,0x00,0x00,0x30,0x0f,0x00, 0xff} }, - {0x50,0x18,0x08,0x4000, + {0x50,0x18,0x08,0x4000, /* 0x06 */ {0x01,0x01,0x00,0x06}, 0x63, {0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f, @@ -139,7 +138,7 @@ 0x01,0x00,0x01,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00, 0xff} }, - {0x50,0x18,0x0e,0x1000, + {0x50,0x18,0x0e,0x1000, /* 0x07 */ {0x00,0x03,0x00,0x03}, 0xa6, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -152,7 +151,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0a,0x00, 0xff} }, /* MDA_DAC*/ - {0x00,0x00,0x00,0x0000, + {0x00,0x00,0x00,0x0000, /* 0x08 */ {0x00,0x00,0x00,0x15}, 0x15, {0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, @@ -165,7 +164,7 @@ {0x15,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f} }, /* CGA_DAC*/ - {0x00,0x10,0x04,0x0114, + {0x00,0x10,0x04,0x0114, /* 0x09 */ {0x11,0x09,0x15,0x00}, 0x10, {0x04,0x14,0x01,0x11,0x09,0x15,0x2a,0x3a, @@ -178,7 +177,7 @@ {0x3f,0x2a,0x3a,0x2e,0x3e,0x2b,0x3b,0x2f, 0x3f} }, /* EGA_DAC*/ - {0x00,0x10,0x04,0x0114, + {0x00,0x10,0x04,0x0114, /* 0x0a */ {0x11,0x05,0x15,0x20}, 0x30, {0x24,0x34,0x21,0x31,0x25,0x35,0x08,0x18, @@ -191,7 +190,7 @@ {0x1f,0x2a,0x3a,0x2e,0x3e,0x2b,0x3b,0x2f, 0x3f} }, /* VGA_DAC*/ - {0x00,0x10,0x04,0x0114, + {0x00,0x10,0x04,0x0114, /* 0x0b */ {0x11,0x09,0x15,0x2a}, 0x3a, {0x2e,0x3e,0x2b,0x3b,0x2f,0x3f,0x00,0x05, @@ -203,7 +202,7 @@ 0x1c,0x0e,0x11,0x15}, {0x18,0x1c,0x14,0x16,0x18,0x1a,0x1c,0x00, 0x04} }, - {0x08,0x0c,0x10,0x0a08, + {0x08,0x0c,0x10,0x0a08, /* 0x0c */ {0x0c,0x0e,0x10,0x0b}, 0x0c, {0x0d,0x0f,0x10,0x10,0x01,0x08,0x00,0x00, @@ -215,7 +214,7 @@ 0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00} }, - {0x28,0x18,0x08,0x2000, + {0x28,0x18,0x08,0x2000, /* 0x0d */ {0x09,0x0f,0x00,0x06}, 0x63, {0x2d,0x27,0x28,0x90,0x2b,0x80,0xbf,0x1f, @@ -227,7 +226,7 @@ 0x01,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, - {0x50,0x18,0x08,0x4000, + {0x50,0x18,0x08,0x4000, /* 0x0e */ {0x01,0x0f,0x00,0x06}, 0x63, {0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f, @@ -239,8 +238,8 @@ 0x01,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, - {0x00,0x00,0x00,0x0000, /* TW: Standtable for VGA modes */ - {0x01,0x0f,0x00,0x0e}, /* (identical to BIOS) */ + {0x00,0x00,0x00,0x0000, /* 0x0f */ /* TW: Standtable for VGA modes */ + {0x01,0x0f,0x00,0x0e}, 0x23, {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, @@ -251,7 +250,7 @@ 0x01,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, 0xff} }, - {0x4a,0x36,0x00,0x00c0, + {0x4a,0x36,0x00,0x00c0, /* 0x10 */ {0x00,0x00,0x00,0x00}, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x3a, @@ -263,7 +262,7 @@ 0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00} }, - {0x50,0x18,0x0e,0x8000, + {0x50,0x18,0x0e,0x8000, /* 0x11 */ {0x01,0x0f,0x00,0x06}, 0xa2, {0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f, @@ -275,7 +274,7 @@ 0x0b,0x00,0x05,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05, 0xff} }, - {0x50,0x18,0x0e,0x8000, + {0x50,0x18,0x0e,0x8000, /* 0x12 */ {0x01,0x0f,0x00,0x06}, 0xa3, {0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f, @@ -287,7 +286,7 @@ 0x01,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, - {0x28,0x18,0x0e,0x0800, + {0x28,0x18,0x0e,0x0800, /* 0x13 */ {0x09,0x03,0x00,0x02}, 0xa3, {0x2d,0x27,0x28,0x90,0x2b,0xa0,0xbf,0x1f, @@ -299,7 +298,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x28,0x18,0x0e,0x0800, + {0x28,0x18,0x0e,0x0800, /* 0x14 */ {0x09,0x03,0x00,0x02}, 0xa3, {0x2d,0x27,0x28,0x90,0x2b,0xa0,0xbf,0x1f, @@ -311,7 +310,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x0e,0x1000, + {0x50,0x18,0x0e,0x1000, /* 0x15 */ {0x01,0x03,0x00,0x02}, 0xa3, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -323,7 +322,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x0e,0x1000, + {0x50,0x18,0x0e,0x1000, /* 0x16 */ {0x01,0x03,0x00,0x02}, 0xa3, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -335,7 +334,7 @@ 0x08,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x28,0x18,0x10,0x0800, + {0x28,0x18,0x10,0x0800, /* 0x17 */ {0x08,0x03,0x00,0x02}, 0x67, {0x2d,0x27,0x28,0x90,0x2b,0xa0,0xbf,0x1f, @@ -347,7 +346,7 @@ 0x0c,0x00,0x0f,0x08}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x10,0x1000, + {0x50,0x18,0x10,0x1000, /* 0x18 */ {0x00,0x03,0x00,0x02}, 0x67, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -359,7 +358,7 @@ 0x0c,0x00,0x0f,0x08}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, - {0x50,0x18,0x10,0x1000, + {0x50,0x18,0x10,0x1000, /* 0x19 */ {0x00,0x03,0x00,0x02}, 0x66, {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, @@ -371,7 +370,7 @@ 0x0e,0x00,0x0f,0x08}, {0x00,0x00,0x00,0x00,0x00,0x10,0x0a,0x00, 0xff} }, - {0x50,0x1d,0x10,0xa000, + {0x50,0x1d,0x10,0xa000, /* 0x1a */ {0x01,0x0f,0x00,0x06}, 0xe3, {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, @@ -383,7 +382,7 @@ 0x01,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x01, 0xff} }, - {0x50,0x1d,0x10,0xa000, + {0x50,0x1d,0x10,0xa000, /* 0x1b */ {0x01,0x0f,0x00,0x06}, 0xe3, {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, @@ -395,7 +394,7 @@ 0x01,0x00,0x0f,0x00}, {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, - {0x28,0x18,0x08,0x2000, + {0x28,0x18,0x08,0x2000, /* 0x1c */ {0x01,0x0f,0x00,0x0e}, 0x63, {0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f, @@ -424,61 +423,88 @@ UCHAR REFindex; } SiS300_ExtStruct; -static const SiS300_ExtStruct SiS300_EModeIDTable[]= +static const SiS300_ExtStruct SiS300_EModeIDTable[] = { - {0x6a,0x2212,0x47,0x3563,0x0102,0x08,0x07,0x00,0x00,0x00,0x00}, /* 37ed */ /* 800x600x? */ - {0x2e,0x0a1b,0x36,0x3539,0x0101,0x08,0x06,0x00,0x00,0x00,0x08}, /* 37c3 */ - {0x2f,0x021b,0x35,0x3532,0x0100,0x08,0x05,0x00,0x00,0x00,0x10}, /* 37bc */ - {0x30,0x2a1b,0x47,0x3563,0x0103,0x08,0x07,0x00,0x00,0x00,0x00}, /* 37ed */ - {0x31,0x0a1b,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 38ba */ /* 720x480x8 */ - {0x32,0x2a1b,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 38c1 */ /* 720x576x8 */ - {0x33,0x0a1d,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 38ba */ /* 720x480x16 */ - {0x34,0x2a1d,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 38c1 */ /* 720x576x16 */ - {0x35,0x0a1f,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 38ba */ /* 720x480x32 */ - {0x36,0x2a1f,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 38c1 */ /* 720x576x32 */ - {0x37,0x0212,0x58,0x358d,0x0104,0x08,0x08,0x00,0x00,0x00,0x13}, /* 3817 */ /* 1024x768x? */ - {0x38,0x0a1b,0x58,0x358d,0x0105,0x08,0x08,0x00,0x00,0x00,0x13}, /* 3817 */ /* 1024x768x8 */ - {0x3a,0x0e3b,0x69,0x35be,0x0107,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 3848 */ /* 1280x1024x8 */ - {0x3c,0x063b,0x7a,0x35d4,0x0130,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 385e */ - {0x3d,0x067d,0x7a,0x35d4,0x0131,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 385e */ - {0x40,0x921c,0x00,0x3516,0x010d,0x08,0x00,0x00,0x00,0x00,0x23}, /* 37a0 */ - {0x41,0x921d,0x00,0x3516,0x010e,0x08,0x00,0x00,0x00,0x00,0x23}, /* 37a0 */ - {0x43,0x0a1c,0x36,0x3539,0x0110,0x08,0x06,0x00,0x00,0x00,0x08}, /* 37c3 */ - {0x44,0x0a1d,0x36,0x3539,0x0111,0x08,0x06,0x00,0x00,0x00,0x08}, /* 37c3 */ - {0x46,0x2a1c,0x47,0x3563,0x0113,0x08,0x07,0x00,0x00,0x00,0x00}, /* 37ed */ /* 800x600 */ - {0x47,0x2a1d,0x47,0x3563,0x0114,0x08,0x07,0x00,0x00,0x00,0x00}, /* 37ed */ /* 800x600 */ - {0x49,0x0a3c,0x58,0x358d,0x0116,0x08,0x08,0x00,0x00,0x00,0x13}, /* 3817 */ - {0x4a,0x0a3d,0x58,0x358d,0x0117,0x08,0x08,0x00,0x00,0x00,0x13}, /* 3817 */ - {0x4c,0x0e7c,0x69,0x35be,0x0119,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 3848 */ - {0x4d,0x0e7d,0x69,0x35be,0x011a,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 3848 */ - {0x50,0x921b,0x01,0x351d,0x0132,0x08,0x01,0x00,0x00,0x00,0x24}, /* 37a7 */ - {0x51,0xb21b,0x13,0x3524,0x0133,0x08,0x03,0x00,0x00,0x00,0x25}, /* 37ae */ /* 400x300 */ - {0x52,0x921b,0x24,0x352b,0x0134,0x08,0x04,0x00,0x00,0x00,0x26}, /* 37b5 */ - {0x56,0x921d,0x01,0x351d,0x0135,0x08,0x01,0x00,0x00,0x00,0x24}, /* 37a7 */ - {0x57,0xb21d,0x13,0x3524,0x0136,0x08,0x03,0x00,0x00,0x00,0x25}, /* 37ae */ /* 400x300 */ - {0x58,0x921d,0x24,0x352b,0x0137,0x08,0x04,0x00,0x00,0x00,0x26}, /* 37b5 */ - {0x59,0x921b,0x00,0x3516,0x0138,0x08,0x00,0x00,0x00,0x00,0x23}, /* 37a0 */ + {0x6a,0x2212,0x47,0x3563,0x0102,0x08,0x07,0x00,0x00,0x00,0x00}, /* 800x600x? */ + {0x2e,0x0a1b,0x36,0x3539,0x0101,0x08,0x06,0x00,0x00,0x00,0x08}, + {0x2f,0x021b,0x35,0x3532,0x0100,0x08,0x05,0x00,0x00,0x00,0x10}, /* 640x400x8 */ + {0x30,0x2a1b,0x47,0x3563,0x0103,0x08,0x07,0x00,0x00,0x00,0x00}, + {0x31,0x0a1b,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 720x480x8 */ + {0x32,0x2a1b,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 720x576x8 */ + {0x33,0x0a1d,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 720x480x16 */ + {0x34,0x2a1d,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 720x576x16 */ + {0x35,0x0a1f,0xad,0x3630,0x0000,0x08,0x0c,0x00,0x00,0x00,0x11}, /* 720x480x32 */ + {0x36,0x2a1f,0xae,0x3637,0x0000,0x08,0x0d,0x00,0x00,0x00,0x12}, /* 720x576x32 */ + {0x37,0x0212,0x58,0x358d,0x0104,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x? */ + {0x38,0x0a1b,0x58,0x358d,0x0105,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x8 */ + {0x3a,0x0e3b,0x69,0x35be,0x0107,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 1280x1024x8 */ + {0x3c,0x063b,0x7a,0x35d4,0x0130,0x08,0x0a,0x00,0x00,0x00,0x1e}, + {0x3d,0x067d,0x7a,0x35d4,0x0131,0x08,0x0a,0x00,0x00,0x00,0x1e}, + {0x40,0x921c,0x00,0x3516,0x010d,0x08,0x00,0x00,0x00,0x00,0x23}, /* 320x200x15 */ + {0x41,0x921d,0x00,0x3516,0x010e,0x08,0x00,0x00,0x00,0x00,0x23}, /* 320x200x16 */ + {0x43,0x0a1c,0x36,0x3539,0x0110,0x08,0x06,0x00,0x00,0x00,0x08}, + {0x44,0x0a1d,0x36,0x3539,0x0111,0x08,0x06,0x00,0x00,0x00,0x08}, + {0x46,0x2a1c,0x47,0x3563,0x0113,0x08,0x07,0x00,0x00,0x00,0x00}, /* 800x600x15 */ + {0x47,0x2a1d,0x47,0x3563,0x0114,0x08,0x07,0x00,0x00,0x00,0x00}, /* 800x600x16 */ + {0x49,0x0a3c,0x58,0x358d,0x0116,0x08,0x08,0x00,0x00,0x00,0x13}, + {0x4a,0x0a3d,0x58,0x358d,0x0117,0x08,0x08,0x00,0x00,0x00,0x13}, + {0x4c,0x0e7c,0x69,0x35be,0x0119,0x08,0x09,0x00,0x00,0x00,0x1a}, + {0x4d,0x0e7d,0x69,0x35be,0x011a,0x08,0x09,0x00,0x00,0x00,0x1a}, + {0x50,0x921b,0x01,0x351d,0x0132,0x08,0x01,0x00,0x00,0x00,0x24}, /* 320x240x8 */ + {0x51,0xb21b,0x13,0x3524,0x0133,0x08,0x03,0x00,0x00,0x00,0x25}, /* 400x300x8 */ + {0x52,0x921b,0x24,0x352b,0x0134,0x08,0x04,0x00,0x00,0x00,0x26}, /* 512x384x8 */ + {0x56,0x921d,0x01,0x351d,0x0135,0x08,0x01,0x00,0x00,0x00,0x24}, /* 320x240x16 */ + {0x57,0xb21d,0x13,0x3524,0x0136,0x08,0x03,0x00,0x00,0x00,0x25}, /* 400x300x16 */ + {0x58,0x921d,0x24,0x352b,0x0137,0x08,0x04,0x00,0x00,0x00,0x26}, /* 512x384x16 */ + {0x59,0x921b,0x00,0x3516,0x0138,0x08,0x00,0x00,0x00,0x00,0x23}, /* 320x200x8 */ {0x5c,0x921f,0x24,0x352b,0x0000,0x08,0x04,0x00,0x00,0x00,0x26}, /* TW: inserted 512x384x32 */ - {0x5d,0x021d,0x35,0x3532,0x0139,0x08,0x05,0x00,0x00,0x00,0x10}, /* 37bc */ - {0x62,0x0a3f,0x36,0x3539,0x013a,0x08,0x06,0x00,0x00,0x00,0x08}, /* 37c3 */ - {0x63,0x2a3f,0x47,0x3563,0x013b,0x08,0x07,0x00,0x00,0x00,0x00}, /* 37ed */ /* 800x600 */ - {0x64,0x0a7f,0x58,0x358d,0x013c,0x08,0x08,0x00,0x00,0x00,0x13}, /* 3817 */ - {0x65,0x0eff,0x69,0x35be,0x013d,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 3848 */ - {0x66,0x06ff,0x7a,0x35d4,0x013e,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 385e */ - {0x68,0x067b,0x8b,0x35ef,0x013f,0x08,0x0b,0x00,0x00,0x00,0x27}, /* 3879 */ - {0x69,0x06fd,0x8b,0x35ef,0x0140,0x08,0x0b,0x00,0x00,0x00,0x27}, /* 3879 */ - {0x6b,0x07ff,0x8b,0x35ef,0x0000,0x10,0x0b,0x00,0x00,0x00,0x27}, /* 3879 */ - {0x6c,0x067b,0x9c,0x35f6,0x0000,0x08,0x0c,0x00,0x00,0x00,0x28}, /* 3880 */ - {0x6d,0x06fd,0x9c,0x35f6,0x0000,0x10,0x0c,0x00,0x00,0x00,0x28}, /* 3880 */ - {0x6e,0x0e3b,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 383c */ /* 1280x960x8 */ - {0x6f,0x0e7d,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 383c */ /* 1280x960x16 */ - {0x7b,0x0eff,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 383c */ /* 1280x960x32 */ - {0x20,0x0a1b,0x54,0x0000,0x0000,0x08,0x0f,0x00,0x00,0x00,0x2b}, /* 1024x600 */ + {0x5d,0x021d,0x35,0x3532,0x0139,0x08,0x05,0x00,0x00,0x00,0x10}, /* 640x400x16 */ + {0x5e,0x021f,0x35,0x3532,0x0000,0x08,0x05,0x00,0x00,0x00,0x10}, /* TW: inserted 640x400x32 */ + {0x62,0x0a3f,0x36,0x3539,0x013a,0x08,0x06,0x00,0x00,0x00,0x08}, + {0x63,0x2a3f,0x47,0x3563,0x013b,0x08,0x07,0x00,0x00,0x00,0x00}, /* 800x600x32 */ + {0x64,0x0a7f,0x58,0x358d,0x013c,0x08,0x08,0x00,0x00,0x00,0x13}, + {0x65,0x0eff,0x69,0x35be,0x013d,0x08,0x09,0x00,0x00,0x00,0x1a}, + {0x66,0x06ff,0x7a,0x35d4,0x013e,0x08,0x0a,0x00,0x00,0x00,0x1e}, + {0x68,0x067b,0x8b,0x35ef,0x013f,0x08,0x0b,0x00,0x00,0x00,0x27}, + {0x69,0x06fd,0x8b,0x35ef,0x0140,0x08,0x0b,0x00,0x00,0x00,0x27}, + {0x6b,0x07ff,0x8b,0x35ef,0x0000,0x10,0x0b,0x00,0x00,0x00,0x27}, + {0x6c,0x067b,0x9c,0x35f6,0x0000,0x08,0x11,0x00,0x00,0x00,0x28}, /* TW: 2048x1536x8 - not in BIOS! */ + {0x6d,0x06fd,0x9c,0x35f6,0x0000,0x10,0x11,0x00,0x00,0x00,0x28}, /* TW: 2048x1536x16 - not in BIOS! */ + {0x6e,0x0a3b,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 1280x960x8 */ + {0x6f,0x0a7d,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 1280x960x16 */ + /* TW: 16:9 modes - not in ANY BIOS */ + {0x70,0x2a1b,0x40,0x3b52,0x0000,0x08,0x12,0x00,0x00,0x07,0x2d}, /* 800x480x8 */ + {0x71,0x0a1b,0x51,0x3b63,0x0000,0x08,0x13,0x00,0x00,0x00,0x30}, /* 1024x576x8 */ + {0x74,0x0a1d,0x51,0x3b63,0x0000,0x08,0x13,0x00,0x00,0x00,0x30}, /* 1024x576x16 */ + {0x75,0x0e3d,0x62,0x3b74,0x0000,0x08,0x14,0x00,0x00,0x00,0x33}, /* 1280x720x16 */ + {0x76,0x2a1f,0x40,0x3b52,0x0000,0x08,0x12,0x00,0x00,0x07,0x2d}, /* 800x480x32 */ + {0x77,0x0a3f,0x51,0x3b63,0x0000,0x08,0x13,0x00,0x00,0x00,0x30}, /* 1024x576x32 */ + {0x78,0x0eff,0x62,0x3b74,0x0000,0x08,0x14,0x00,0x00,0x00,0x33}, /* 1280x720x32 */ + {0x79,0x0e3b,0x62,0x3b74,0x0000,0x08,0x14,0x00,0x00,0x00,0x33}, /* 1280x720x8 */ + {0x7a,0x2a1d,0x40,0x3b52,0x0000,0x08,0x12,0x00,0x00,0x07,0x2d}, /* 800x480x16 */ + /* TW: End of new 16:9 modes */ + {0x7b,0x0aff,0x6f,0x35b2,0x0000,0x08,0x0e,0x00,0x00,0x00,0x29}, /* 1280x960x32 */ + {0x20,0x0a1b,0x54,0x0000,0x0000,0x08,0x0f,0x00,0x00,0x00,0x2b}, /* 1024x600 */ {0x21,0x0a3d,0x54,0x0000,0x0000,0x08,0x0f,0x00,0x00,0x00,0x2b}, {0x22,0x0a7f,0x54,0x0000,0x0000,0x08,0x0f,0x00,0x00,0x00,0x2b}, - {0x23,0x0a1b,0xc5,0x0000,0x0000,0x08,0x10,0x00,0x00,0x00,0x2c}, /* 1152x768 */ + {0x23,0x0a1b,0xc5,0x0000,0x0000,0x08,0x10,0x00,0x00,0x00,0x2c}, /* 1152x768 */ {0x24,0x0a3d,0xc5,0x431d,0x0000,0x08,0x10,0x00,0x00,0x00,0x2c}, {0x25,0x0a7f,0xc5,0x431d,0x0000,0x08,0x10,0x00,0x00,0x00,0x2c}, + {0x29,0x0e1b,0xc5,0x0000,0x0000,0x08,0x15,0x00,0x00,0x00,0x36}, /* TW: NEW 1152x864 - not in BIOS */ + {0x2a,0x0e3d,0xc5,0x0000,0x0000,0x08,0x15,0x00,0x00,0x00,0x36}, + {0x2b,0x0e7f,0xc5,0x0000,0x0000,0x08,0x15,0x00,0x00,0x00,0x36}, + {0x39,0x2a1b,0xd6,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x38}, /* TW: NEW 848x480 - not in BIOS */ + {0x3b,0x2a3d,0xd6,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x38}, + {0x3e,0x2a7f,0xd6,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x38}, + {0x3f,0x2a1b,0xd7,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x3a}, /* TW: NEW 856x480 - not in BIOS */ + {0x42,0x2a3d,0xd7,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x3a}, + {0x45,0x2a7f,0xd7,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x3a}, + {0x48,0x223b,0xe8,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x3c}, /* TW: NEW 1360x768 - not in BIOS */ + {0x4b,0x227d,0xe8,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x3c}, + {0x4e,0x22ff,0xe8,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x3c}, + {0x4f,0x921f,0x00,0x0000,0x0000,0x08,0x00,0x00,0x00,0x00,0x23}, /* TW: New 320x200x32 */ + {0x53,0x921f,0x01,0x0000,0x0000,0x08,0x01,0x00,0x00,0x00,0x24}, /* TW: New 320x240x32 */ + {0x54,0xb21f,0x13,0x0000,0x0000,0x08,0x03,0x00,0x00,0x00,0x25}, /* TW: New 400x300x32 */ {0xff,0x0000,0x00,0x0000,0xffff,0x00,0x00,0x00,0x00,0x00,0x00} }; @@ -486,7 +512,7 @@ { USHORT Ext_InfoFlag; UCHAR Ext_CRT1CRTC; /* TW: Index in SiS300_CRT1Table */ - UCHAR Ext_CRTVCLK; + UCHAR Ext_CRTVCLK; /* TW: Index in VCLK array */ UCHAR Ext_CRT2CRTC; /* TW: Index in LCD Paneltype arrays (&3f) */ UCHAR ModeID; USHORT XRes; @@ -494,15 +520,15 @@ USHORT ROM_OFFSET; } SiS300_Ext2Struct; -static const SiS300_Ext2Struct SiS300_RefIndex[]= +static const SiS300_Ext2Struct SiS300_RefIndex[] = { /* TW: Don't ever insert anything here, table is indexed */ {0x085f,0x0d,0x03,0x05,0x6a, 800, 600,0x3563}, /* 00 */ {0x0467,0x0e,0x44,0x05,0x6a, 800, 600,0x3568}, /* 01 */ - {0x0067,0x4f,0x07,0x48,0x6a, 800, 600,0x356d}, /* 02 */ + {0x0067,0x0f,0x07,0x48,0x6a, 800, 600,0x356d}, /* 02 - CRT1CRTC was 0x4f */ {0x0067,0x10,0x06,0x8b,0x6a, 800, 600,0x3572}, /* 03 */ {0x0147,0x11,0x08,0x00,0x6a, 800, 600,0x3577}, /* 04 */ {0x0147,0x12,0x0c,0x00,0x6a, 800, 600,0x357c}, /* 05 */ - {0x0047,0x51,0x4e,0x00,0x6a, 800, 600,0x3581}, /* 06 */ + {0x0047,0x11,0x4e,0x00,0x6a, 800, 600,0x3581}, /* 06 - CRT1CRTC was 0x51 */ {0x0047,0x11,0x13,0x00,0x6a, 800, 600,0x3586}, /* 07 */ {0xc85f,0x05,0x00,0x04,0x2e, 640, 480,0x3539}, /* 08 */ {0xc067,0x06,0x02,0x04,0x2e, 640, 480,0x353e}, /* 09 */ @@ -517,30 +543,46 @@ {0x000f,0x32,0x03,0x06,0x32, 720, 576,0x3637}, /* 12 */ {0x0187,0x15,0x05,0x00,0x37,1024, 768,0x358d}, /* 13 */ {0xc877,0x16,0x09,0x06,0x37,1024, 768,0x3592}, /* 14 */ - {0xc067,0x97,0x0b,0x49,0x37,1024, 768,0x3597}, /* 15 */ + {0xc067,0x17,0x0b,0x49,0x37,1024, 768,0x3597}, /* 15 - CRT1CRTC was 0x97 */ {0x0267,0x18,0x0d,0x00,0x37,1024, 768,0x359c}, /* 16 */ - {0x0047,0x59,0x11,0x8c,0x37,1024, 768,0x35a1}, /* 17 */ + {0x0047,0x19,0x11,0x8c,0x37,1024, 768,0x35a1}, /* 17 - CRT1CRTC was 0x59 */ {0x0047,0x1a,0x52,0x00,0x37,1024, 768,0x35a6}, /* 18 */ - {0x0047,0x5b,0x16,0x00,0x37,1024, 768,0x35ab}, /* 19 */ - {0x0387,0x5c,0x4d,0x00,0x3a,1280,1024,0x35be}, /* 1a */ + {0x0007,0x1b,0x16,0x00,0x37,1024, 768,0x35ab}, /* 19 - CRT1CRTC was 0x5b */ + {0x0387,0x1c,0x4d,0x00,0x3a,1280,1024,0x35be}, /* 1a - CRT1CRTC was 0x5c */ {0x0077,0x1d,0x14,0x07,0x3a,1280,1024,0x35c3}, /* 1b */ {0x0047,0x1e,0x17,0x00,0x3a,1280,1024,0x35c8}, /* 1c */ {0x0007,0x1f,0x98,0x00,0x3a,1280,1024,0x35cd}, /* 1d */ - {0x0007,0x60,0x59,0x00,0x3c,1600,1200,0x35d4}, /* 1e */ + {0x0007,0x20,0x59,0x00,0x3c,1600,1200,0x35d4}, /* 1e - CRT1CRTC was 0x60 */ {0x0007,0x21,0x5a,0x00,0x3c,1600,1200,0x35d9}, /* 1f */ {0x0007,0x22,0x1b,0x00,0x3c,1600,1200,0x35de}, /* 20 */ - {0x0007,0x63,0x1d,0x00,0x3c,1600,1200,0x35e3}, /* 21 */ + {0x0007,0x23,0x1d,0x00,0x3c,1600,1200,0x35e3}, /* 21 - CRT1CRTC was 0x63 */ {0x0007,0x24,0x1e,0x00,0x3c,1600,1200,0x35e8}, /* 22 */ {0x407f,0x00,0x00,0x00,0x40, 320, 200,0x3516}, /* 23 */ {0xc07f,0x01,0x00,0x04,0x50, 320, 240,0x351d}, /* 24 */ {0x0077,0x02,0x04,0x05,0x51, 400, 300,0x3524}, /* 25 */ {0xc877,0x03,0x09,0x06,0x52, 512, 384,0x352b}, /* 26 */ /* was c077 */ {0x8207,0x25,0x1f,0x00,0x68,1920,1440,0x35ef}, /* 27 */ - {0x0007,0x26,0x20,0x00,0x6c, 720, 480,0x35f6}, /* 28 */ - {0x0027,0x27,0x14,0x08,0x6e,1280, 960,0x35b2}, /* 29 */ - {0x0027,0x27,0x14,0x08,0x6e,1280, 960,0x35b7}, /* 2a */ + {0x0007,0x26,0x20,0x00,0x6c,2048,1536,0x35f6}, /* 28 */ + {0x0067,0x27,0x14,0x08,0x6e,1280, 960,0x35b7}, /* 29 - TW: 1280x960-60 */ + {0x0027,0x45,0x3c,0x08,0x6e,1280, 960,0x35b7}, /* 2a - TW: 1280x960-85 */ {0xc077,0x33,0x09,0x06,0x20,1024, 600,0x0000}, /* 2b */ - {0xc077,0x34,0x09,0x06,0x23,1152, 768,0x0000}, /* 2c */ + {0xc077,0x34,0x0b,0x06,0x23,1152, 768,0x0000}, /* 2c */ /* VCLK 0x09 */ + {0x0057,0x35,0x27,0x08,0x70, 800, 480,0x3b52}, /* 2d - TW: 16:9 modes */ + {0x0047,0x36,0x37,0x08,0x70, 800, 480,0x3b57}, /* 2e */ + {0x0047,0x37,0x08,0x08,0x70, 800, 480,0x3b5c}, /* 2f */ + {0x0057,0x38,0x09,0x09,0x71,1024, 576,0x3b63}, /* 30 */ + {0x0047,0x39,0x38,0x09,0x71,1024, 576,0x3b68}, /* 31 */ + {0x0047,0x3a,0x11,0x09,0x71,1024, 576,0x3b6d}, /* 32 */ + {0x0057,0x3b,0x39,0x0a,0x75,1280, 720,0x3b74}, /* 33 */ + {0x0047,0x3c,0x3a,0x0a,0x75,1280, 720,0x3b79}, /* 34 */ + {0x0007,0x3d,0x3b,0x0a,0x75,1280, 720,0x3b7e}, /* 35 - TW: END of 16:9 modes */ + {0x0047,0x3e,0x34,0x06,0x29,1152, 864,0x0000}, /* 36 TW: 1152x864-75Hz - Non-BIOS, new */ + {0x0047,0x44,0x3a,0x06,0x29,1152, 864,0x0000}, /* 37 TW: 1152x864-85Hz - Non-BIOS, new */ + {0x00c7,0x3f,0x28,0x00,0x39, 848, 480,0x0000}, /* 38 TW: 848x480-38Hzi - Non-BIOS, new */ + {0xc047,0x40,0x3d,0x00,0x39, 848, 480,0x0000}, /* 39 TW: 848x480-60Hz - Non-BIOS, new */ + {0x00c7,0x41,0x28,0x00,0x3f, 856, 480,0x0000}, /* 3a TW: 856x480-38Hzi - Non-BIOS, new */ + {0xc047,0x42,0x28,0x00,0x3f, 856, 480,0x0000}, /* 3b TW: 856x480-60Hz - Non-BIOS, new */ + {0x0047,0x43,0x3e,0x00,0x48,1360, 768,0x0000}, /* 3c TW: 1360x768-60Hz - Non-BIOS, new */ {0xffff,0,0,0,0,0,0,0} }; @@ -557,9 +599,9 @@ UCHAR _VB_LCDVIndex; }SiS_VBModeIDTableStruct; -static const SiS_VBModeIDTableStruct SiS300_VBModeIDTable[]= +static const SiS_VBModeIDTableStruct SiS300_VBModeIDTable[] = { - {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* TW: Identical to 630/301B 2.04.50 BIOS */ + {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, {0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x02}, {0x03,0x00,0x00,0x00,0x02,0x00,0x02,0x00}, @@ -619,46 +661,87 @@ UCHAR CR[17]; } SiS300_CRT1TableStruct; -static const SiS300_CRT1TableStruct SiS300_CRT1Table[]= +static const SiS300_CRT1TableStruct SiS300_CRT1Table[] = { - {{0x2d,0x27,0x28,0x90,0x2c,0x80,0xbf,0x1f, /* 0x00 */ - 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x00, +#if 1 + {{0x2d,0x27,0x28,0x90,0x2c,0x80,0xbf,0x1f, /* 0x00 - 320x200 */ + 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x00, /* HRE [4],[15] is invalid - but correcting it does not work */ + 0x00}}, +#endif +#if 0 + {{0x2d,0x27,0x27,0x91,0x2c,0x92,0xbf,0x1f, /* 0x00 - corrected 320x200-72 - does not work */ + 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x04, 0x00}}, - {{0x2d,0x27,0x28,0x90,0x2c,0x80,0x0b,0x3e, - 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x00, +#endif +#if 1 + {{0x2d,0x27,0x28,0x90,0x2c,0x80,0x0b,0x3e, /* 0x01 */ + 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x00, /* HRE [4],[15] is invalid - but correcting it does not work */ 0x00}}, - {{0x3d,0x31,0x31,0x81,0x37,0x1f,0x72,0xf0, +#endif +#if 0 + {{0x2d,0x27,0x27,0x91,0x2c,0x92,0x0b,0x3e, /* 0x01 - corrected 320x240-60 - does not work */ + 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x04, + 0x00}}, +#endif +#if 1 + {{0x3d,0x31,0x31,0x81,0x37,0x1f,0x72,0xf0, /* 0x02 */ + 0x58,0x8c,0x57,0x57,0x73,0x20,0x00,0x05, + 0x01}}, +#endif +#if 0 + {{0x3d,0x31,0x31,0x81,0x37,0x1f,0x72,0xf0, /* 0x02 - corrected 400x300-60 */ 0x58,0x8c,0x57,0x57,0x73,0x20,0x00,0x05, 0x01}}, +#endif {{0x4f,0x3f,0x3f,0x93,0x45,0x0d,0x24,0xf5, 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x01, 0x01}}, {{0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x05, 0x00}}, - {{0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e, +#if 0 + {{0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e, /* 0x05 */ 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x05, 0x00}}, - {{0x63,0x4f,0x50,0x86,0x56,0x9b,0x06,0x3e, +#endif + {{0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, /* 0x05 - corrected 640x480-60 */ + 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x00}}, + #if 0 + {{0x63,0x4f,0x50,0x86,0x56,0x9b,0x06,0x3e, /* 0x06 */ 0xe8,0x8b,0xdf,0xe7,0xff,0x10,0x00,0x01, 0x00}}, +#endif + {{0x63,0x4f,0x4f,0x87,0x56,0x9b,0x06,0x3e, /* 0x06 - corrected 640x480-72 */ + 0xe8,0x8a,0xdf,0xe7,0x07,0x00,0x00,0x01, + 0x00}}, {{0x64,0x4f,0x4f,0x88,0x55,0x9d,0xf2,0x1f, 0xe0,0x83,0xdf,0xdf,0xf3,0x10,0x00,0x01, 0x00}}, {{0x63,0x4f,0x4f,0x87,0x5a,0x81,0xfb,0x1f, 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x05, 0x00}}, - {{0x66,0x4f,0x4f,0x86,0x56,0x9e,0x03,0x3e, +#if 0 + {{0x66,0x4f,0x4f,0x86,0x56,0x9e,0x03,0x3e, /* 0x09 */ 0xe4,0x87,0xdf,0xdf,0x04,0x00,0x00,0x01, 0x00}}, +#endif + {{0x67,0x4f,0x4f,0x8b,0x57,0x83,0x10,0x3e, /* 0x09 - corrected 640x480-100 */ + 0xe7,0x8d,0xdf,0xe6,0x11,0x00,0x00,0x05, + 0x00}}, +#if 0 {{0x6c,0x4f,0x4f,0x83,0x59,0x9e,0x00,0x3e, /* 0x0a */ 0xe5,0x8d,0xdf,0xdf,0x01,0x00,0x00,0x01, 0x00}}, +#endif + {{0x67,0x4f,0x4f,0x8b,0x57,0x83,0x10,0x3e, /* 0x0a - corrected 640x480-120 */ + 0xe7,0x8d,0xdf,0xe6,0x11,0x00,0x00,0x05, + 0x00}}, {{0x63,0x4f,0x4f,0x87,0x56,0x9d,0xfb,0x1f, 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x01, 0x00}}, {{0x65,0x4f,0x4f,0x89,0x57,0x9f,0xfb,0x1f, - 0xe6,0x8a,0xe5,0xe5,0xfc,0x00,0x00,0x01, + 0xe6,0x8a,0xdf,0xdf,0xfc,0x10,0x00,0x01, /* TW: Corrected VDE, VBE */ 0x00}}, {{0x7b,0x63,0x63,0x9f,0x6a,0x93,0x6f,0xf0, 0x58,0x8a,0x57,0x57,0x70,0x20,0x00,0x05, @@ -699,7 +782,7 @@ {{0xa7,0x7f,0x7f,0x8b,0x89,0x95,0x26,0xf5, 0x00,0x83,0xff,0xff,0x27,0x10,0x00,0x02, 0x01}}, - {{0x9f,0x7f,0x7f,0x83,0x83,0x93,0x1e,0xf5, + {{0x9f,0x7f,0x7f,0x83,0x83,0x93,0x1e,0xf5, /* 0x1a */ 0x00,0x84,0xff,0xff,0x1f,0x10,0x00,0x02, 0x01}}, {{0xa2,0x7f,0x7f,0x86,0x84,0x94,0x37,0xf5, @@ -738,9 +821,14 @@ {{0x55,0xff,0xff,0x99,0x0d,0x0c,0x3e,0xba, 0x00,0x84,0xff,0xff,0x3f,0x0f,0x41,0x05, 0x00}}, - {{0xdc,0x9f,0x9f,0x00,0xab,0x19,0xe6,0xef, +#if 0 + {{0xdc,0x9f,0x9f,0x00,0xab,0x19,0xe6,0xef, /* 0x27: 1280x960-70 - invalid! */ 0xc0,0xc3,0xbf,0xbf,0xe7,0x10,0x00,0x07, 0x01}}, +#endif + {{0xdc,0x9f,0x9f,0x80,0xaf,0x9d,0xe6,0xff, /* 0x27: 1280x960-60 - correct */ + 0xc0,0x83,0xbf,0xbf,0xe7,0x10,0x00,0x07, + 0x01}}, {{0x7f,0x63,0x63,0x83,0x6c,0x1c,0x72,0xba, /* 0x28 */ 0x27,0x8b,0xdf,0xdf,0x73,0x00,0x00,0x06, 0x01}}, @@ -777,9 +865,70 @@ {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x1e,0xf1, /* 0x33 - 1024x600 */ 0xae,0x85,0x57,0x57,0x1f,0x30,0x00,0x02, 0x01}}, +#if 0 {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf5, /* 0x34 - 1152x768 */ 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x02, - 0x01}} + 0x01}}, +#endif + {{0xa3,0x8f,0x8f,0x97,0x96,0x97,0x24,0xf5, /* 0x34 - 1152x768 - TW: corrected */ + 0x02,0x88,0xff,0xff,0x25,0x10,0x00,0x02, + 0x01}}, + {{0x7f,0x63,0x63,0x83,0x6c,0x1c,0x72,0xba, /* 0x35 - NEW 16:9 modes, not in BIOS ------ */ + 0x27,0x8b,0xdf,0xdf,0x73,0x00,0x00,0x06, + 0x01}}, /* 0x35 */ + {{0x7f,0x63,0x63,0x83,0x69,0x13,0x6f,0xba, + 0x26,0x89,0xdf,0xdf,0x6f,0x00,0x00,0x06, + 0x01}}, /* 0x36 */ + {{0x7f,0x63,0x63,0x82,0x6b,0x13,0x75,0xba, + 0x29,0x8c,0xdf,0xdf,0x75,0x00,0x00,0x06, + 0x01}}, /* 0x37 */ + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf1, + 0xaf,0x85,0x3f,0x3f,0x25,0x30,0x00,0x02, + 0x01}}, /* 0x38 */ + {{0x9f,0x7f,0x7f,0x83,0x85,0x91,0x1e,0xf1, + 0xad,0x81,0x3f,0x3f,0x1f,0x30,0x00,0x02, + 0x01}}, /* 0x39 */ + {{0xa7,0x7f,0x7f,0x88,0x89,0x95,0x26,0xf1, /* TW: 95 was 15 - illegal HBE! */ + 0xb1,0x85,0x3f,0x3f,0x27,0x30,0x00,0x02, + 0x01}}, /* 0x3a */ + {{0xce,0x9f,0x9f,0x92,0xa9,0x17,0x28,0xc4, + 0x7a,0x8e,0xcf,0xcf,0x29,0x21,0x00,0x07, + 0x01}}, /* 0x3b */ + {{0xce,0x9f,0x9f,0x92,0xa5,0x17,0x28,0xd4, + 0x7a,0x8e,0xcf,0xcf,0x29,0x21,0x00,0x07, + 0x01}}, /* 0x3c */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0x2e,0xd4, + 0x7d,0x81,0xcf,0xcf,0x2f,0x21,0x00,0x07, + 0x01}}, /* 0x3d */ /* TW: End of 16:9 modes --------------- */ + {{0xc3,0x8f,0x8f,0x87,0x9b,0x0b,0x82,0xef, /* TW: New, 1152x864-75 (not in any BIOS) */ + 0x60,0x83,0x5f,0x5f,0x83,0x10,0x00,0x07, + 0x01}}, /* 0x3e */ + {{0x86,0x69,0x69,0x8A,0x74,0x06,0x8C,0x15, /* TW: New, 848x480-38i, not in BIOS */ + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x3f */ +#if 0 + {{0x81,0x69,0x69,0x85,0x70,0x00,0x0F,0x3E, /* TW: New, 848x480-60, not in BIOS - incorrect for Philips panel */ + 0xEB,0x8E,0xDF,0xDF,0x10,0x00,0x00,0x02, + 0x00}}, /* 0x40 */ +#endif + {{0x83,0x69,0x69,0x87,0x6f,0x1d,0x03,0x3E, /* TW: New, 848x480-60, not in BIOS */ + 0xE5,0x8d,0xDF,0xe4,0x04,0x00,0x00,0x06, + 0x00}}, /* 0x40 */ + {{0x86,0x6A,0x6A,0x8A,0x74,0x06,0x8C,0x15, /* TW: New, 856x480-38i, not in BIOS */ + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x41 */ + {{0x81,0x6A,0x6A,0x85,0x70,0x00,0x0F,0x3E, /* TW: New, 856x480-60, not in BIOS */ + 0xEB,0x8E,0xDF,0xDF,0x10,0x00,0x00,0x02, + 0x00}}, /* 0x42 */ + {{0xdd,0xa9,0xa9,0x81,0xb4,0x97,0x26,0xfd, /* TW: New, 1360x768-60, not in BIOS */ + 0x01,0x8d,0xff,0x00,0x27,0x10,0x00,0x03, + 0x01}}, /* 0x43 */ + {{0xd9,0x8f,0x8f,0x9d,0xba,0x0a,0x8a,0xff, /* TW: New, 1152x864-84 (not in any BIOS) */ + 0x60,0x8b,0x5f,0x5f,0x8b,0x10,0x00,0x03, + 0x01}}, /* 0x44 */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0xf1,0xff, /* TW: New, 1280x960-85 (not in any BIOS) */ + 0xc0,0x83,0xbf,0xbf,0xf2,0x10,0x00,0x07, + 0x01}} /* 0x45 */ }; typedef struct _SiS300_MCLKDataStruct @@ -818,7 +967,7 @@ USHORT CLOCK; } SiS300_ECLKDataStruct; -static const SiS300_ECLKDataStruct SiS300_ECLKData[]= +static const SiS300_ECLKDataStruct SiS300_ECLKData[] = { { 0x54,0x43,0x80,100}, { 0x53,0x43,0x80,100}, @@ -836,68 +985,79 @@ USHORT CLOCK; } SiS300_VCLKDataStruct; -static const SiS300_VCLKDataStruct SiS300_VCLKData[]= +static const SiS300_VCLKDataStruct SiS300_VCLKData[] = { - { 0x1b,0xe1, 25}, + { 0x1b,0xe1, 25}, /* 0x00 */ { 0x4e,0xe4, 28}, - { 0x57,0xe4, 32}, + { 0x57,0xe4, 32}, /* 0x02 */ { 0xc3,0xc8, 36}, - { 0x42,0xc3, 40}, + { 0x42,0xc3, 40}, /* 0x04 */ { 0x5d,0xc4, 45}, - { 0x52,0x65, 50}, + { 0x52,0x65, 50}, /* 0x06 */ { 0x53,0x65, 50}, - { 0x6d,0x66, 56}, + { 0x6d,0x66, 56}, /* 0x08 */ { 0x5a,0x64, 65}, - { 0x46,0x44, 68}, + { 0x46,0x44, 68}, /* 0x0a */ { 0x3e,0x43, 75}, - { 0x6d,0x46, 76}, /* 0x0c: 800x600 | LVDS_2(CH), MITAC(CH); - LVDS2(CH), A901(301B): 0xb1,0x46, 76 */ + { 0x6d,0x46, 76}, /* 0x0c: 800x600 | LVDS_2(CH), MITAC(CH); - 730, A901(301B): 0xb1,0x46, 76 */ { 0x41,0x43, 79}, - { 0x31,0x42, 79}, + { 0x31,0x42, 79}, /* 0x0e */ { 0x46,0x25, 85}, { 0x78,0x29, 87}, /* 0x10 */ { 0x62,0x44, 95}, - { 0x2b,0x22,105}, + { 0x2b,0x22,105}, /* 0x12 */ { 0x49,0x24,106}, - { 0xc3,0x28,108}, + { 0xc3,0x28,108}, /* 0x14 */ { 0x3c,0x23,109}, - { 0xf7,0x2c,132}, + { 0xf7,0x2c,132}, /* 0x16 */ { 0xd4,0x28,136}, - { 0x41,0x05,158}, + { 0x41,0x05,158}, /* 0x18 */ { 0x43,0x05,162}, - { 0xe1,0x0f,175}, - { 0xfc,0x12,189}, - { 0xde,0x26,194}, + { 0xe1,0x0f,175}, /* 0x1a */ + { 0xfc,0x12,189}, /* 0x1b */ + { 0xde,0x26,194}, /* 0x1c */ { 0x54,0x05,203}, - { 0x3f,0x03,230}, + { 0x3f,0x03,230}, /* 0x1e */ { 0x30,0x02,234}, - { 0x24,0x01,266}, /* 0x20 */ - { 0x52,0x2a, 54}, /* 301 TV */ - { 0x52,0x6a, 27}, /* 301 TV */ - { 0x62,0x24, 70}, /* 301 TV */ - { 0x62,0x64, 70}, /* 301 TV */ - { 0xa8,0x4c, 30}, /* 301 TV */ - { 0x20,0x26, 33}, /* 301 TV */ + { 0x24,0x01,266}, /* 0x20 */ + { 0x52,0x2a, 54}, /* 301 TV */ + { 0x52,0x6a, 27}, /* 301 TV */ + { 0x62,0x24, 70}, /* 301 TV */ + { 0x62,0x64, 70}, /* 301 TV */ + { 0xa8,0x4c, 30}, /* 301 TV */ + { 0x20,0x26, 33}, /* 301 TV */ { 0x31,0xc2, 39}, - { 0xbf,0xc8, 35}, /* 0x28 */ - { 0x60,0x36, 30}, /* 0x29 CH/UNTSC TEXT | LVDS_2(CH) - LVDS2(CH), A901(301B), Mitac(CH): 0xe0, 0xb6, 30 */ + { 0xbf,0xc8, 35}, /* 0x28 - 856x480 */ + { 0x60,0x36, 30}, /* 0x29 CH/UNTSC TEXT | LVDS_2(CH) - 730, A901(301B), Mitac(CH): 0xe0, 0xb6, 30 */ { 0x40,0x4a, 28}, { 0x9f,0x46, 44}, { 0x97,0x2c, 26}, { 0x44,0xe4, 25}, { 0x7e,0x32, 47}, - { 0x8a,0x24, 31}, /* 0x2f CH/PAL TEXT | LVDS_2(CH), Mitac(CH) - LVDS2(CH), A901(301B): 0x57, 0xe4, 31 */ + { 0x8a,0x24, 31}, /* 0x2f CH/PAL TEXT | LVDS_2(CH), Mitac(CH) - 730, A901(301B): 0x57, 0xe4, 31 */ { 0x97,0x2c, 26}, { 0xce,0x3c, 39}, - { 0x52,0x4a, 36}, + { 0x52,0x4a, 36}, /* 0x32 CH/PAL 800x600 5/6 */ { 0x34,0x61, 95}, { 0x78,0x27,108}, - { 0xce,0x25,189}, - { 0x45,0x6b, 21}, /* 0x36 */ /* TW: Added from Mitac */ - { 0xff,0x00, 0} + { 0xce,0x25,189}, /* 0x35 */ + { 0x45,0x6b, 21}, /* 0x36 */ /* TW: Added from Mitac */ + { 0x52,0xe2, 49}, /* 0x37 - added for 16:9 modes (not in any BIOS) */ + { 0x2b,0x61, 78}, /* 0x38 - added for 16:9 modes (not in any BIOS) */ + { 0x70,0x44,108}, /* 0x39 - added for 16:9 modes (not in any BIOS) */ + { 0x54,0x42,135}, /* 0x3a - added for 16:9 modes (not in any BIOS) */ + { 0x41,0x22,157}, /* 0x3b - added for 16:9 modes (not in any BIOS) */ + { 0x52,0x07,149}, /* 0x3c - added for 1280x960-85 (not in any BIOS)*/ + { 0x62,0xc6, 34}, /* 0x3d - added for 848x480-60 (not in any BIOS) */ + { 0x30,0x23, 88}, /* 0x3e - added for 1360x768-60 (not in any BIOS)*/ + { 0x3f,0x64, 46}, /* 0x3f - added for 640x480-100 (not in any BIOS)*/ + { 0x72,0x2a, 76}, /* 0x40 - test for SiS730 */ + { 0x15,0x21, 79}, /* 0x41 - test for SiS730 */ + { 0xff,0x00, 0} }; #if 0 /* TW: This table is in all BIOSes, but not used */ -static const SiS300_VCLKDataStruct SiS300_VBVCLKData[]= +static const SiS300_VCLKDataStruct SiS300_VBVCLKData[] = { { 0x1b,0xe1, 25}, { 0x4e,0xe4, 28}, @@ -953,7 +1113,8 @@ static const UCHAR SiS300_ScreenOffset[] = { 0x14,0x19,0x20,0x28,0x32,0x40,0x50, - 0x64,0x78,0x80,0x2d,0x35,0x48,0xff + 0x64,0x78,0x80,0x2d,0x35,0x48,0x35, /* 0x35 for 848 and 856 */ + 0x55,0xff /* 0x55 for 1360 */ }; typedef struct _SiS300_StResInfoStruct @@ -997,12 +1158,20 @@ { 720, 576, 8,16}, /* 0x0d */ { 1280, 960, 8,16}, /* 0x0e */ { 1024, 600, 8,16}, /* 0x0f */ - { 1152, 768, 8,16} /* 0x10 */ + { 1152, 768, 8,16}, /* 0x10 */ + { 2048,1536, 8,16}, /* 0x11 - TW: Not in BIOS! */ + { 800, 480, 8,16}, /* 0x12 - TW: New, not in any BIOS */ + { 1024, 576, 8,16}, /* 0x13 - TW: New, not in any BIOS */ + { 1280, 720, 8,16}, /* 0x14 - TW: New, not in any BIOS */ + { 1152, 864, 8,16}, /* 0x15 - TW: New, not in any BIOS */ + { 848, 480, 8,16}, /* 0x16 - TW: New, not in any BIOS */ + { 856, 480, 8,16}, /* 0x17 - TW: New, not in any BIOS */ + { 1360, 768, 8,16} /* 0x18 - TW: New, not in any BIOS */ }; static const UCHAR SiS300_OutputSelect = 0x40; -static const UCHAR SiS300_SoftSetting = 30; +static const UCHAR SiS300_SoftSetting = 0x30; #ifndef LINUX_XF86 static UCHAR SiS300_SR07 = 0x10; @@ -1111,7 +1280,7 @@ USHORT LCDVT; } SiS300_LCDDataStruct; -static const SiS300_LCDDataStruct SiS300_StLCD1024x768Data[]= +static const SiS300_LCDDataStruct SiS300_StLCD1024x768Data[] = { { 66, 31, 992, 510,1320, 816}, { 66, 31, 992, 510,1320, 816}, @@ -1122,7 +1291,7 @@ { 1, 1,1344, 806,1344, 806} }; -static const SiS300_LCDDataStruct SiS300_ExtLCD1024x768Data[]= +static const SiS300_LCDDataStruct SiS300_ExtLCD1024x768Data[] = { { 12, 5, 896, 512,1344, 806}, { 12, 5, 896, 510,1344, 806}, @@ -1139,7 +1308,7 @@ { 1, 1,1344, 806,1344, 806} }; -static const SiS300_LCDDataStruct SiS300_St2LCD1024x768Data[]= +static const SiS300_LCDDataStruct SiS300_St2LCD1024x768Data[] = { { 62, 25, 800, 546,1344, 806}, { 32, 15, 930, 546,1344, 806}, @@ -1150,7 +1319,7 @@ { 1, 1,1344, 806,1344, 806} }; -static const SiS300_LCDDataStruct SiS300_StLCD1280x1024Data[]= +static const SiS300_LCDDataStruct SiS300_StLCD1280x1024Data[] = { { 4, 1, 880, 510,1650,1088}, { 4, 1, 880, 510,1650,1088}, @@ -1162,7 +1331,7 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS300_LCDDataStruct SiS300_ExtLCD1280x1024Data[]= +static const SiS300_LCDDataStruct SiS300_ExtLCD1280x1024Data[] = { { 211, 60,1024, 501,1688,1066}, { 211, 60,1024, 508,1688,1066}, @@ -1174,7 +1343,7 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS300_LCDDataStruct SiS300_St2LCD1280x1024Data[]= +static const SiS300_LCDDataStruct SiS300_St2LCD1280x1024Data[] = { { 22, 5, 800, 510,1650,1088}, { 22, 5, 800, 510,1650,1088}, @@ -1186,7 +1355,7 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS300_LCDDataStruct SiS300_NoScaleData1024x768[]= +static const SiS300_LCDDataStruct SiS300_NoScaleData1024x768[] = { { 1, 1, 800, 449, 800, 449}, { 1, 1, 800, 449, 800, 449}, @@ -1198,7 +1367,7 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS300_LCDDataStruct SiS300_NoScaleData1280x1024[]= /* TW: Fake */ +static const SiS300_LCDDataStruct SiS300_NoScaleData1280x1024[] = /* TW: Fake */ { { 1, 1, 800, 449, 800, 449}, { 1, 1, 800, 449, 800, 449}, @@ -1210,7 +1379,20 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS300_LCDDataStruct SiS300_LCD1280x960Data[]= +static const SiS300_LCDDataStruct SiS300_NoScaleData[] = +{ + { 1, 1, 800, 449, 800, 449 }, + { 1, 1, 800, 449, 800, 449 }, + { 1, 1, 900, 449, 900, 449 }, + { 1, 1, 900, 449, 900, 449 }, + { 1, 1, 800, 525, 800, 525 }, + { 1, 1,1056, 628,1056, 628 }, + { 1, 1,1344, 806,1344, 806 }, + { 1, 1,1688,1066,1688,1066 }, + { 1, 1,1800,1000,1800,1000 } /* 1280x960 */ +}; + +static const SiS300_LCDDataStruct SiS300_LCD1280x960Data[] = { { 9, 2, 800, 500,1800,1000}, { 9, 2, 800, 500,1800,1000}, @@ -1325,7 +1507,7 @@ UCHAR RY4COE; } SiS300_TVDataStruct; -static const SiS300_TVDataStruct SiS300_StPALData[]= +static const SiS300_TVDataStruct SiS300_StPALData[] = { { 1, 1, 864, 525,1270, 400, 100, 0, 760,0xf4,0xff,0x1c,0x22}, { 1, 1, 864, 525,1270, 350, 100, 0, 760,0xf4,0xff,0x1c,0x22}, @@ -1335,7 +1517,7 @@ { 1, 1, 864, 525,1270, 600, 50, 0, 0,0xf4,0xff,0x1c,0x22} }; -static const SiS300_TVDataStruct SiS300_ExtPALData[]= +static const SiS300_TVDataStruct SiS300_ExtPALData[] = { { 27, 10, 848, 448,1270, 530, 50, 0, 50,0xf4,0xff,0x1c,0x22}, { 108, 35, 848, 398,1270, 530, 50, 0, 50,0xf4,0xff,0x1c,0x22}, @@ -1348,7 +1530,7 @@ }; -static const SiS300_TVDataStruct SiS300_StNTSCData[]= +static const SiS300_TVDataStruct SiS300_StNTSCData[] = { { 1, 1, 858, 525,1270, 400, 50, 0, 760,0xf1,0x04,0x1f,0x18}, { 1, 1, 858, 525,1270, 350, 50, 0, 640,0xf1,0x04,0x1f,0x18}, @@ -1357,7 +1539,7 @@ { 1, 1, 858, 525,1270, 480, 0, 0, 760,0xf1,0x04,0x1f,0x18} }; -static const SiS300_TVDataStruct SiS300_ExtNTSCData[]= +static const SiS300_TVDataStruct SiS300_ExtNTSCData[] = { { 143, 65, 858, 443,1270, 440, 171, 0, 171,0xf1,0x04,0x1f,0x18}, { 88, 35, 858, 393,1270, 440, 171, 0, 171,0xf1,0x04,0x1f,0x18}, @@ -1369,49 +1551,68 @@ { 65, 64,1056, 791,1270, 480, 638, 0, 0,0xf1,0x04,0x1f,0x18} }; -static const SiS_TVDataStruct SiS300_St1HiTVData[]= +#if 0 +static const SiS300_TVDataStruct SiS300_St1HiTVData[]= { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + }; +#endif -static const SiS_TVDataStruct SiS300_St2HiTVData[]= +static const SiS300_TVDataStruct SiS300_St2HiTVData[]= { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + { 3, 1, 0x348,0x1e3,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 1, 1, 0x37c,0x233,0x2b2,0x2bc, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x348,0x1e3,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 1, 1, 0x3e8,0x233,0x311,0x2bc, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 2, 0x348,0x233,0x670,0x3c0,0x08d,128, 0, 0x00,0x00,0x00,0x00}, + { 8, 5, 0x41a,0x2ab,0x670,0x3c0,0x17c,128, 0, 0x00,0x00,0x00,0x00} +}; + +static const SiS300_TVDataStruct SiS300_ExtHiTVData[]= +{ + { 6, 1, 0x348,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x3c0,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x348,0x1e3,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x3c0,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 1, 0x348,0x233,0x670,0x3c0,0x166,128, 0, 0x00,0x00,0x00,0x00}, + { 16, 5, 0x41a,0x2ab,0x670,0x3c0,0x143,128, 0, 0x00,0x00,0x00,0x00}, + { 25, 12, 0x4ec,0x353,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 4, 0x627,0x464,0x670,0x3c0,0x128, 0, 0, 0x00,0x00,0x00,0x00}, + { 4, 1, 0x41a,0x233,0x670,0x3c0,0x143,128, 0, 0x00,0x00,0x00,0x00}, + { 5, 2, 0x578,0x293,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 8, 5, 0x6d6,0x323,0x670,0x3c0,0x128, 0, 0, 0x00,0x00,0x00,0x00} }; -static const SiS_TVDataStruct SiS300_ExtHiTVData[]= +static const UCHAR SiS300_NTSCTiming[] = { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} -}; - -static const UCHAR SiS300_NTSCTiming[] = { 0x17,0x1d,0x03,0x09,0x05,0x06,0x0c,0x0c, 0x94,0x49,0x01,0x0a,0x06,0x0d,0x04,0x0a, 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x1b, -/* 0x0c,0x50,0x00,0x97,0x00,0xda,0x4a,0x17, - old */ - 0x0c,0x50,0x00,0x99,0x00,0xec,0x4a,0x17, /* new (2.04.5a) */ -/* 0x7d,0x05,0x4b,0x00,0x00,0xe2,0x00,0x02, - old */ - 0x88,0x00,0x4b,0x00,0x00,0xe2,0x00,0x02, /* new */ + 0x0c,0x50,0x00,0x97,0x00,0xda,0x4a,0x17, /* (in 2.06.50) */ +/* 0x0c,0x50,0x00,0x99,0x00,0xec,0x4a,0x17, (in 2.04.5a) */ + 0x7d,0x05,0x4b,0x00,0x00,0xe2,0x00,0x02, /* (in 2.06.50) */ +/* 0x88,0x00,0x4b,0x00,0x00,0xe2,0x00,0x02, (in 2.04.5a) */ 0x03,0x0a,0x65,0x9d,0x08,0x92,0x8f,0x40, 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x50, 0x00,0x40,0x44,0x00,0xdb,0x02,0x3b,0x00 }; -static const UCHAR SiS300_PALTiming[] = { +static const UCHAR SiS300_PALTiming[] = +{ 0x19,0x52,0x35,0x6e,0x04,0x38,0x3d,0x70, 0x94,0x49,0x01,0x12,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0x45,0x2b, -/* 0x70,0x50,0x00,0x9b,0x00,0xd9,0x5d,0x17, - old */ - 0x70,0x50,0x00,0x97,0x00,0xd7,0x5d,0x17, /* new */ -/* 0x7d,0x05,0x45,0x00,0x00,0xe8,0x00,0x02, -old */ - 0x88,0x00,0x45,0x00,0x00,0xe8,0x00,0x02, /* new */ + 0x70,0x50,0x00,0x9b,0x00,0xd9,0x5d,0x17, /* (in 2.06.50) */ +/* 0x70,0x50,0x00,0x97,0x00,0xd7,0x5d,0x17, (in 2.04.5a) */ + 0x7d,0x05,0x45,0x00,0x00,0xe8,0x00,0x02, /* (in 2.06.50) */ +/* 0x88,0x00,0x45,0x00,0x00,0xe8,0x00,0x02, (in 2.04.5a) */ 0x0d,0x00,0x68,0xb0,0x0b,0x92,0x8f,0x40, 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x63, 0x00,0x40,0x3e,0x00,0xe1,0x02,0x28,0x00 }; -#ifdef oldHV -static const UCHAR SiS300_HiTVExtTiming[] = { /* TW: New */ +static const UCHAR SiS300_HiTVExtTiming[] = /* TW: New */ +{ 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x64, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1422,7 +1623,8 @@ 0x63,0x4f,0x27,0x00,0xfc,0xff,0x6a,0x00 }; -static const UCHAR SiS300_HiTVSt1Timing[] = { /* TW: New */ +static const UCHAR SiS300_HiTVSt1Timing[] = /* TW: New */ +{ 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x65, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1433,7 +1635,8 @@ 0xaf,0x5d,0x0e,0x00,0xfc,0xff,0x2d,0x00 }; -static const UCHAR SiS300_HiTVSt2Timing[] = { /* TW: New */ +static const UCHAR SiS300_HiTVSt2Timing[] = /* TW: New */ +{ 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x64, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1444,7 +1647,8 @@ 0x63,0x4f,0x27,0x00,0xfc,0xff,0x6a,0x00 }; -static const UCHAR SiS300_HiTVTextTiming[] = { /* TW: New */ +static const UCHAR SiS300_HiTVTextTiming[] = /* TW: New */ +{ 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x65, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1455,7 +1659,8 @@ 0x72,0x5c,0x11,0x00,0xfc,0xff,0x32,0x00 }; -static const UCHAR SiS300_HiTVGroup3Data[] = { /* TW: New */ +static const UCHAR SiS300_HiTVGroup3Data[] = /* TW: New */ +{ 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0x5f, 0x05,0x21,0xb2,0xb2,0x55,0x77,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1466,7 +1671,8 @@ 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -static const UCHAR SiS300_HiTVGroup3Simu[] = { /* TW: New */ +static const UCHAR SiS300_HiTVGroup3Simu[] = /* TW: New */ +{ 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0x95, 0xdb,0x20,0xb8,0xb8,0x55,0x47,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1477,7 +1683,8 @@ 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -static const UCHAR SiS300_HiTVGroup3Text[] = { /* TW: New */ +static const UCHAR SiS300_HiTVGroup3Text[] = /* TW: New */ +{ 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0xa7, 0xf5,0x20,0xce,0xce,0x55,0x47,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1487,7 +1694,6 @@ 0x01,0x05,0x03,0x7e,0x65,0x31,0x14,0x75, 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -#endif typedef struct _SiS300_LVDSDataStruct { @@ -1497,7 +1703,7 @@ USHORT LCDVT; } SiS300_LVDSDataStruct; -static const SiS300_LVDSDataStruct SiS300_LVDS320x480Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS320x480Data_1[] = { {848, 433,400, 525}, {848, 389,400, 525}, @@ -1510,7 +1716,7 @@ {800, 525,1000, 635} }; -static const SiS300_LVDSDataStruct SiS300_LVDS800x600Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS800x600Data_1[] = { {848, 433,1060, 629}, {848, 389,1060, 629}, @@ -1523,7 +1729,7 @@ {800, 525,1000, 635} }; -static const SiS300_LVDSDataStruct SiS300_LVDS800x600Data_2[]= +static const SiS300_LVDSDataStruct SiS300_LVDS800x600Data_2[] = { {1056, 628,1056, 628}, {1056, 628,1056, 628}, @@ -1536,7 +1742,7 @@ {800, 525,1000, 635} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1024x768Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1024x768Data_1[] = { {840, 438,1344, 806}, {840, 409,1344, 806}, @@ -1549,7 +1755,7 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1024x768Data_2[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1024x768Data_2[] = { {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1562,33 +1768,31 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1280x1024Data_1[]= -{ - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 518,1344, 806}, - {1050, 638,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} +static const SiS300_LVDSDataStruct SiS300_LVDS1280x1024Data_1[]= +{ + {1048, 442,1688,1066}, + {1048, 392,1688,1066}, + {1048, 442,1688,1066}, + {1048, 392,1688,1066}, + {1048, 522,1688,1066}, + {1208, 642,1688,1066}, + {1432, 810,1688,1066}, + {1688,1066,1688,1066} +}; + +static const SiS300_LVDSDataStruct SiS300_LVDS1280x1024Data_2[]= +{ + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1280x1024Data_2[]= -{ - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} -}; - -static const SiS300_LVDSDataStruct SiS300_LVDS1400x1050Data_1[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LVDS1400x1050Data_1[] = { {928, 416, 1688, 1066}, {928, 366, 1688, 1066}, @@ -1601,7 +1805,7 @@ {1688, 1066, 1688, 1066} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1400x1050Data_2[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LVDS1400x1050Data_2[] = { {1688,1066, 1688,1066}, {1688,1066, 1688,1066}, @@ -1614,8 +1818,61 @@ {1688,1066, 1688,1066}, }; -/* TW: New: */ -static const SiS300_LVDSDataStruct SiS300_LVDS1024x600Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1600x1200Data_1[]= +{ + {1088, 450, 2048,1250}, + {1088, 400, 2048,1250}, + {1088, 450, 2048,1250}, + {1088, 400, 2048,1250}, + {1088, 530, 2048,1250}, + {1248, 650, 2048,1250}, + {1472, 818, 2048,1250}, + {1728,1066, 2048,1250}, + {1848,1066, 2048,1250}, + {2048,1250, 2048,1250} +}; + +static const SiS300_LVDSDataStruct SiS300_LVDS1600x1200Data_2[]= +{ + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250} +}; + +static const SiS300_LVDSDataStruct SiS300_LVDS1280x768Data_1[]= +{ + { 768, 438, 1408, 806}, + { 768, 388, 1408, 806}, + { 768, 438, 1408, 806}, + { 768, 388, 1408, 806}, + { 768, 518, 1408, 806}, + { 928, 638, 1408, 806}, + {1152, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806} +}; + +static const SiS300_LVDSDataStruct SiS300_LVDS1280x768Data_2[]= +{ + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806} +}; + +static const SiS300_LVDSDataStruct SiS300_LVDS1024x600Data_1[] = { {840, 604,1344, 800}, {840, 560,1344, 800}, @@ -1628,8 +1885,7 @@ {800, 525,1280, 785} }; -/* TW: New: */ -static const SiS300_LVDSDataStruct SiS300_LVDS1024x600Data_2[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1024x600Data_2[] = { {1344, 800,1344, 800}, {1344, 800,1344, 800}, @@ -1642,8 +1898,7 @@ {800, 525,1280, 813} }; -/* TW: New: */ -static const SiS300_LVDSDataStruct SiS300_LVDS1152x768Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1152x768Data_1[] = { {840, 438,1344, 806}, {840, 409,1344, 806}, @@ -1656,8 +1911,7 @@ {800, 525,1280, 813} }; -/* TW: New: */ -static const SiS300_LVDSDataStruct SiS300_LVDS1152x768Data_2[]= +static const SiS300_LVDSDataStruct SiS300_LVDS1152x768Data_2[] = { {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1670,20 +1924,22 @@ {800, 525,1280, 813} }; -/* TW: New in 650/LVDS BIOS - resolution unknown */ -static const SiS300_LVDSDataStruct SiS300_LVDSXXXxXXXData_1[]= /* TW: New */ +/* TW: pass 1:1 data */ +static const SiS300_LVDSDataStruct SiS300_LVDSXXXxXXXData_1[]= { - { 800, 449, 800, 449}, - { 800, 449, 800, 449}, - { 900, 449, 900, 449}, - { 900, 449, 900, 449}, - { 800, 525, 800, 525}, - {1056, 628,1056, 628}, - {1344, 806,1344, 806}, - {1688, 806,1688, 806} + { 800, 449, 800, 449}, + { 800, 449, 800, 449}, + { 900, 449, 900, 449}, + { 900, 449, 900, 449}, + { 800, 525, 800, 525}, /* 640x480 */ + {1056, 628, 1056, 628}, /* 800x600 */ + {1344, 806, 1344, 806}, /* 1024x768 */ + {1344,1066, 1344,1066}, /* 1280x1024 */ /* INSERTED ! */ + {1688, 806, 1688, 806}, /* 1280x768 ! */ + /* No other panels ! */ }; -static const SiS300_LVDSDataStruct SiS300_LVDS640x480Data_1[]= +static const SiS300_LVDSDataStruct SiS300_LVDS640x480Data_1[] = { {800, 449, 800, 449}, {800, 449, 800, 449}, @@ -1696,7 +1952,7 @@ {1056, 628,1056, 628} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1280x960Data_1[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LVDS1280x960Data_1[] = /* TW: New */ { {840, 438,1344, 806}, {840, 409,1344, 806}, @@ -1709,7 +1965,7 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LVDS1280x960Data_2[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LVDS1280x960Data_2[] = /* TW: New */ { {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1722,7 +1978,7 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LCDA1400x1050Data_1[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LCDA1400x1050Data_1[] = /* TW: New */ { /* TW: Might be temporary (invalid) data */ {928, 416, 1688, 1066}, {928, 366, 1688, 1066}, @@ -1735,7 +1991,7 @@ {1688, 1066, 1688, 1066} }; -static const SiS300_LVDSDataStruct SiS300_LCDA1400x1050Data_2[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LCDA1400x1050Data_2[] = /* TW: New */ { /* TW: Temporary data. Not valid */ {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1748,7 +2004,7 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LCDA1600x1200Data_1[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LCDA1600x1200Data_1[] = /* TW: New */ { /* TW: Temporary data. Not valid */ {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1761,7 +2017,7 @@ {800, 525,1280, 813} }; -static const SiS300_LVDSDataStruct SiS300_LCDA1600x1200Data_2[]= /* TW: New */ +static const SiS300_LVDSDataStruct SiS300_LCDA1600x1200Data_2[] = /* TW: New */ { /* TW: Temporary data. Not valid */ {0, 0, 0, 0}, {0, 0, 0, 0}, @@ -1779,7 +2035,7 @@ /* TW: New: */ -static const SiS300_LVDSDataStruct SiS300_CHTVUNTSCData[]= +static const SiS300_LVDSDataStruct SiS300_CHTVUNTSCData[] = { {840, 600, 840, 600}, {840, 600, 840, 600}, @@ -1789,7 +2045,7 @@ {1064, 750,1064, 750} }; -static const SiS300_LVDSDataStruct SiS300_CHTVONTSCData[]= +static const SiS300_LVDSDataStruct SiS300_CHTVONTSCData[] = { {840, 525, 840, 525}, {840, 525, 840, 525}, @@ -1799,7 +2055,7 @@ {1040, 700,1040, 700} }; -static const SiS300_LVDSDataStruct SiS300_CHTVUPALData[]= +static const SiS300_LVDSDataStruct SiS300_CHTVUPALData[] = { {1008, 625,1008, 625}, {1008, 625,1008, 625}, @@ -1809,7 +2065,7 @@ {936, 836, 936, 836} }; -static const SiS300_LVDSDataStruct SiS300_CHTVOPALData[]= +static const SiS300_LVDSDataStruct SiS300_CHTVOPALData[] = { {1008, 625,1008, 625}, {1008, 625,1008, 625}, @@ -1818,6 +2074,17 @@ {840, 625, 840, 625}, {960, 750, 960, 750} }; + +static const SiS300_LVDSDataStruct SiS300_CHTVSOPALData[] = +{ + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {840, 500, 840, 500}, + {944, 625, 944, 625} +}; + /* TW: new end */ typedef struct _SiS300_LVDSDesStruct @@ -1826,7 +2093,7 @@ USHORT LCDVDES; } SiS300_LVDSDesStruct; -static const SiS300_LVDSDesStruct SiS300_PanelType00_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType00_1[] = { {0, 626}, {0, 624}, @@ -1839,7 +2106,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType01_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType01_1[] = { {1343, 798}, {1343, 794}, @@ -1852,7 +2119,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType02_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType02_1[] = { {0, 626}, {0, 624}, @@ -1865,7 +2132,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType03_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType03_1[] = { { 8, 436}, { 8, 440}, @@ -1878,7 +2145,7 @@ {1343, 794} }; -static const SiS300_LVDSDesStruct SiS300_PanelType04_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType04_1[] = { {1343, 798}, {1343, 794}, @@ -1891,7 +2158,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType05_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType05_1[] = { {1343, 798}, {1343, 794}, @@ -1904,7 +2171,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType06_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType06_1[] = { {1343, 798}, {1343, 794}, @@ -1917,7 +2184,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType07_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType07_1[] = { {1343, 798}, {1343, 794}, @@ -1930,7 +2197,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType08_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType08_1[] = { {1059, 626}, {1059, 624}, @@ -1943,7 +2210,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType09_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType09_1[] = { {1343, 798}, {1343, 794}, @@ -1956,7 +2223,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0a_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0a_1[] = { {1059, 626}, {1059, 624}, @@ -1969,7 +2236,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0b_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0b_1[] = { {1343, 0}, {1343, 0}, @@ -1982,7 +2249,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0c_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0c_1[] = { {1343, 798}, {1343, 794}, @@ -1995,7 +2262,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0d_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0d_1[] = { {1343, 798}, {1343, 794}, @@ -2008,7 +2275,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0e_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0e_1[] = { {1343, 798}, {1343, 794}, @@ -2021,7 +2288,7 @@ { 0, 0} /* 1280x960 - not applicable */ }; -static const SiS300_LVDSDesStruct SiS300_PanelType0f_1[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0f_1[] = { {1343, 798}, {1343, 794}, @@ -2034,7 +2301,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType00_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType00_2[] = { {976, 527}, {976, 502}, @@ -2047,7 +2314,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType01_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType01_2[] = { {1152, 622}, {1152, 597}, @@ -2060,7 +2327,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType02_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType02_2[] = { {976, 527}, {976, 502}, @@ -2073,7 +2340,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType03_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType03_2[] = { {1152, 622}, {1152, 597}, @@ -2086,7 +2353,7 @@ {1152, 597} }; -static const SiS300_LVDSDesStruct SiS300_PanelType04_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType04_2[] = { {1152, 622}, {1152, 597}, @@ -2099,7 +2366,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType05_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType05_2[] = { {1152, 622}, {1152, 597}, @@ -2112,7 +2379,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType06_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType06_2[] = { {1152, 622}, {1152, 597}, @@ -2125,7 +2392,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType07_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType07_2[] = { {1152, 622}, {1152, 597}, @@ -2138,7 +2405,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType08_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType08_2[] = { {976, 527}, {976, 502}, @@ -2151,7 +2418,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType09_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType09_2[] = { {1152, 622}, {1152, 597}, @@ -2164,7 +2431,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0a_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0a_2[] = { {976, 527}, {976, 502}, @@ -2177,7 +2444,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0b_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0b_2[] = { { 1152, 700}, { 1152, 675}, @@ -2190,7 +2457,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0c_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0c_2[] = { {1152, 622}, {1152, 597}, @@ -2203,7 +2470,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0d_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0d_2[] = { {1152, 622}, {1152, 597}, @@ -2216,7 +2483,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0e_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0e_2[] = { {1152, 622}, {1152, 597}, @@ -2229,7 +2496,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType0f_2[]= +static const SiS300_LVDSDesStruct SiS300_PanelType0f_2[] = { {1152, 622}, {1152, 597}, @@ -2242,7 +2509,35 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType1076_1[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelTypeNS_1[]= +{ + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 806}, + { 0, 0 } +}; + +static const SiS300_LVDSDesStruct SiS300_PanelTypeNS_2[] = +{ + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0} +}; + +static const SiS300_LVDSDesStruct SiS300_PanelType1076_1[] = /* TW: New */ { { 0 , 0}, { 0 , 0}, @@ -2255,7 +2550,7 @@ { 0 , 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType1076_2[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelType1076_2[] = /* TW: New */ { { 1152, 622 }, { 1152, 597 }, @@ -2268,7 +2563,7 @@ { 0, 0 } }; -static const SiS300_LVDSDesStruct SiS300_PanelType1210_1[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelType1210_1[] = /* TW: New */ { { 0 , 0}, { 0 , 0}, @@ -2281,7 +2576,7 @@ { 0 , 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType1210_2[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelType1210_2[] = /* TW: New */ { { 0 , 0}, { 0 , 0}, @@ -2294,7 +2589,7 @@ { 0 , 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType1296_1[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelType1296_1[] = /* TW: New */ { { 0 , 0}, { 0 , 0}, @@ -2307,7 +2602,7 @@ { 0 , 0} }; -static const SiS300_LVDSDesStruct SiS300_PanelType1296_2[]= /* TW: New */ +static const SiS300_LVDSDesStruct SiS300_PanelType1296_2[] = /* TW: New */ { { 0 , 0}, { 0 , 0}, @@ -2322,7 +2617,7 @@ /* TW: New */ -static const SiS300_LVDSDesStruct SiS300_CHTVUNTSCDesData[]= +static const SiS300_LVDSDesStruct SiS300_CHTVUNTSCDesData[] = { { 0, 0}, { 0, 0}, @@ -2332,7 +2627,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_CHTVONTSCDesData[]= +static const SiS300_LVDSDesStruct SiS300_CHTVONTSCDesData[] = { { 0, 0}, { 0, 0}, @@ -2342,7 +2637,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_CHTVUPALDesData[]= +static const SiS300_LVDSDesStruct SiS300_CHTVUPALDesData[] = { {256, 0}, {256, 0}, @@ -2352,7 +2647,7 @@ { 0, 0} }; -static const SiS300_LVDSDesStruct SiS300_CHTVOPALDesData[]= +static const SiS300_LVDSDesStruct SiS300_CHTVOPALDesData[] = { {256, 0}, {256, 0}, @@ -2363,12 +2658,90 @@ }; /* TW: New end */ +/* TW: New for SiS300+301LV */ +typedef struct _SiS300_Part2PortTblStruct +{ + UCHAR CR[12]; +} SiS300_Part2PortTblStruct; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1024x768_1[] = +{ /* VESA Timing */ + {{0x21,0x12,0xbf,0xe4,0xc0,0x21,0x45,0x09,0x00,0xa9,0x09,0x04}}, + {{0x2c,0x12,0x9a,0xae,0x88,0x21,0x45,0x09,0x00,0xa9,0x09,0x04}}, + {{0x21,0x12,0xbf,0xe4,0xc0,0x21,0x45,0x09,0x00,0xa9,0x09,0x04}}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, + {{0x22,0x13,0xfe,0x25,0xff,0x21,0x45,0x0a,0x00,0xa9,0x0d,0x04}}, + {{0x22,0x13,0xfe,0x25,0xff,0x21,0x45,0x0a,0x00,0xa9,0x0d,0x04}}, + {{0x22,0x13,0xfe,0x25,0xff,0x21,0x45,0x0a,0x00,0xa9,0x0d,0x04}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1280x1024_1[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1400x1050_1[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1600x1200_1[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1024x768_2[] = +{ /* Non-VESA */ + {{0x28,0x12,0xa3,0xd0,0xaa,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x2c,0x12,0x9a,0xae,0x88,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x28,0x12,0xa3,0xd0,0xaa,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x2c,0x12,0x9a,0xae,0x88,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x28,0x13,0xe7,0x0b,0xe8,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x38,0x18,0x16,0x00,0x00,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}}, + {{0x36,0x13,0x13,0x25,0xff,0x5a,0x45,0x0a,0x07,0xfa,0x0a,0x24}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1280x1024_2[] = +{ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1400x1050_2[] = +{ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1600x1200_2[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1024x768_3[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1280x1024_3[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1400x1050_3[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + +static const SiS300_Part2PortTblStruct SiS300_CRT2Part2_1600x1200_3[] = +{ /* TW: Temporary data, invalid */ + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} +}; + typedef struct _SiS300_LVDSCRT1DataStruct { UCHAR CR[15]; } SiS300_LVDSCRT1DataStruct; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_1[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_1[] = { {{0x65,0x4f,0x89,0x56,0x83,0xaf,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, @@ -2390,7 +2763,29 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_1[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_1_H[] = +{ + {{0x30,0x27,0x94,0x2c,0x92,0xaf,0x1f, + 0x90,0x85,0x8f,0xab,0x30,0x00,0x04, + 0x00 }}, + {{0x30,0x27,0x94,0x2c,0x92,0x83,0x1f, + 0x5e,0x83,0x5d,0x79,0x10,0x00,0x04, + 0x00 }}, + {{0x30,0x27,0x94,0x2c,0x92,0xaf,0x1f, + 0x90,0x85,0x8f,0xab,0x30,0x00,0x04, + 0x00 }}, + {{0x30,0x27,0x94,0x2c,0x92,0x83,0x1f, + 0x5e,0x83,0x5d,0x79,0x10,0x00,0x04, + 0x00 }}, + {{0x30,0x27,0x94,0x2c,0x92,0x04,0x3e, + 0xe0,0x85,0xdf,0xfb,0x10,0x00,0x04, + 0x00 }}, + {{0x3d,0x31,0x81,0x37,0x1f,0x72,0xf0, + 0x58,0x8c,0x57,0x73,0x20,0x00,0x05, + 0x01 }} +}; + +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_1[] = { {{0x64,0x4f,0x88,0x54,0x9f,0xc4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, @@ -2412,58 +2807,34 @@ 0x01}}, {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, 0x02,0x88,0xff,0x25,0x10,0x00,0x02, - 0x01} } -}; - -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_1[]= -{ - {{0x63,0x4f,0x87,0x54,0x9f,0xb4,0x1f, - 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, - 0x00 }}, - {{0x63,0x4f,0x87,0x54,0x9f,0x82,0x1f, - 0x60,0x87,0x5d,0x83,0x10,0x00,0x01, - 0x00 }}, - {{0x63,0x4f,0x87,0x54,0x9f,0xb4,0x1f, - 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, - 0x00 }}, - {{0x63,0x4f,0x87,0x54,0x9f,0x82,0x1f, - 0x60,0x87,0x5d,0x83,0x10,0x00,0x01, - 0x00 }}, - {{0x63,0x4f,0x87,0x54,0x9f,0x04,0x3e, - 0xe2,0x89,0xdf,0x05,0x00,0x00,0x01, - 0x00 }}, - {{0x7e,0x63,0x82,0x68,0x15,0x7c,0xf0, - 0x5a,0x8f,0x57,0x7d,0x20,0x00,0x26, - 0x01 }}, - {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, - 0x02,0x88,0xff,0x25,0x10,0x00,0x02, - 0x01 }} + 0x01}} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_1_H[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_1_H[] = { - {{0x30,0x27,0x94,0x2c,0x92,0xaf,0x1f, - 0x90,0x85,0x8f,0xab,0x30,0x00,0x04, - 0x00 }}, - {{0x30,0x27,0x94,0x2c,0x92,0x83,0x1f, - 0x5e,0x83,0x5d,0x79,0x10,0x00,0x04, - 0x00 }}, - {{0x30,0x27,0x94,0x2c,0x92,0xaf,0x1f, - 0x90,0x85,0x8f,0xab,0x30,0x00,0x04, - 0x00 }}, - {{0x30,0x27,0x94,0x2c,0x92,0x83,0x1f, - 0x5e,0x83,0x5d,0x79,0x10,0x00,0x04, - 0x00 }}, - {{0x30,0x27,0x94,0x2c,0x92,0x04,0x3e, - 0xe0,0x85,0xdf,0xfb,0x10,0x00,0x04, + {{0x2f,0x27,0x93,0x2b,0x90,0xc4,0x1f, + 0x92,0x89,0x8f,0xb5,0x30,0x00,0x44, 0x00 }}, - {{0x3d,0x31,0x81,0x37,0x1f,0x72,0xf0, - 0x58,0x8c,0x57,0x73,0x20,0x00,0x05, + {{0x2f,0x27,0x93,0x2b,0x90,0x97,0x1f, + 0x60,0x87,0x5D,0x83,0x10,0x00,0x44, + 0x00}}, + {{0x2f,0x27,0x93,0x2b,0x90,0xc4,0x1f, + 0x92,0x89,0x8f,0xb5,0x30,0x00,0x44, + 0x00}}, + {{0x2f,0x27,0x93,0x2b,0x90,0x97,0x1f, + 0x60,0x87,0x5D,0x83,0x10,0x00,0x44, + 0x00}}, + {{0x2f,0x27,0x93,0x2b,0x90,0x04,0x3e, + 0xE2,0x89,0xdf,0x05,0x00,0x00,0x44, + 0x00}}, + {{0x3c,0x31,0x80,0x35,0x1c,0x7c,0xf0, + 0x5A,0x8F,0x57,0x7D,0x20,0x00,0x55, + 0x01}}, + {{0x4f,0x3F,0x93,0x45,0x0D,0x24,0xf5, + 0x02,0x88,0xff,0x25,0x10,0x00,0x01, 0x01 }} -}; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_1_H[]= -{ +#if 0 {{0x37,0x27,0x9B,0x2b,0x94,0xc4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x44, 0x00 }}, @@ -2485,9 +2856,35 @@ {{0x4f,0x3F,0x93,0x45,0x0D,0x24,0xf5, 0x02,0x88,0xFf,0x25,0x10,0x00,0x01, 0x01 }} +#endif +}; + +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_1[] = +{ + {{0x63,0x4f,0x87,0x54,0x9f,0xb4,0x1f, + 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, + 0x00 }}, + {{0x63,0x4f,0x87,0x54,0x9f,0x82,0x1f, + 0x60,0x87,0x5d,0x83,0x10,0x00,0x01, + 0x00 }}, + {{0x63,0x4f,0x87,0x54,0x9f,0xb4,0x1f, + 0x92,0x89,0x8f,0xb5,0x30,0x00,0x01, + 0x00 }}, + {{0x63,0x4f,0x87,0x54,0x9f,0x82,0x1f, + 0x60,0x87,0x5d,0x83,0x10,0x00,0x01, + 0x00 }}, + {{0x63,0x4f,0x87,0x54,0x9f,0x04,0x3e, + 0xe2,0x89,0xdf,0x05,0x00,0x00,0x01, + 0x00 }}, + {{0x7e,0x63,0x82,0x68,0x15,0x7c,0xf0, + 0x5a,0x8f,0x57,0x7d,0x20,0x00,0x26, + 0x01 }}, + {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, + 0x02,0x88,0xff,0x25,0x10,0x00,0x02, + 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_1_H[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_1_H[] = { {{0x2f,0x27,0x93,0x2b,0x90,0xb4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x04, @@ -2512,7 +2909,7 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_2[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_2[] = { {{0x7f,0x4f,0x83,0x62,0x12,0x72,0x3e, 0xf4,0x88,0x8f,0x73,0x20,0x00,0x06, @@ -2534,32 +2931,29 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_2[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_2_H[] = { - {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, - 0x4a,0x80,0x8f,0x25,0x30,0x00,0x06, + {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, + 0xf4,0x88,0x8f,0x73,0x20,0x00,0x05, 0x00 }}, - {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, - 0x31,0x87,0x5d,0x25,0x30,0x00,0x06, + {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, + 0xdb,0x8f,0x5d,0x73,0x20,0x00,0x05, 0x00 }}, - {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, - 0x4a,0x80,0x8f,0x25,0x30,0x00,0x06, + {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, + 0xf4,0x88,0x8f,0x73,0x20,0x00,0x05, 0x00 }}, - {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, - 0x31,0x87,0x5d,0x25,0x30,0x00,0x06, + {{0x3d,0x27,0x81,0x3a,0x1a,0x72,0x3e, + 0xdb,0x8f,0x5d,0x73,0x20,0x00,0x05, 0x00 }}, - {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, - 0x72,0x88,0xdf,0x25,0x30,0x00,0x06, + {{0x3d,0x27,0x81,0x32,0x1a,0x72,0xba, + 0x1c,0x80,0xdf,0x73,0x00,0x00,0x05, 0x00 }}, - {{0xa3,0x63,0x87,0x78,0x89,0x24,0xf1, - 0xae,0x84,0x57,0x25,0x30,0x00,0x02, - 0x01 }}, - {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, - 0x02,0x88,0xff,0x25,0x10,0x00,0x02, + {{0x3d,0x31,0x81,0x37,0x1f,0x72,0xf0, + 0x58,0x8c,0x57,0x73,0x20,0x00,0x05, 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_2[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_2[] = { {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, 0x4a,0x80,0x8f,0x25,0x30,0x00,0x06, @@ -2584,29 +2978,7 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT1800x600_2_H[]= -{ - {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, - 0xf4,0x88,0x8f,0x73,0x20,0x00,0x05, - 0x00 }}, - {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, - 0xdb,0x8f,0x5d,0x73,0x20,0x00,0x05, - 0x00 }}, - {{0x3d,0x27,0x81,0x32,0x1a,0x72,0x3e, - 0xf4,0x88,0x8f,0x73,0x20,0x00,0x05, - 0x00 }}, - {{0x3d,0x27,0x81,0x3a,0x1a,0x72,0x3e, - 0xdb,0x8f,0x5d,0x73,0x20,0x00,0x05, - 0x00 }}, - {{0x3d,0x27,0x81,0x32,0x1a,0x72,0xba, - 0x1c,0x80,0xdf,0x73,0x00,0x00,0x05, - 0x00 }}, - {{0x3d,0x31,0x81,0x37,0x1f,0x72,0xf0, - 0x58,0x8c,0x57,0x73,0x20,0x00,0x05, - 0x01 }} -}; - -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_2_H[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11024x768_2_H[] = { {{0x4f,0x27,0x93,0x39,0x01,0x24,0xbb, 0x4a,0x80,0x8f,0x25,0x30,0x00,0x01, @@ -2631,7 +3003,32 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_2_H[]= +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_2[] = +{ + {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, + 0x4a,0x80,0x8f,0x25,0x30,0x00,0x06, + 0x00 }}, + {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, + 0x31,0x87,0x5d,0x25,0x30,0x00,0x06, + 0x00 }}, + {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, + 0x4a,0x80,0x8f,0x25,0x30,0x00,0x06, + 0x00 }}, + {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, + 0x31,0x87,0x5d,0x25,0x30,0x00,0x06, + 0x00 }}, + {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, + 0x72,0x88,0xdf,0x25,0x30,0x00,0x06, + 0x00 }}, + {{0xa3,0x63,0x87,0x78,0x89,0x24,0xf1, + 0xae,0x84,0x57,0x25,0x30,0x00,0x02, + 0x01 }}, + {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, + 0x02,0x88,0xff,0x25,0x10,0x00,0x02, + 0x01 }} +}; + +static const SiS300_LVDSCRT1DataStruct SiS300_LVDSCRT11280x1024_2_H[] = { {{0x4f,0x27,0x93,0x39,0x81,0x24,0xbb, 0x4a,0x80,0x8f,0x25,0x30,0x00,0x01, @@ -2857,7 +3254,7 @@ }; /* TW: New */ -static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1UNTSC[]= +static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1UNTSC[] = { {{0x64,0x4f,0x88,0x56,0x9f,0x56,0x3e, 0xe8,0x84,0x8f,0x57,0x20,0x00,0x01, @@ -2879,7 +3276,7 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1ONTSC[]= +static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1ONTSC[] = { {{0x64,0x4f,0x88,0x5a,0x9f,0x0b,0x3e, 0xc0,0x84,0x8f,0x0c,0x20,0x00,0x01, @@ -2901,7 +3298,7 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1UPAL[]= +static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1UPAL[] = { {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, 0xf8,0x83,0x8f,0x70,0x20,0x00,0x05, @@ -2923,7 +3320,7 @@ 0x01 }} }; -static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1OPAL[]= +static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1OPAL[] = { {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, @@ -2944,6 +3341,28 @@ 0x90,0x8c,0x57,0xed,0x20,0x00,0x05, 0x01 }} }; + +static const SiS300_LVDSCRT1DataStruct SiS300_CHTVCRT1SOPAL[] = +{ + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xde,0x81,0x5d,0x70,0x00,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xde,0x81,0x5d,0x70,0x00,0x00,0x05, + 0x00 }}, + {{0x64,0x4f,0x88,0x55,0x80,0x6f,0xba, /* TODO */ + 0x20,0x83,0xdf,0x70,0x00,0x00,0x05, + 0x00 }}, + {{0x73,0x63,0x97,0x69,0x8e,0xec,0xf0, /* TODO */ + 0x90,0x8c,0x57,0xed,0x20,0x00,0x05, + 0x01 }} +}; /* TW: New end */ /* TW: New */ @@ -2952,7 +3371,8 @@ UCHAR Reg[16]; } SiS300_CHTVRegDataStruct; -static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_UNTSC[] = { +static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_UNTSC[] = +{ {{0x4a,0x94,0x00,0x48,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, {{0x4a,0x94,0x00,0x48,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, {{0x4a,0x94,0x00,0x48,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, @@ -2961,7 +3381,8 @@ {{0x8d,0xc4,0x00,0x3b,0xfb,0,0,0,0,0,0,0,0,0,0,0}} /* Mode 24: 800x600 NTSC 7/10 */ }; -static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_ONTSC[] = { +static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_ONTSC[] = +{ {{0x49,0x94,0x00,0x34,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, {{0x49,0x94,0x00,0x34,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, {{0x49,0x94,0x00,0x34,0xfe,0,0,0,0,0,0,0,0,0,0,0}}, @@ -2970,37 +3391,51 @@ {{0x8c,0xb4,0x00,0x32,0xf9,0,0,0,0,0,0,0,0,0,0,0}} /* Mode 23: 800x600 NTSC 3/4 */ }; -static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_UPAL[] = { +static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_UPAL[] = +{ {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, {{0x63,0x94,0x01,0x50,0x30,0,0,0,0,0,0,0,0,0,0,0}}, /* Mode 15: 640x480 PAL 5/6 */ - /* TW: For 800x600, 3/4 is VERY underscan */ {{0x84,0x64,0x01,0x4e,0x2f,0,0,0,0,0,0,0,0,0,0,0}} /* Mode 21: 800x600 PAL 3/4 */ - /* TW: Mode 20 is still underscan, use it instead? */ - /* {{0x83,0x76,0x01,0x40,0x31}} */ /* Mode 20: 800x600 PAL 5/6 */ + }; -static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_OPAL[] = { +static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_OPAL[] = +{ {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, /* Mode 9: 640x400 PAL 1/1 */ {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, {{0x61,0x94,0x01,0x36,0x30,0,0,0,0,0,0,0,0,0,0,0}}, /* Mode 14: 640x480 PAL 1/1 */ {{0x83,0x76,0x01,0x40,0x31,0,0,0,0,0,0,0,0,0,0,0}} /* Mode 20: 800x600 PAL 5/6 */ - /* {{0x81,0x12,0x01,0x50,0x34}} */ /* TW: (test) Mode 19: 800x600 PAL 1/1 */ + +}; + +static const SiS300_CHTVRegDataStruct SiS300_CHTVReg_SOPAL[] = +{ + {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, /* Mode 9: 640x400 PAL 5/4 */ + {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, + {{0x41,0x12,0x01,0x50,0x34,0,0,0,0,0,0,0,0,0,0,0}}, + {{0x41,0x12,0x00,0x50,0x00,0,0,0,0,0,0,0,0,0,0,0}}, + {{0x60,0x30,0x00,0x10,0x00,0,0,0,0,0,0,0,0,0,0,0}}, /* TW: Mode 13: 640x480 PAL 5/4 */ + {{0x81,0x50,0x00,0x1b,0x00,0,0,0,0,0,0,0,0,0,0,0}} /* TW: Mode 19: 800x600 PAL 1/1 */ }; /* TW: New end */ /* TW: New */ -static const UCHAR SiS300_CHTVVCLKUNTSC[] = {0x29,0x29,0x29,0x29,0x2a,0x2e}; +static const UCHAR SiS300_CHTVVCLKUNTSC[] = {0x29,0x29,0x29,0x29,0x2a,0x2e}; + +static const UCHAR SiS300_CHTVVCLKONTSC[] = {0x2c,0x2c,0x2c,0x2c,0x2d,0x2b}; + +static const UCHAR SiS300_CHTVVCLKSONTSC[] = {0x2c,0x2c,0x2c,0x2c,0x2d,0x2b}; -static const UCHAR SiS300_CHTVVCLKONTSC[] = {0x2c,0x2c,0x2c,0x2c,0x2d,0x2b}; +static const UCHAR SiS300_CHTVVCLKUPAL[] = {0x2f,0x2f,0x2f,0x2f,0x2f,0x31}; -static const UCHAR SiS300_CHTVVCLKUPAL[] = {0x2f,0x2f,0x2f,0x2f,0x2f,0x31}; +static const UCHAR SiS300_CHTVVCLKOPAL[] = {0x2f,0x2f,0x2f,0x2f,0x30,0x32}; -static const UCHAR SiS300_CHTVVCLKOPAL[] = {0x2f,0x2f,0x2f,0x2f,0x30,0x32}; +static const UCHAR SiS300_CHTVVCLKSOPAL[] = {0x2f,0x2f,0x2f,0x2f,0x36,0x29}; /* TW: New end */ diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/sis/310vtbl.h linux.21rc1-ac2/drivers/video/sis/310vtbl.h --- linux.21rc1/drivers/video/sis/310vtbl.h 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/sis/310vtbl.h 2003-04-22 16:44:37.000000000 +0100 @@ -1,6 +1,6 @@ -/* Register settings for SiS 310/325 series */ +/* Register settings for SiS 310/325/330 series */ typedef struct _SiS310_StStruct @@ -54,7 +54,7 @@ static const SiS310_StandTableStruct SiS310_StandTable[]= { -/* MD_0_200 */ +/* 0x00: MD_0_200 */ { 0x28,0x18,0x08,0x0800, {0x09,0x03,0x00,0x02}, @@ -69,7 +69,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_1_200 */ +/* 0x01: MD_1_200 */ { 0x28,0x18,0x08,0x0800, {0x09,0x03,0x00,0x02}, @@ -84,7 +84,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_2_200 */ +/* 0x02: MD_2_200 */ { 0x50,0x18,0x08,0x1000, {0x01,0x03,0x00,0x02}, @@ -99,7 +99,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_3_200 */ +/* 0x03: MD_3_200 - mode 0x03 - 0 */ { 0x50,0x18,0x08,0x1000, {0x01,0x03,0x00,0x02}, @@ -114,7 +114,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_4 */ +/* 0x04: MD_4 */ { 0x28,0x18,0x08,0x4000, {0x09,0x03,0x00,0x02}, @@ -129,7 +129,7 @@ {0x00,0x00,0x00,0x00,0x00,0x30,0x0f,0x00, 0xff} }, -/* MD_5 */ +/* 0x05: MD_5 */ { 0x28,0x18,0x08,0x4000, {0x09,0x03,0x00,0x02}, @@ -144,7 +144,7 @@ {0x00,0x00,0x00,0x00,0x00,0x30,0x0f,0x00, 0xff} }, -/* MD_6 */ +/* 0x06: MD_6 */ { 0x50,0x18,0x08,0x4000, {0x01,0x01,0x00,0x06}, @@ -159,7 +159,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00, 0xff} }, -/* MD_7 */ +/* 0x07: MD_7 */ { 0x50,0x18,0x0e,0x1000, {0x00,0x03,0x00,0x03}, @@ -174,7 +174,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0a,0x00, 0xff} }, -/* MDA_DAC */ +/* 0x08: MDA_DAC */ { 0x00,0x00,0x00,0x0000, {0x00,0x00,0x00,0x15}, @@ -189,7 +189,7 @@ {0x15,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f} }, -/* CGA_DAC */ +/* 0x09: CGA_DAC */ { 0x00,0x10,0x04,0x0114, {0x11,0x09,0x15,0x00}, @@ -204,7 +204,7 @@ {0x3f,0x2a,0x3a,0x2e,0x3e,0x2b,0x3b,0x2f, 0x3f} }, -/* EGA_DAC */ +/* 0x0a: EGA_DAC */ { 0x00,0x10,0x04,0x0114, {0x11,0x05,0x15,0x20}, @@ -219,7 +219,7 @@ {0x1f,0x2a,0x3a,0x2e,0x3e,0x2b,0x3b,0x2f, 0x3f} }, -/* VGA_DAC */ +/* 0x0b: VGA_DAC */ { 0x00,0x10,0x04,0x0114, {0x11,0x09,0x15,0x2a}, @@ -234,6 +234,7 @@ {0x18,0x1c,0x14,0x16,0x18,0x1a,0x1c,0x00, 0x04} }, +/* 0x0c */ { 0x08,0x0c,0x10,0x0a08, {0x0c,0x0e,0x10,0x0b}, @@ -248,7 +249,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00} }, -/* MD_D */ +/* 0x0d: MD_D */ { 0x28,0x18,0x08,0x2000, {0x09,0x0f,0x00,0x06}, @@ -263,7 +264,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, -/* MD_E */ +/* 0x0e: MD_E */ { 0x50,0x18,0x08,0x4000, {0x01,0x0f,0x00,0x06}, @@ -278,7 +279,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, -/* ExtVGATable */ +/* 0x0f: ExtVGATable - modes > 0x13 */ { 0x00,0x00,0x00,0x0000, {0x01,0x0f,0x00,0x0e}, @@ -293,7 +294,7 @@ {0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, 0xff} }, -/* ROM_SAVEPTR */ +/* 0x10: ROM_SAVEPTR */ { 0x9f,0x3b,0x00,0x00c0, {0x00,0x00,0x00,0x00}, @@ -308,7 +309,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00} }, -/* MD_F */ +/* 0x11: MD_F */ { 0x50,0x18,0x0e,0x8000, {0x01,0x0f,0x00,0x06}, @@ -323,7 +324,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05, 0xff} }, -/* MD_10 */ +/* 0x12: MD_10 */ { 0x50,0x18,0x0e,0x8000, {0x01,0x0f,0x00,0x06}, @@ -338,7 +339,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, -/* MD_0_350 */ +/* 0x13: MD_0_350 */ { 0x28,0x18,0x0e,0x0800, {0x09,0x03,0x00,0x02}, @@ -353,7 +354,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_1_350 */ +/* 0x14: MD_1_350 */ { 0x28,0x18,0x0e,0x0800, {0x09,0x03,0x00,0x02}, @@ -368,7 +369,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_2_350 */ +/* 0x15: MD_2_350 */ { 0x50,0x18,0x0e,0x1000, {0x01,0x03,0x00,0x02}, @@ -383,7 +384,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_3_350 */ +/* 0x16: MD_3_350 - mode 0x03 - 1 */ { 0x50,0x18,0x0e,0x1000, {0x01,0x03,0x00,0x02}, @@ -398,7 +399,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_0_1_400 */ +/* 0x17: MD_0_1_400 */ { 0x28,0x18,0x10,0x0800, {0x08,0x03,0x00,0x02}, @@ -413,7 +414,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_2_3_400 */ +/* 0x18: MD_2_3_400 - mode 0x03 - 2 */ { 0x50,0x18,0x10,0x1000, {0x00,0x03,0x00,0x02}, @@ -428,7 +429,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00, 0xff} }, -/* MD_7_400 */ +/* 0x19: MD_7_400 */ { 0x50,0x18,0x10,0x1000, {0x00,0x03,0x00,0x02}, @@ -443,7 +444,7 @@ {0x00,0x00,0x00,0x00,0x00,0x10,0x0a,0x00, 0xff} }, -/* MD_11 */ +/* 0x1a: MD_11 */ { 0x50,0x1d,0x10,0xa000, {0x01,0x0f,0x00,0x06}, @@ -458,7 +459,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x01, 0xff} }, -/* ExtEGATable */ +/* 0x1b: ExtEGATable - Modes <= 0x02 */ { 0x50,0x1d,0x10,0xa000, {0x01,0x0f,0x00,0x06}, @@ -473,7 +474,7 @@ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f, 0xff} }, -/* MD_13 */ +/* 0x1c: MD_13 */ { 0x28,0x18,0x08,0x2000, {0x01,0x0f,0x00,0x0e}, @@ -522,45 +523,38 @@ {0x34,0x2a1d,0x0a0e,0x3b8c,0x0000,0x08,0x0e,0x00,0x00,0x06,0x12}, /* 720x576x16 */ {0x35,0x0a1f,0x0a0d,0x3b85,0x0000,0x08,0x0d,0x00,0x00,0x06,0x11}, /* 720x480x32 */ {0x36,0x2a1f,0x0a0e,0x3b8c,0x0000,0x08,0x0e,0x00,0x00,0x06,0x12}, /* 720x576x32 */ - {0x37,0x0212,0x0508,0x3aab,0x0104,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x? */ - {0x38,0x0a1b,0x0508,0x3aab,0x0105,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x8 */ -/* {0x38,0x021b,0x0508,0x3aab,0x0105,0x08,0x08,0x00,0x00,0x00,0x13}, */ /* 1024x768x8 - 650/LVDS BIOS (no CRt2Mode) */ + {0x37,0x0212,0x0508,0x3aab,0x0104,0x08,0x08,0x00,0x00,0x08,0x13}, /* 1024x768x? */ + {0x38,0x0a1b,0x0508,0x3aab,0x0105,0x08,0x08,0x00,0x00,0x08,0x13}, /* 1024x768x8 */ {0x3a,0x0e3b,0x0609,0x3adc,0x0107,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 1280x1024x8 */ -/* {0x3a,0x063b,0x0609,0x3adc,0x0107,0x08,0x09,0x00,0x00,0x00,0x1a}, */ /* 1280x1024x8 - 650/LVDS BIOS*/ {0x3c,0x0e3b,0x070a,0x3af2,0x0130,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 1600x1200x8 */ -/* {0x3c,0x063b,0x070a,0x3af2,0x0130,0x08,0x0a,0x00,0x00,0x00,0x1e}, */ /* 1600x1200x8 - 650/LVDS BIOS */ - {0x3d,0x067d,0x070a,0x3af2,0x0131,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 1600x1200x16 - 650/301LVx - no CRT2Mode? */ - {0x40,0x9a1c,0x0000,0x3a34,0x010d,0x08,0x00,0x00,0x00,0x04,0x25}, - {0x41,0x9a1d,0x0000,0x3a34,0x010e,0x08,0x00,0x00,0x00,0x04,0x25}, + {0x3d,0x0e7d,0x070a,0x3af2,0x0131,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 1600x1200x16 - 650/301LVx - no CRT2Mode? */ + {0x40,0x9a1c,0x0000,0x3a34,0x010d,0x08,0x00,0x00,0x00,0x04,0x25}, /* 320x200x15 */ + {0x41,0x9a1d,0x0000,0x3a34,0x010e,0x08,0x00,0x00,0x00,0x04,0x25}, /* 320x200x16 */ {0x43,0x0a1c,0x0306,0x3a57,0x0110,0x08,0x06,0x00,0x00,0x05,0x08}, {0x44,0x0a1d,0x0306,0x3a57,0x0111,0x08,0x06,0x00,0x00,0x05,0x08}, /* 640x480x16 */ {0x46,0x2a1c,0x0407,0x3a81,0x0113,0x08,0x07,0x00,0x00,0x07,0x00}, {0x47,0x2a1d,0x0407,0x3a81,0x0114,0x08,0x07,0x00,0x00,0x07,0x00}, /* 800x600x16 */ {0x49,0x0a3c,0x0508,0x3aab,0x0116,0x08,0x08,0x00,0x00,0x00,0x13}, - {0x4a,0x0a3d,0x0508,0x3aab,0x0117,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x16 */ + {0x4a,0x0a3d,0x0508,0x3aab,0x0117,0x08,0x08,0x00,0x00,0x08,0x13}, /* 1024x768x16 */ {0x4c,0x0e7c,0x0609,0x3adc,0x0119,0x08,0x09,0x00,0x00,0x00,0x1a}, {0x4d,0x0e7d,0x0609,0x3adc,0x011a,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 1280x1024x16 */ - {0x50,0x9a1b,0x0001,0x3a3b,0x0132,0x08,0x01,0x00,0x00,0x04,0x26}, -/* {0x50,0x921b,0x0001,0x3a3b,0x0132,0x08,0x01,0x00,0x00,0x04,0x26}, */ /* 650/LVDS BIOS */ - {0x51,0xba1b,0x0103,0x3a42,0x0133,0x08,0x03,0x00,0x00,0x07,0x27}, -/* {0x52,0x9a1b,0x0204,0x3a49,0x0134,0x08,0x04,0x00,0x00,0x00,0x28}, */ - {0x52,0xba1b,0x0204,0x3a49,0x0134,0x08,0x04,0x00,0x00,0x00,0x28}, /* 650/301 BIOS */ -/* {0x52,0xb21b,0x0204,0x3a49,0x0134,0x08,0x04,0x00,0x00,0x00,0x28}, */ /* 650/LVDS BIOS (no CRT2Mode) */ - {0x56,0x9a1d,0x0001,0x3a3b,0x0135,0x08,0x01,0x00,0x00,0x04,0x26}, - {0x57,0xba1d,0x0103,0x3a42,0x0136,0x08,0x03,0x00,0x00,0x07,0x27}, -/* {0x58,0x9a1d,0x0204,0x3a49,0x0137,0x08,0x04,0x00,0x00,0x00,0x28}, */ - {0x58,0xba1d,0x0204,0x3a49,0x0137,0x08,0x04,0x00,0x00,0x00,0x28}, /* BIOS (301+LVDS) */ - {0x59,0x9a1b,0x0000,0x3a34,0x0138,0x08,0x00,0x00,0x00,0x04,0x25}, -/* {0x59,0x921b,0x0000,0x3a34,0x0138,0x08,0x00,0x00,0x00,0x04,0x25}, */ /* 650/LVDS BIOS (no CRT2Mode) */ + {0x50,0x9a1b,0x0001,0x3a3b,0x0132,0x08,0x01,0x00,0x00,0x04,0x26}, /* 320x240x8 */ + {0x51,0xba1b,0x0103,0x3a42,0x0133,0x08,0x03,0x00,0x00,0x07,0x27}, /* 400x300x8 */ + {0x52,0xba1b,0x0204,0x3a49,0x0134,0x08,0x04,0x00,0x00,0x00,0x28}, /* 512x384x8 */ + {0x56,0x9a1d,0x0001,0x3a3b,0x0135,0x08,0x01,0x00,0x00,0x04,0x26}, /* 320x240x16 */ + {0x57,0xba1d,0x0103,0x3a42,0x0136,0x08,0x03,0x00,0x00,0x07,0x27}, /* 400x300x16 */ + {0x58,0xba1d,0x0204,0x3a49,0x0137,0x08,0x04,0x00,0x00,0x00,0x28}, /* 512x384x16 */ + {0x59,0x9a1b,0x0000,0x3a34,0x0138,0x08,0x00,0x00,0x00,0x04,0x25}, /* 320x200x8 */ {0x5A,0x021b,0x0014,0x3b83,0x0138,0x08,0x01,0x00,0x00,0x04,0x3f}, /* 320x480x8 fstn add new mode*/ {0x5B,0x0a1d,0x0014,0x3b83,0x0135,0x08,0x01,0x00,0x00,0x04,0x3f}, /* 320x480x16 fstn add new mode*/ {0x5c,0xba1f,0x0204,0x3a49,0x0000,0x08,0x04,0x00,0x00,0x00,0x28}, /* TW: inserted 512x384x32 */ {0x5d,0x0a1d,0x0305,0x3a50,0x0139,0x08,0x05,0x00,0x00,0x07,0x10}, + {0x5e,0x0a1f,0x0305,0x3a50,0x0000,0x08,0x05,0x00,0x00,0x07,0x10}, /* TW: Inserted 640x400x32 */ {0x62,0x0a3f,0x0306,0x3a57,0x013a,0x08,0x06,0x00,0x00,0x05,0x08}, /* 640x480x32 */ {0x63,0x2a3f,0x0407,0x3a81,0x013b,0x08,0x07,0x00,0x00,0x07,0x00}, /* 800x600x32 */ - {0x64,0x0a7f,0x0508,0x3aab,0x013c,0x08,0x08,0x00,0x00,0x00,0x13}, /* 1024x768x32 */ + {0x64,0x0a7f,0x0508,0x3aab,0x013c,0x08,0x08,0x00,0x00,0x08,0x13}, /* 1024x768x32 */ {0x65,0x0eff,0x0609,0x3adc,0x013d,0x08,0x09,0x00,0x00,0x00,0x1a}, /* 1280x1024x32 */ - {0x66,0x06ff,0x070a,0x3af2,0x013e,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 1600x1200x32 */ + {0x66,0x0eff,0x070a,0x3af2,0x013e,0x08,0x0a,0x00,0x00,0x00,0x1e}, /* 1600x1200x32 */ {0x68,0x067b,0x080b,0x3b17,0x013f,0x08,0x0b,0x00,0x00,0x00,0x29}, /* 1920x1440x8 */ {0x69,0x06fd,0x080b,0x3b17,0x0140,0x08,0x0b,0x00,0x00,0x00,0x29}, /* 1920x1440x16 */ {0x6b,0x07ff,0x080b,0x3b17,0x0141,0x10,0x0b,0x00,0x00,0x00,0x29}, /* 1920x1440x32 */ @@ -580,14 +574,27 @@ {0x7d,0x0e7d,0x060f,0x3ad0,0x0000,0x08,0x0f,0x00,0x00,0x00,0x3d}, /* 1280x960x16 - TW */ {0x7e,0x0eff,0x060f,0x3ad0,0x0000,0x08,0x0f,0x00,0x00,0x00,0x3d}, /* 1280x960x32 - TW */ /* TW: 650/LVDS BIOS new modes */ -/* {0x23,0x063b,0x0614,0x36f7,0x0000,0x08,0x14,0x00,0x00,0x00,0x40}, */ /* 1280x768x8 - 650/LVDS BIOS */ {0x23,0x0e3b,0x0614,0x36f7,0x0000,0x08,0x14,0x00,0x00,0x00,0x40}, /* 1280x768x8 */ {0x24,0x0e7d,0x0614,0x36f7,0x0000,0x08,0x14,0x00,0x00,0x00,0x40}, /* 1280x768x16 */ {0x25,0x0eff,0x0614,0x36f7,0x0000,0x08,0x14,0x00,0x00,0x00,0x40}, /* 1280x768x32 */ {0x26,0x0e3b,0x0c15,0x36fe,0x0000,0x08,0x15,0x00,0x00,0x00,0x41}, /* 1400x1050x8 */ -/* {0x26,0x063b,0x0c15,0x36fe,0x0000,0x08,0x15,0x00,0x00,0x00,0x41}, */ /* 1400x1050x8 - 650/LVDS BIOS */ {0x27,0x0e7d,0x0c15,0x36fe,0x0000,0x08,0x15,0x00,0x00,0x00,0x41}, /* 1400x1050x16 */ {0x28,0x0eff,0x0c15,0x36fe,0x0000,0x08,0x15,0x00,0x00,0x00,0x41}, /* 1400x1050x32*/ + {0x29,0x0e1b,0x0d16,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x43}, /* TW: NEW 1152x864 - not in BIOS */ + {0x2a,0x0e3d,0x0d16,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x43}, + {0x2b,0x0e7f,0x0d16,0x0000,0x0000,0x08,0x16,0x00,0x00,0x00,0x43}, + {0x39,0x2a1b,0x0b17,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x45}, /* TW: NEW 848x480 - not in BIOS */ + {0x3b,0x2a3d,0x0b17,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x45}, + {0x3e,0x2a7f,0x0b17,0x0000,0x0000,0x08,0x17,0x00,0x00,0x00,0x45}, + {0x3f,0x2a1b,0x0b13,0x0000,0x0000,0x08,0x13,0x00,0x00,0x00,0x47}, /* TW: NEW 856x480 - not in BIOS */ + {0x42,0x2a3d,0x0b13,0x0000,0x0000,0x08,0x13,0x00,0x00,0x00,0x47}, + {0x45,0x2a7f,0x0b13,0x0000,0x0000,0x08,0x13,0x00,0x00,0x00,0x47}, + {0x48,0x2a1b,0x0e18,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x49}, /* TW: NEW 1360x768 - not in BIOS */ + {0x4b,0x2a3d,0x0e18,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x49}, + {0x4e,0x2a7f,0x0e18,0x0000,0x0000,0x08,0x18,0x00,0x00,0x00,0x49}, + {0x4f,0x9a1f,0x0000,0x0000,0x0000,0x08,0x00,0x00,0x00,0x04,0x25}, /* TW: New 320x200x32 */ + {0x53,0x9a1f,0x0001,0x0000,0x0000,0x08,0x01,0x00,0x00,0x04,0x26}, /* TW: New 320x240x32 */ + {0x54,0xba1f,0x0103,0x0000,0x0000,0x08,0x03,0x00,0x00,0x07,0x27}, /* TW: New 400x300x32 */ {0xff,0x0000,0x0000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00} }; @@ -611,33 +618,33 @@ {0x0067,0x0f,0x08,0x48,0x6a, 800, 600,0x3a8b}, /* 0x2 */ {0x0067,0x10,0x07,0x8b,0x6a, 800, 600,0x3a90}, /* 0x3 */ {0x0147,0x11,0x0a,0x00,0x6a, 800, 600,0x3a95}, /* 0x4 */ - {0x4147,0x12,0x0d,0x00,0x6a, 800, 600,0x3a9a}, /* 0x5 */ - {0x4047,0x13,0x13,0x00,0x6a, 800, 600,0x3a9f}, /* 0x6 */ - {0x4047,0x14,0x1c,0x00,0x6a, 800, 600,0x3aa4}, /* 0x7 */ + {0x0147,0x12,0x0d,0x00,0x6a, 800, 600,0x3a9a}, /* 0x5 - 4147 TW: Test sync change */ + {0x0047,0x13,0x13,0x00,0x6a, 800, 600,0x3a9f}, /* 0x6 - 4047 */ + {0x0047,0x14,0x1c,0x00,0x6a, 800, 600,0x3aa4}, /* 0x7 - 4047 */ /* {0xc05f,0x05,0x00,0x04,0x2e, 640, 480,0x3a57}, 0x8 - TW: Patch for Chrontel 7019 */ {0xc85f,0x05,0x00,0x04,0x2e, 640, 480,0x3a57}, /* 0x8 */ {0xc067,0x06,0x02,0x04,0x2e, 640, 480,0x3a5c}, /* 0x9 */ {0xc067,0x07,0x02,0x47,0x2e, 640, 480,0x3a61}, /* 0xa */ {0xc067,0x08,0x03,0x8a,0x2e, 640, 480,0x3a66}, /* 0xb */ - {0x4047,0x09,0x05,0x00,0x2e, 640, 480,0x3a6b}, /* 0xc */ - {0x4047,0x0a,0x09,0x00,0x2e, 640, 480,0x3a70}, /* 0xd */ - {0x4047,0x0b,0x0e,0x00,0x2e, 640, 480,0x3a75}, /* 0xe */ + {0xc047,0x09,0x05,0x00,0x2e, 640, 480,0x3a6b}, /* 0xc - 4047 */ + {0xc047,0x0a,0x09,0x00,0x2e, 640, 480,0x3a70}, /* 0xd - 4047 */ + {0xc047,0x0b,0x0e,0x00,0x2e, 640, 480,0x3a75}, /* 0xe - 4047 */ {0xc047,0x0c,0x15,0x00,0x2e, 640, 480,0x3a7a}, /* 0xf */ {0x407f,0x04,0x00,0x00,0x2f, 640, 400,0x3a50}, /* 0x10 */ {0xc00f,0x3c,0x01,0x06,0x31, 720, 480,0x3b85}, /* 0x11 */ {0x000f,0x3d,0x03,0x06,0x32, 720, 576,0x3b8c}, /* 0x12 */ {0x0187,0x15,0x06,0x00,0x37,1024, 768,0x3aab}, /* 0x13 */ - {0xc877,0x16,0x0b,0x06,0x37,1024, 768,0x3ab0}, /* 0x14 301b TV1024x768*/ + {0xc877,0x16,0x0b,0x06,0x37,1024, 768,0x3ab0}, /* 0x14 */ {0xc067,0x17,0x0f,0x49,0x37,1024, 768,0x3ab5}, /* 0x15 */ {0x0267,0x18,0x11,0x00,0x37,1024, 768,0x3aba}, /* 0x16 */ {0x0047,0x19,0x16,0x8c,0x37,1024, 768,0x3abf}, /* 0x17 */ - {0x4047,0x1a,0x1b,0x00,0x37,1024, 768,0x3ac4}, /* 0x18 */ - {0x4047,0x1b,0x1f,0x00,0x37,1024, 768,0x3ac9}, /* 0x19 */ + {0x0047,0x1a,0x1b,0x00,0x37,1024, 768,0x3ac4}, /* 0x18 - 4047 */ + {0x0007,0x1b,0x1f,0x00,0x37,1024, 768,0x3ac9}, /* 0x19 - 4047 */ {0x0387,0x1c,0x11,0x00,0x3a,1280,1024,0x3adc}, /* 0x1a */ {0x0077,0x1d,0x19,0x07,0x3a,1280,1024,0x3ae1}, /* 0x1b */ {0x0047,0x1e,0x1e,0x00,0x3a,1280,1024,0x3ae6}, /* 0x1c */ {0x0007,0x1f,0x20,0x00,0x3a,1280,1024,0x3aeb}, /* 0x1d */ - {0x0007,0x20,0x21,0x00,0x3c,1600,1200,0x3af2}, /* 0x1e */ + {0x0027,0x20,0x21,0x09,0x3c,1600,1200,0x3af2}, /* 0x1e */ {0x0007,0x21,0x22,0x00,0x3c,1600,1200,0x3af7}, /* 0x1f */ {0x0007,0x22,0x23,0x00,0x3c,1600,1200,0x3afc}, /* 0x20 */ {0x0007,0x23,0x25,0x00,0x3c,1600,1200,0x3b01}, /* 0x21 */ @@ -667,14 +674,22 @@ {0x0047,0x37,0x16,0x09,0x71,1024, 576,0x3b6d}, /* 0x39 */ {0x0057,0x38,0x19,0x0a,0x75,1280, 720,0x3b74}, /* 0x3a */ {0x0047,0x39,0x1e,0x0a,0x75,1280, 720,0x3b79}, /* 0x3b */ - {0x0047,0x3a,0x20,0x0a,0x75,1280, 720,0x3b7e}, /* 0x3c */ - {0x0027,0x3b,0x19,0x08,0x7c,1280, 960,0x3ad0}, /* 0x3d */ - {0x0027,0x3b,0x19,0x08,0x7c,1280, 960,0x3ad5}, /* 0x3e */ + {0x0007,0x3a,0x20,0x0a,0x75,1280, 720,0x3b7e}, /* 0x3c */ + {0x0067,0x3b,0x19,0x08,0x7c,1280, 960,0x3ad0}, /* 0x3d */ + {0x0027,0x4c,0x59,0x08,0x7c,1280, 960,0x3ad0}, /* 0x3e */ {0xc07f,0x01,0x00,0x06,0x5a, 320, 480,0x3b83}, /* 0x3f */ /* FSTN mode */ - {0x0077,0x42,0x12,0x07,0x23,1280, 768,0x0000}, /* 0x40 */ /* TW: 650/LVDS/301LVx new mode */ - {0x0067,0x43,0x4d,0x08,0x26,1400,1050,0x0000}, /* 0x41 */ /* TW: 650/LVDS/301LVx new mode */ - {0xffff,0x00,0x00,0x00,0x00,0000,0000,0x0000} -}; + {0x0077,0x42,0x5b,0x08,0x23,1280, 768,0x0000}, /* 0x40 */ /* TW: 0x5b was 0x12 */ + {0x0067,0x43,0x4d,0x08,0x26,1400,1050,0x0000}, /* 0x41 */ + {0x0007,0x4b,0x5a,0x08,0x26,1400,1050,0x0000}, /* 0x42 */ /* TW: new, not in any BIOS */ + {0x0047,0x44,0x19,0x00,0x29,1152, 864,0x0000}, /* 0x43 TW: Non-BIOS, new */ + {0x0047,0x4a,0x1e,0x00,0x29,1152, 864,0x0000}, /* 0x44 TW: Non-BIOS, new */ + {0x00c7,0x45,0x57,0x00,0x39, 848, 480,0x0000}, /* 0x45 TW: 848x480-38Hzi - Non-BIOS, new */ + {0xc047,0x46,0x55,0x00,0x39, 848, 480,0x0000}, /* 0x46 TW: 848x480-60Hz - Non-BIOS, new */ + {0x00c7,0x47,0x57,0x00,0x3f, 856, 480,0x0000}, /* 0x47 TW: 856x480-38Hzi - Non-BIOS, new */ + {0xc047,0x48,0x57,0x00,0x3f, 856, 480,0x0000}, /* 0x48 TW: 856x480-60Hz - Non-BIOS, new */ + {0x0047,0x49,0x58,0x00,0x48,1360, 768,0x0000}, /* 0x49 TW: 1360x768-60Hz - Non-BIOS, new */ + {0xffff,0x00,0x00,0x00,0x00, 0, 0,0x0000} +}; typedef struct _SiS310_CRT1TableStruct { @@ -698,12 +713,22 @@ {{0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f, 0x9c,0x8e,0x8f,0x96,0xb9,0x30,0x00,0x05, 0x00}}, /* 0x4 */ +#if 0 {{0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e, 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x05, 0x00}}, /* 0x5 */ +#endif + {{0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, /* 0x05 - corrected 640x480-60 */ + 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x00}}, +#if 0 {{0x63,0x4f,0x50,0x86,0x56,0x9b,0x06,0x3e, 0xe8,0x8b,0xdf,0xe7,0xff,0x10,0x00,0x01, 0x00}}, /* 0x6 */ +#endif + {{0x63,0x4f,0x4f,0x87,0x56,0x9b,0x06,0x3e, /* 0x06 - corrected 640x480-72 */ + 0xe8,0x8a,0xdf,0xe7,0x07,0x00,0x00,0x01, + 0x00}}, {{0x64,0x4f,0x4f,0x88,0x55,0x9d,0xf2,0x1f, 0xe0,0x83,0xdf,0xdf,0xf3,0x10,0x00,0x01, 0x00}}, /* 0x7 */ @@ -711,16 +736,16 @@ 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x05, 0x00}}, /* 0x8 */ {{0x65,0x4f,0x4f,0x89,0x58,0x80,0xfb,0x1f, - 0xe0,0x83,0xdf,0xdf,0xfc,0x00,0x00,0x05, + 0xe0,0x83,0xdf,0xdf,0xfc,0x10,0x00,0x05, /* TW: Corrected VBE */ 0x61}}, /* 0x9 */ {{0x65,0x4f,0x4f,0x89,0x58,0x80,0x01,0x3e, 0xe0,0x83,0xdf,0xdf,0x02,0x00,0x00,0x05, 0x61}}, /* 0xa */ {{0x67,0x4f,0x4f,0x8b,0x58,0x81,0x0d,0x3e, - 0xe0,0x83,0xdf,0xdf,0x0e,0x10,0x00,0x05, + 0xe0,0x83,0xdf,0xdf,0x0e,0x00,0x00,0x05, /* TW: Corrected VBE */ 0x61}}, /* 0xb */ {{0x65,0x4f,0x4f,0x89,0x57,0x9f,0xfb,0x1f, - 0xe6,0x8a,0xe5,0xe5,0xfc,0x00,0x00,0x01, + 0xe6,0x8a,0xdf,0xdf,0xfc,0x10,0x00,0x01, /* TW: Corrected VDE, VBE */ 0x00}}, /* 0xc */ {{0x7b,0x63,0x63,0x9f,0x6a,0x93,0x6f,0xf0, 0x58,0x8a,0x57,0x57,0x70,0x20,0x00,0x05, @@ -848,7 +873,7 @@ {{0x9f,0x7f,0x7f,0x83,0x85,0x91,0x1e,0xf1, 0xad,0x81,0x3f,0x3f,0x1f,0x30,0x00,0x02, 0x01}}, /* 0x36 */ - {{0xa7,0x7f,0x7f,0x88,0x89,0x15,0x26,0xf1, + {{0xa7,0x7f,0x7f,0x88,0x89,0x95,0x26,0xf1, /* TW: 95 was 15 - illegal HBE! */ 0xb1,0x85,0x3f,0x3f,0x27,0x30,0x00,0x02, 0x01}}, /* 0x37 */ {{0xce,0x9f,0x9f,0x92,0xa9,0x17,0x28,0xc4, @@ -860,9 +885,14 @@ {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0x2e,0xd4, 0x7d,0x81,0xcf,0xcf,0x2f,0x21,0x00,0x07, 0x01}}, /* 0x3a */ - {{0xdc,0x9f,0x9f,0x00,0xab,0x19,0xe6,0xef, +#if 0 + {{0xdc,0x9f,0x9f,0x00,0xab,0x19,0xe6,0xef, /* 1280x960 - invalid */ 0xc0,0xc3,0xbf,0xbf,0xe7,0x10,0x00,0x07, 0x01}}, /* 0x3b */ +#endif + {{0xdc,0x9f,0x9f,0x80,0xaf,0x9d,0xe6,0xff, /* 1280x960-60 - corrected */ + 0xc0,0x83,0xbf,0xbf,0xe7,0x10,0x00,0x07, + 0x01}}, /* 0x3b */ {{0x6b,0x59,0x59,0x8f,0x5e,0x8c,0x0b,0x3e, 0xe9,0x8b,0xdf,0xe7,0x04,0x00,0x00,0x05, 0x00}}, /* 0x3c */ @@ -875,8 +905,7 @@ {{0x81,0x6a,0x6a,0x85,0x70,0x00,0x0f,0x3e, 0xeb,0x8e,0xdf,0xdf,0x10,0x00,0x00,0x02, 0x00}}, /* 0x3f */ - /* TW: New from 650/LVDS BIOS */ - {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x1e,0xf1, + {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x1e,0xf1, /* TW: The following from 650/LVDS BIOS */ 0xae,0x85,0x57,0x57,0x1f,0x30,0x00,0x02, 0x01}}, /* 0x40 */ {{0xa3,0x7f,0x7f,0x87,0x86,0x97,0x24,0xf5, @@ -887,9 +916,37 @@ 0x01}}, /* 0x42 */ {{0xe6,0xae,0xae,0x8a,0xbd,0x90,0x3d,0x10, 0x1a,0x8d,0x19,0x19,0x3e,0x2f,0x00,0x03, - 0x00}} /* 0x43 */ + 0x00}}, /* 0x43 */ + {{0xc3,0x8f,0x8f,0x87,0x9b,0x0b,0x82,0xef, /* TW: New, 1152x864-75, not in any BIOS */ + 0x60,0x83,0x5f,0x5f,0x83,0x10,0x00,0x07, + 0x01}}, /* 0x44 */ + {{0x86,0x69,0x69,0x8A,0x74,0x06,0x8C,0x15, /* TW: New, 848x480-38i, not in BIOS */ + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x45 */ + {{0x83,0x69,0x69,0x87,0x6f,0x1d,0x03,0x3E, /* TW: New, 848x480-60, not in BIOS */ + 0xE5,0x8d,0xDF,0xe4,0x04,0x00,0x00,0x06, + 0x00}}, /* 0x46 */ + {{0x86,0x6A,0x6A,0x8A,0x74,0x06,0x8C,0x15, /* TW: New, 856x480-38i, not in BIOS */ + 0x4F,0x83,0xEF,0xEF,0x8D,0x30,0x00,0x02, + 0x00}}, /* 0x47 */ + {{0x81,0x6A,0x6A,0x85,0x70,0x00,0x0F,0x3E, /* TW: New, 856x480-60, not in BIOS */ + 0xEB,0x8E,0xDF,0xDF,0x10,0x00,0x00,0x02, + 0x00}}, /* 0x48 */ + {{0xdd,0xa9,0xa9,0x81,0xb4,0x97,0x26,0xfd, /* TW: New, 1360x768-60, not in BIOS */ + 0x01,0x8d,0xff,0x00,0x27,0x10,0x00,0x03, + 0x01}}, /* 0x49 */ + {{0xd9,0x8f,0x8f,0x9d,0xba,0x0a,0x8a,0xff, /* TW: New, 1152x864-84, not in any BIOS */ + 0x60,0x8b,0x5f,0x5f,0x8b,0x10,0x00,0x03, + 0x01}}, /* 0x4a */ + {{0xea,0xae,0xae,0x8e,0xba,0x82,0x40,0x10, /* TW: New, 1400x1050-75, not in any BIOS */ + 0x1b,0x87,0x19,0x1a,0x41,0x0f,0x00,0x03, + 0x00}}, /* 0x4b */ + {{0xd3,0x9f,0x9f,0x97,0xab,0x1f,0xf1,0xff, /* TW: New, 1280x960-85, not in any BIOS */ + 0xc0,0x83,0xbf,0xbf,0xf2,0x10,0x00,0x07, + 0x01}} /* 0x4c */ }; + typedef struct _SiS310_MCLKDataStruct { UCHAR SR28,SR29,SR2A; @@ -920,6 +977,18 @@ { 0x37,0x22,0x82,133} }; +static const SiS310_MCLKDataStruct SiS310_MCLKData_0_330[] = /* @ 0x54 */ +{ + { 0x5c,0x23,0x01,166}, + { 0x5c,0x23,0x01,166}, + { 0x7c,0x08,0x01,200}, + { 0x79,0x06,0x01,250}, + { 0x7c,0x08,0x01,200}, + { 0x7c,0x08,0x01,200}, + { 0x7c,0x08,0x01,200}, + { 0x79,0x06,0x01,250} +}; + static const SiS310_MCLKDataStruct SiS310_MCLKData_1[] = /* @ 0x155 */ { { 0x29,0x21,0x82,150}, @@ -1019,7 +1088,6 @@ { 0xa8,0x4c, 30}, /* 0x3e */ { 0x20,0x26, 33}, /* 0x3f */ { 0x31,0xc2, 39}, /* 0x40 */ - /* TW: 650/LVDS BIOS @ 0x574b new: */ { 0x60,0x36, 30}, /* 0x41 */ /* Chrontel */ { 0x40,0x4a, 28}, /* 0x42 */ /* Chrontel */ { 0x9f,0x46, 44}, /* 0x43 */ /* Chrontel */ @@ -1039,7 +1107,14 @@ { 0x54,0x46, 58}, /* 0x51 */ /* Chrontel */ { 0x25,0x42, 61}, /* 0x52 */ { 0x44,0x44, 66}, /* 0x53 */ /* Chrontel */ - { 0x3a,0x62, 70} /* 0x54 */ /* Chrontel */ + { 0x3a,0x62, 70}, /* 0x54 */ /* Chrontel */ + { 0x62,0xc6, 34}, /* 0x55 - added for 848x480-60 (not in any BIOS) */ + { 0x6a,0xc6, 37}, /* 0x56 - added for 848x480-75 (not in any BIOS) - TEMP */ + { 0xbf,0xc8, 35}, /* 0x57 - added for 856x480-38i,60 (not in any BIOS) */ + { 0x30,0x23, 88}, /* 0x58 - added for 1360x768-62 (is 60Hz!) (not in any BIOS) */ + { 0x52,0x07,149}, /* 0x59 - added for 1280x960-85 (Not in any BIOS) */ + { 0x56,0x07,156}, /* 0x5a - added for 1400x1050-75 */ + { 0x7a,0x2a, 81} /* 0x5b */ /* For 1280x768 LCD mode */ }; typedef struct _SiS310_VBVCLKDataStruct @@ -1050,7 +1125,7 @@ static const SiS310_VBVCLKDataStruct SiS310_VBVCLKData[]= { - { 0x1b,0xe1, 25}, /* 0x0 */ /* 650/LVDS BIOS: @ 0x579c */ + { 0x1b,0xe1, 25}, /* 0x0 */ { 0x4e,0xe4, 28}, /* 0x1 */ { 0x57,0xe4, 31}, /* 0x2 */ { 0xc3,0xc8, 36}, /* 0x3 */ @@ -1115,20 +1190,41 @@ { 0xa8,0x4c, 30}, /* 0x3e */ { 0x20,0x26, 33}, /* 0x3f */ { 0x31,0xc2, 39}, /* 0x40 */ - /* TW: 650/LVDS+301 BIOS (@ 0x58a0 in LVDS) new: */ { 0x2e,0x48, 25}, /* 0x41 */ { 0x24,0x46, 25}, /* 0x42 */ { 0x26,0x64, 28}, /* 0x43 */ { 0x37,0x64, 40}, /* 0x44 */ { 0xa1,0x42,108}, /* 0x45 */ { 0x37,0x61,100}, /* 0x46 */ - { 0x78,0x27,108} /* 0x47 */ - /* --- 0x58bc --- */ -}; - -static const UCHAR SiS310_ScreenOffset[]= - { 0x14,0x19,0x20,0x28,0x32,0x40, - 0x50,0x64,0x78,0x80,0x2d,0x35,0x57}; /* TW: Added 1400x1050 offset */ + { 0x78,0x27,108}, /* 0x47 */ + { 0x97,0x2c, 26}, /* 0x48 */ /* UNUSED - Entries from here new, not in any BIOS */ + { 0xce,0x3c, 39}, /* 0x49 */ /* UNUSED */ + { 0x52,0x4a, 36}, /* 0x4a */ /* UNUSED */ + { 0x34,0x61, 95}, /* 0x4b */ /* UNUSED */ + { 0x78,0x27,108}, /* 0x4c */ /* UNUSED */ + { 0x66,0x43,123}, /* 0x4d */ /* 1400x1050-60 */ + { 0x41,0x4e, 21}, /* 0x4e */ /* UNUSED */ + { 0xa1,0x4a, 29}, /* 0x4f */ /* UNUSED */ + { 0x19,0x42, 42}, /* 0x50 */ /* UNUSED */ + { 0x54,0x46, 58}, /* 0x51 */ /* UNUSED */ + { 0x25,0x42, 61}, /* 0x52 */ /* UNUSED */ + { 0x44,0x44, 66}, /* 0x53 */ /* UNUSED */ + { 0x3a,0x62, 70}, /* 0x54 */ /* UNUSED */ + { 0x62,0xc6, 34}, /* 0x55 */ /* 848x480-60 */ + { 0x6a,0xc6, 37}, /* 0x56 */ /* 848x480-75 - TEMP, UNUSED */ + { 0xbf,0xc8, 35}, /* 0x57 */ /* 856x480-38i,60 */ + { 0x30,0x23, 88}, /* 0x58 */ /* 1360x768-62 (is 60Hz!) TEMP, UNUSED */ + { 0x52,0x07,149}, /* 0x59 */ /* 1280x960-85 - UNUSED */ + { 0x56,0x07,156}, /* 0x5a */ /* 1400x1050-75 - UNUSED */ + { 0x7a,0x2a, 81} /* 0x5b */ /* For 1280x768 LCD mode */ +}; + +static const UCHAR SiS310_ScreenOffset[] = +{ + 0x14,0x19,0x20,0x28,0x32,0x40,0x50,0x64, + 0x78,0x80,0x2d,0x35,0x57,0x48,0x55, + 0xff +}; /* TW: Added 1400x1050, 1152x864, 848/856x480, 1360x768 */ typedef struct _SiS310_StResInfoStruct { @@ -1153,7 +1249,7 @@ UCHAR YChar; } SiS310_ModeResInfoStruct; -static const SiS310_ModeResInfoStruct SiS310_ModeResInfo[]= +static const SiS310_ModeResInfoStruct SiS310_ModeResInfo[] = { { 320, 200, 8, 8}, /* 0x00 */ { 320, 240, 8, 8}, /* 0x01 */ @@ -1174,9 +1270,12 @@ { 800, 480, 8,16}, /* 0x10 */ { 1024, 576, 8,16}, /* 0x11 */ { 1280, 720, 8,16}, /* 0x12 */ - { 856, 480, 8,16}, /* 0x13 19; TW: New */ + { 856, 480, 8,16}, /* 0x13 - TW: New, not in any BIOS */ { 1280, 768, 8,16}, /* 0x14 20; TW: New */ - { 1400,1050, 8,16} /* 0x15 21; TW: New */ + { 1400,1050, 8,16}, /* 0x15 21; TW: New */ + { 1152, 864, 8,16}, /* 0x16 - TW: New, not in any BIOS */ + { 848, 480, 8,16}, /* 0x17 - TW: New, not in any BIOS */ + { 1360, 768, 8,16} /* 0x18 - TW: New, not in any BIOS */ }; static const UCHAR SiS310_OutputSelect = 0x40; @@ -1223,7 +1322,7 @@ static const USHORT SiS310_RGBSenseData = 0xd1; static const USHORT SiS310_VideoSenseData = 0xb9; static const USHORT SiS310_YCSenseData = 0xb3; -static const USHORT SiS310_RGBSenseData2 = 0x0190; /*301b*/ +static const USHORT SiS310_RGBSenseData2 = 0x0190; static const USHORT SiS310_VideoSenseData2 = 0x0174; static const USHORT SiS310_YCSenseData2 = 0x016b; #endif @@ -1259,7 +1358,8 @@ { 1, 1,1344, 806,1344, 806} }; -static const SiS310_LCDDataStruct SiS310_ExtLCD1024x768Data[] = /* TW: Checked */ +#if 0 /* Seems out-dated, all BIOSes since 03/27/2002 have the other version */ +static const SiS310_LCDDataStruct SiS310_ExtLCD1024x768Data[] = { { 12, 5, 896, 512,1344, 806}, { 12, 5, 896, 510,1344, 806}, @@ -1275,13 +1375,31 @@ { 42, 25,1024, 625,1344, 806}, { 1, 1,1344, 806,1344, 806} }; +#endif + +static const SiS310_LCDDataStruct SiS310_ExtLCD1024x768Data[] = +{ + { 42, 25,1536, 419,1344, 806}, + { 48, 25,1536, 369,1344, 806}, + { 42, 25,1536, 419,1344, 806}, + { 48, 25,1536, 369,1344, 806}, + { 12, 5, 896, 500,1344, 806}, + { 42, 25,1024, 625,1344, 806}, + { 1, 1,1344, 806,1344, 806}, + { 12, 5, 896, 500,1344, 806}, + { 42, 25,1024, 625,1344, 806}, + { 1, 1,1344, 806,1344, 806}, + { 12, 5, 896, 500,1344, 806}, + { 42, 25,1024, 625,1344, 806}, + { 1, 1,1344, 806,1344, 806} + +}; -static const SiS310_LCDDataStruct SiS310_St2LCD1024x768Data[] = /* TW: Checked */ +static const SiS310_LCDDataStruct SiS310_St2LCD1024x768Data[] = { { 62, 25, 800, 546,1344, 806}, { 32, 15, 930, 546,1344, 806}, -/* { 32, 15, 930, 546,1344, 806}, */ - { 62, 25, 800, 546,1344, 806}, /* TW: Different in 650/301LV BIOS */ + { 62, 25, 800, 546,1344, 806}, { 104, 45, 945, 496,1344, 806}, { 62, 25, 800, 546,1344, 806}, { 31, 18,1008, 624,1344, 806}, @@ -1300,7 +1418,7 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS310_LCDDataStruct SiS310_ExtLCD1280x1024Data[] = /* TW: Checked */ +static const SiS310_LCDDataStruct SiS310_ExtLCD1280x1024Data[] = { { 211, 60,1024, 501,1688,1066}, { 211, 60,1024, 508,1688,1066}, @@ -1309,7 +1427,8 @@ { 211, 60,1024, 500,1688,1066}, { 211, 75,1024, 625,1688,1066}, { 211, 120,1280, 798,1688,1066}, - { 1, 1,1688,1066,1688,1066} + { 1, 1,1688,1066,1688,1066}, + { 1, 1,1800,1000,1688,1066} /* 1280x960 - does not work, use panel scaler instead */ }; static const SiS310_LCDDataStruct SiS310_St2LCD1280x1024Data[] = @@ -1324,29 +1443,19 @@ { 1, 1,1688,1066,1688,1066} }; -static const SiS310_LCDDataStruct SiS310_NoScaleData1024x768[] = /* TW: Checked */ +static const SiS310_LCDDataStruct SiS310_NoScaleData1024x768[] = { { 1, 1,1344, 806,1344, 806}, { 1, 1,1344, 806,1344, 806}, { 1, 1,1344, 806,1344, 806}, - { 1, 1,1344, 806,1344, 806}, - { 1, 1,1344, 806,1344, 806}, + { 1, 1,1344, 806,1344, 806}, /* 640x400 - does not work */ + { 1, 1,1344, 806,1344, 806}, /* 640x480 - does not work */ { 1, 1,1344, 806,1344, 806}, { 1, 1,1344, 806,1344, 806}, { 1, 1,1344, 806,1344, 806} -#if 0 - { 1, 1, 800, 449, 800, 449}, - { 1, 1, 800, 449, 800, 449}, - { 1, 1, 900, 449, 900, 449}, - { 1, 1, 900, 449, 900, 449}, - { 1, 1, 800, 525, 800, 525}, - { 1, 1,1056, 628,1056, 628}, - { 1, 1,1344, 806,1344, 806}, - { 1, 1,1688,1066,1688,1066} -#endif }; -static const SiS310_LCDDataStruct SiS310_NoScaleData1280x1024[] = /* TW: New; Checked */ +static const SiS310_LCDDataStruct SiS310_NoScaleData1280x1024[] = { { 1, 1,1688,1066,1688,1066}, { 1, 1,1688,1066,1688,1066}, @@ -1355,6 +1464,7 @@ { 1, 1,1688,1066,1688,1066}, { 1, 1,1688,1066,1688,1066}, { 1, 1,1688,1066,1688,1066}, + { 1, 1,1688,1066,1688,1066}, { 1, 1,1688,1066,1688,1066} }; @@ -1371,88 +1481,121 @@ { 1, 1,1800,1000,1800,1000} }; -static const SiS310_LCDDataStruct SiS310_ExtLCD1400x1050Data[] = /* TW: New */ -{ /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0} -}; - -static const SiS310_LCDDataStruct SiS310_ExtLCD1600x1200Data[] = /* TW: New */ -{ /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0} -}; - -static const SiS310_LCDDataStruct SiS310_StLCD1400x1050Data[] = /* TW: New */ -{ /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0} +static const SiS310_LCDDataStruct SiS310_StLCD1400x1050Data[] = +{ /* TW: New from 1.11.6s */ + { 211, 100, 2100, 408, 1688, 1066 }, + { 211, 64, 1536, 358, 1688, 1066 }, + { 211, 100, 2100, 408, 1688, 1066 }, + { 211, 64, 1536, 358, 1688, 1066 }, + { 211, 48, 840, 488, 1688, 1066 }, + { 211, 72, 1008, 609, 1688, 1066 }, + { 211, 128, 1400, 776, 1688, 1066 }, + { 211, 205, 1680, 1041, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 } +}; + +static const SiS310_LCDDataStruct SiS310_ExtLCD1400x1050Data[] = +{ /* TW: New from 1.11.6s */ + { 211, 100, 2100, 408, 1688, 1066 }, + { 211, 64, 1536, 358, 1688, 1066 }, + { 211, 100, 2100, 408, 1688, 1066 }, + { 211, 64, 1536, 358, 1688, 1066 }, + { 211, 48, 840, 488, 1688, 1066 }, + { 211, 72, 1008, 609, 1688, 1066 }, + { 211, 128, 1400, 776, 1688, 1066 }, + { 211, 205, 1680, 1041, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 } +}; + +static const SiS310_LCDDataStruct SiS310_NoScaleData1400x1050[] = +{ /* TW: To be checked (BIOS uses 1280x1024 data, one line too short) */ + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 }, + { 1, 1, 1688, 1066, 1688, 1066 } }; -static const SiS310_LCDDataStruct SiS310_StLCD1600x1200Data[] = /* TW: New */ +static const SiS310_LCDDataStruct SiS310_StLCD1600x1200Data[] = { /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; -static const SiS310_LCDDataStruct SiS310_NoScaleData1400x1050[] = /* TW: New */ +static const SiS310_LCDDataStruct SiS310_ExtLCD1600x1200Data[] = { /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0} }; -static const SiS310_LCDDataStruct SiS310_NoScaleData1600x1200[] = /* TW: New */ -{ /* TODO */ - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0} +static const SiS310_LCDDataStruct SiS310_NoScaleData1600x1200[] = +{ /* TODO - values guessed */ + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250}, + {1, 1, 2048, 1250, 2048, 1250} +}; + +static const SiS310_LCDDataStruct SiS310_StLCD1280x768Data[] = +{ + { 211, 100, 2100, 408, 1408, 806 }, /* These values are *wrong* */ + { 211, 64, 1536, 358, 1408, 806 }, + { 211, 100, 2100, 408, 1408, 806 }, + { 211, 64, 1536, 358, 1408, 806 }, + { 211, 48, 840, 488, 1408, 806 }, + { 211, 72, 1008, 609, 1408, 806 }, + { 211, 128, 1400, 776, 1408, 806 }, + { 211, 205, 1680, 1041, 1408, 806 }, + { 1, 1, 1408, 806, 1408, 806 } /* That's the only one that *might* be correct */ +}; + +static const SiS310_LCDDataStruct SiS310_ExtLCD1280x768Data[] = +{ + { 211, 100, 2100, 408, 1408, 806 }, /* These values are *wrong* */ + { 211, 64, 1536, 358, 1408, 806 }, + { 211, 100, 2100, 408, 1408, 806 }, + { 211, 64, 1536, 358, 1408, 806 }, + { 211, 48, 840, 488, 1408, 806 }, + { 211, 72, 1008, 609, 1408, 806 }, + { 211, 128, 1400, 776, 1408, 806 }, + { 211, 205, 1680, 1041, 1408, 806 }, + { 1, 1, 1408, 806, 1408, 806 } /* That's the only one that *might* be correct */ +}; + +static const SiS310_LCDDataStruct SiS310_NoScaleData1280x768[] = +{ /* All values guessed */ + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806}, + { 1, 1, 1408, 806, 1408, 806} +}; + +static const SiS310_LCDDataStruct SiS310_NoScaleData[] = +{ + { 1, 1, 800, 449, 800, 449 }, + { 1, 1, 800, 449, 800, 449 }, + { 1, 1, 900, 449, 900, 449 }, + { 1, 1, 900, 449, 900, 449 }, + { 1, 1, 800, 525, 800, 525 }, + { 1, 1,1056, 628,1056, 628 }, + { 1, 1,1344, 806,1344, 806 }, + { 1, 1,1688,1066,1688,1066 }, + { 1, 1,1688, 791,1688, 791 }, /* 1280x768: 791 was 860 in both cases */ + { 1, 1,2048,1250,2048,1250 }, /* 1600x1200 (guessed) */ + { 1, 1,1800,1000,1800,1000 } /* 1280x960 */ }; typedef struct _SiS310_TVDataStruct @@ -1482,16 +1625,16 @@ { 1, 1, 864, 525,1270, 600, 50, 0, 0,0xf4,0xff,0x1c,0x22} }; -static const SiS310_TVDataStruct SiS310_ExtPALData[]= +static const SiS310_TVDataStruct SiS310_ExtPALData[] = { { 27, 10, 848, 448,1270, 530, 50, 0, 50,0xf4,0xff,0x1c,0x22}, { 108, 35, 848, 398,1270, 530, 50, 0, 50,0xf4,0xff,0x1c,0x22}, { 12, 5, 954, 448,1270, 530, 50, 0, 50,0xf1,0x04,0x1f,0x18}, { 9, 4, 960, 463,1644, 438, 50, 0, 50,0xf4,0x0b,0x1c,0x0a}, - { 9, 4, 848, 528,1270, 530, 0, 0, 50,0xf5,0xfb,0x1b,0x2a}, + { 9, 4, 848, 528,1270, 530, 0, 0, 50,0xf5,0xfb,0x1b,0x2a}, /* 640x480 */ { 36, 25,1060, 648,1316, 530, 438, 0, 438,0xeb,0x05,0x25,0x16}, /* 800x600 */ - { 3, 2,1080, 619,1270, 540, 438, 0, 438,0xf3,0x00,0x1d,0x20}, - { 1, 1,1170, 821,1270, 520, 686, 0, 686,0xF3,0x00,0x1D,0x20} /*301b*/ + { 3, 2,1080, 619,1270, 540, 438, 0, 438,0xf3,0x00,0x1d,0x20}, /* 720x480/576 */ + { 1, 1,1170, 821,1270, 520, 686, 0, 686,0xF3,0x00,0x1D,0x20} /* 1024x768 */ }; static const SiS310_TVDataStruct SiS310_StNTSCData[]= @@ -1509,29 +1652,45 @@ { 88, 35, 858, 393,1270, 440, 171, 0, 171,0xf1,0x04,0x1f,0x18}, { 143, 70, 924, 443,1270, 440, 92, 0, 92,0xf1,0x04,0x1f,0x18}, { 143, 70, 924, 393,1270, 440, 92, 0, 92,0xf4,0x0b,0x1c,0x0a}, - { 143, 76, 836, 523,1270, 440, 224, 0, 0,0xf1,0x05,0x1f,0x16}, - { 143, 120,1056, 643,1270, 440, 0, 128, 0,0xf4,0x10,0x1c,0x00}, /* 800x600 */ - { 2, 1, 858, 503,1270, 480, 0, 128, 0,0xee,0x0c,0x22,0x08}, - { 65, 64,1056, 791,1270, 480, 638, 0, 0,0xEE,0x0C,0x22,0x08} /*301b*/ + { 143, 76, 836, 523,1270, 440, 224, 0, 0,0xf1,0x05,0x1f,0x16}, /* 640x480 */ + { 143, 120,1056, 643,1270, 440, 0, 128, 0,0xf4,0x10,0x1c,0x00}, /* 800x600 */ + { 2, 1, 858, 503,1270, 480, 0, 128, 0,0xee,0x0c,0x22,0x08}, /* 720x480/576 */ + { 65, 64,1056, 791,1270, 480, 638, 0, 0,0xEE,0x0C,0x22,0x08} /* 1024x768 */ }; -/* TW: These tables will need data ! */ +#if 0 static const SiS310_TVDataStruct SiS310_St1HiTVData[]= { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + }; +#endif static const SiS310_TVDataStruct SiS310_St2HiTVData[]= { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + { 3, 1, 0x348,0x1e3,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 1, 1, 0x37c,0x233,0x2b2,0x2bc, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x348,0x1e3,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 1, 1, 0x3e8,0x233,0x311,0x2bc, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 2, 0x348,0x233,0x670,0x3c0,0x08d,128, 0, 0x00,0x00,0x00,0x00}, + { 8, 5, 0x41a,0x2ab,0x670,0x3c0,0x17c,128, 0, 0x00,0x00,0x00,0x00} }; static const SiS310_TVDataStruct SiS310_ExtHiTVData[]= { - {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + { 6, 1, 0x348,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x3c0,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x348,0x1e3,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 3, 1, 0x3c0,0x233,0x660,0x3c0, 0, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 1, 0x348,0x233,0x670,0x3c0,0x166,128, 0, 0x00,0x00,0x00,0x00}, + { 16, 5, 0x41a,0x2ab,0x670,0x3c0,0x143,128, 0, 0x00,0x00,0x00,0x00}, + { 25, 12, 0x4ec,0x353,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 5, 4, 0x627,0x464,0x670,0x3c0,0x128, 0, 0, 0x00,0x00,0x00,0x00}, + { 4, 1, 0x41a,0x233,0x670,0x3c0,0x143,128, 0, 0x00,0x00,0x00,0x00}, + { 5, 2, 0x578,0x293,0x670,0x3c0,0x032, 0, 0, 0x00,0x00,0x00,0x00}, + { 8, 5, 0x6d6,0x323,0x670,0x3c0,0x128, 0, 0, 0x00,0x00,0x00,0x00} }; -static const UCHAR SiS310_NTSCTiming[] = { /* TW: New (checked 1.09, 1.10.6s) */ +static const UCHAR SiS310_NTSCTiming[] = { 0x17,0x1d,0x03,0x09,0x05,0x06,0x0c,0x0c, 0x94,0x49,0x01,0x0a,0x06,0x0d,0x04,0x0a, 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x1b, @@ -1542,7 +1701,7 @@ 0x00,0x40,0x44,0x00,0xdb,0x02,0x3b,0x00 }; -static const UCHAR SiS310_PALTiming[] = { /* TW: New (checked 1.09, 1.10.6s) */ +static const UCHAR SiS310_PALTiming[] = { 0x19,0x52,0x35,0x6e,0x04,0x38,0x3d,0x70, 0x94,0x49,0x01,0x12,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0x45,0x2b, @@ -1553,8 +1712,7 @@ 0x00,0x40,0x3e,0x00,0xe1,0x02,0x28,0x00 }; -#ifdef oldHV -static const UCHAR SiS310_HiTVExtTiming[] = { /* TW: New */ +static const UCHAR SiS310_HiTVExtTiming[] = { 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x64, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1565,7 +1723,7 @@ 0x63,0x4f,0x27,0x00,0xfc,0xff,0x6a,0x00 }; -static const UCHAR SiS310_HiTVSt1Timing[] = { /* TW: New */ +static const UCHAR SiS310_HiTVSt1Timing[] = { 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x65, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1576,7 +1734,7 @@ 0xaf,0x5d,0x0e,0x00,0xfc,0xff,0x2d,0x00 }; -static const UCHAR SiS310_HiTVSt2Timing[] = { /* TW: New */ +static const UCHAR SiS310_HiTVSt2Timing[] = { 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x64, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1587,7 +1745,7 @@ 0x63,0x4f,0x27,0x00,0xfc,0xff,0x6a,0x00 }; -static const UCHAR SiS310_HiTVTextTiming[] = { /* TW: New */ +static const UCHAR SiS310_HiTVTextTiming[] = { 0x32,0x65,0x2c,0x5f,0x08,0x31,0x3a,0x65, 0x28,0x02,0x01,0x3d,0x06,0x3e,0x35,0x6d, 0x06,0x14,0x3e,0x35,0x6d,0x00,0xc5,0x3f, @@ -1598,7 +1756,7 @@ 0x72,0x5c,0x11,0x00,0xfc,0xff,0x32,0x00 }; -static const UCHAR SiS310_HiTVGroup3Data[] = { /* TW: New */ +static const UCHAR SiS310_HiTVGroup3Data[] = { 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0x5f, 0x05,0x21,0xb2,0xb2,0x55,0x77,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1609,7 +1767,7 @@ 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -static const UCHAR SiS310_HiTVGroup3Simu[] = { /* TW: New */ +static const UCHAR SiS310_HiTVGroup3Simu[] = { 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0x95, 0xdb,0x20,0xb8,0xb8,0x55,0x47,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1620,7 +1778,7 @@ 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -static const UCHAR SiS310_HiTVGroup3Text[] = { /* TW: New */ +static const UCHAR SiS310_HiTVGroup3Text[] = { 0x00,0x1a,0x22,0x63,0x62,0x22,0x08,0xa7, 0xf5,0x20,0xce,0xce,0x55,0x47,0x2a,0xa6, 0x25,0x2f,0x47,0xfa,0xc8,0xff,0x8e,0x20, @@ -1630,14 +1788,13 @@ 0x01,0x05,0x03,0x7e,0x65,0x31,0x14,0x75, 0x18,0x05,0x18,0x05,0x4c,0xa8,0x01 }; -#endif typedef struct _SiS310_PanelDelayTblStruct { UCHAR timer[2]; } SiS310_PanelDelayTblStruct; -static const SiS310_PanelDelayTblStruct SiS310_PanelDelayTbl[]= /* TW: New */ +static const SiS310_PanelDelayTblStruct SiS310_PanelDelayTbl[]= { {{0x10,0x40}}, /* TW: from 650/301LVx 1.10.6s BIOS */ {{0x10,0x40}}, @@ -1705,31 +1862,31 @@ static const SiS310_LVDSDataStruct SiS310_LVDS320x480Data_1[]= { - {848, 433,400, 525}, - {848, 389,400, 525}, - {848, 433,400, 525}, - {848, 389,400, 525}, - {848, 518,400, 525}, - {1056,628,400, 525}, - {400, 525,400, 525}, - {800, 449,1000, 644}, - {800, 525,1000, 635} -}; - -static const SiS310_LVDSDataStruct SiS310_LVDS800x600Data_1[]= /* TW: New */ -{ - {848, 433,1060, 629}, - {848, 389,1060, 629}, - {848, 433,1060, 629}, - {848, 389,1060, 629}, - {848, 518,1060, 629}, + { 848, 433, 400, 525}, + { 848, 389, 400, 525}, + { 848, 433, 400, 525}, + { 848, 389, 400, 525}, + { 848, 518, 400, 525}, + {1056, 628, 400, 525}, + { 400, 525, 400, 525}, + { 800, 449,1000, 644}, + { 800, 525,1000, 635} +}; + +static const SiS310_LVDSDataStruct SiS310_LVDS800x600Data_1[]= +{ + { 848, 433,1060, 629}, + { 848, 389,1060, 629}, + { 848, 433,1060, 629}, + { 848, 389,1060, 629}, + { 848, 518,1060, 629}, {1056, 628,1056, 628}, {1056, 628,1056, 628}, - {800, 449,1000, 644}, - {800, 525,1000, 635} + { 800, 449,1000, 644}, + { 800, 525,1000, 635} }; -static const SiS310_LVDSDataStruct SiS310_LVDS800x600Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS800x600Data_2[]= { {1056, 628,1056, 628}, {1056, 628,1056, 628}, @@ -1738,24 +1895,24 @@ {1056, 628,1056, 628}, {1056, 628,1056, 628}, {1056, 628,1056, 628}, - {800, 449,1000, 644}, - {800, 525,1000, 635} + { 800, 449,1000, 644}, + { 800, 525,1000, 635} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1024x768Data_1[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1024x768Data_1[]= { - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 518,1344, 806}, /* 640x480 */ + { 840, 438,1344, 806}, + { 840, 409,1344, 806}, + { 840, 438,1344, 806}, + { 840, 409,1344, 806}, + { 840, 518,1344, 806}, /* 640x480 */ {1050, 638,1344, 806}, /* 800x600 */ {1344, 806,1344, 806}, /* 1024x768 */ - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1024x768Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1024x768Data_2[]= { {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1764,50 +1921,48 @@ {1344, 806,1344, 806}, {1344, 806,1344, 806}, {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1280x1024Data_1[]= /* TW: New */ -{ - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 518,1344, 806}, - {1050, 638,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} +static const SiS310_LVDSDataStruct SiS310_LVDS1280x1024Data_1[]= +{ + {1048, 442,1688,1066}, + {1048, 392,1688,1066}, + {1048, 442,1688,1066}, + {1048, 392,1688,1066}, + {1048, 522,1688,1066}, + {1208, 642,1688,1066}, + {1432, 810,1688,1066}, + {1688,1066,1688,1066} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1280x1024Data_2[]= /* TW: New */ -{ - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} +static const SiS310_LVDSDataStruct SiS310_LVDS1280x1024Data_2[]= +{ + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066}, + {1688,1066,1688,1066} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1400x1050Data_1[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1400x1050Data_1[]= { - {928, 416, 1688, 1066}, - {928, 366, 1688, 1066}, - {928, 416, 1688, 1066}, - {928, 366, 1688, 1066}, - {928, 496, 1688, 1066}, - {1088, 616, 1688, 1066}, - {1312, 784, 1688, 1066}, - {1568, 1040, 1688, 1066}, - {1688, 1066, 1688, 1066} + { 928, 416, 1688,1066}, + { 928, 366, 1688,1066}, + { 928, 416, 1688,1066}, + { 928, 366, 1688,1066}, + { 928, 496, 1688,1066}, + {1088, 616, 1688,1066}, + {1312, 784, 1688,1066}, + {1568,1040, 1688,1066}, + {1688,1066, 1688,1066} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1400x1050Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1400x1050Data_2[]= { {1688,1066, 1688,1066}, {1688,1066, 1688,1066}, @@ -1820,102 +1975,154 @@ {1688,1066, 1688,1066}, }; -/* TW: New: - from 300 series */ +static const SiS310_LVDSDataStruct SiS310_LVDS1600x1200Data_1[]= +{ + {1088, 450, 2048,1250}, + {1088, 400, 2048,1250}, + {1088, 450, 2048,1250}, + {1088, 400, 2048,1250}, + {1088, 530, 2048,1250}, + {1248, 650, 2048,1250}, + {1472, 818, 2048,1250}, + {1728,1066, 2048,1250}, + {1848,1066, 2048,1250}, + {2048,1250, 2048,1250} +}; + +static const SiS310_LVDSDataStruct SiS310_LVDS1600x1200Data_2[]= +{ + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250}, + {2048,1250, 2048,1250} +}; + +static const SiS310_LVDSDataStruct SiS310_LVDS1280x768Data_1[]= +{ + { 768, 438, 1408, 806}, + { 768, 388, 1408, 806}, + { 768, 438, 1408, 806}, + { 768, 388, 1408, 806}, + { 768, 518, 1408, 806}, + { 928, 638, 1408, 806}, + {1152, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806} +}; + +static const SiS310_LVDSDataStruct SiS310_LVDS1280x768Data_2[]= +{ + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806}, + {1408, 806, 1408, 806} +}; + static const SiS310_LVDSDataStruct SiS310_LVDS1024x600Data_1[]= { - {840, 604,1344, 800}, - {840, 560,1344, 800}, - {840, 604,1344, 800}, - {840, 560,1344, 800}, - {840, 689,1344, 800}, - {1050, 800,1344, 800}, - {1344, 800,1344, 800}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 840, 604, 1344, 800}, + { 840, 560, 1344, 800}, + { 840, 604, 1344, 800}, + { 840, 560, 1344, 800}, + { 840, 689, 1344, 800}, + {1050, 800, 1344, 800}, + {1344, 800, 1344, 800}, + { 800, 449, 1280, 801}, + { 800, 525, 1280, 813} }; -/* TW: New: - from 300 series */ static const SiS310_LVDSDataStruct SiS310_LVDS1024x600Data_2[]= { - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {1344, 800,1344, 800}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + {1344, 800, 1344, 800}, + { 800, 449, 1280, 801}, + { 800, 525, 1280, 813} }; -/* TW: New: - from 300 series */ static const SiS310_LVDSDataStruct SiS310_LVDS1152x768Data_1[]= { - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 518,1344, 806}, - {1050, 638,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 840, 438, 1344, 806}, + { 840, 409, 1344, 806}, + { 840, 438, 1344, 806}, + { 840, 409, 1344, 806}, + { 840, 518, 1344, 806}, + {1050, 638, 1344, 806}, + {1344, 806, 1344, 806}, + { 800, 449, 1280, 801}, + { 800, 525, 1280, 813} }; -/* TW: New: - from 300 series */ static const SiS310_LVDSDataStruct SiS310_LVDS1152x768Data_2[]= { - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + {1344, 806, 1344, 806}, + { 800, 449, 1280, 801}, + { 800, 525, 1280, 813} +}; + +/* TW: Pass 1:1 data */ +static const SiS310_LVDSDataStruct SiS310_LVDSXXXxXXXData_1[]= +{ + { 800, 449, 800, 449}, + { 800, 449, 800, 449}, + { 900, 449, 900, 449}, + { 900, 449, 900, 449}, + { 800, 525, 800, 525}, /* 640x480 */ + {1056, 628, 1056, 628}, /* 800x600 */ + {1344, 806, 1344, 806}, /* 1024x768 */ + {1344,1066, 1344,1066}, /* 1280x1024 */ /* INSERTED ! */ + {1688, 806, 1688, 806}, /* 1280x768 */ + /* No other panels ! */ }; -/* TW: New in 650/LVDS BIOS - pass 1:1 data */ -static const SiS310_LVDSDataStruct SiS310_LVDSXXXxXXXData_1[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS640x480Data_1[]= { - { 800, 449, 800, 449}, { 800, 449, 800, 449}, - { 900, 449, 900, 449}, - { 900, 449, 900, 449}, + { 800, 449, 800, 449}, + { 800, 449, 800, 449}, + { 800, 449, 800, 449}, { 800, 525, 800, 525}, {1056, 628,1056, 628}, - {1344, 806,1344, 806}, - {1688, 806,1688, 806} -}; - -static const SiS310_LVDSDataStruct SiS310_LVDS640x480Data_1[]= /* TW: New */ -{ - {800, 449, 800, 449}, - {800, 449, 800, 449}, - {800, 449, 800, 449}, - {800, 449, 800, 449}, - {800, 525, 800, 525}, - {1056, 628,1056, 628}, {1056, 628,1056, 628}, {1056, 628,1056, 628}, {1056, 628,1056, 628} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1280x960Data_1[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1280x960Data_1[]= { - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 438,1344, 806}, - {840, 409,1344, 806}, - {840, 518,1344, 806}, + { 840, 438,1344, 806}, + { 840, 409,1344, 806}, + { 840, 438,1344, 806}, + { 840, 409,1344, 806}, + { 840, 518,1344, 806}, {1050, 638,1344, 806}, {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LVDS1280x960Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LVDS1280x960Data_2[]= { {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1924,24 +2131,26 @@ {1344, 806,1344, 806}, {1344, 806,1344, 806}, {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LCDA1400x1050Data_1[]= /* TW: New */ +/* LCDA */ + +static const SiS310_LVDSDataStruct SiS310_LCDA1400x1050Data_1[]= { /* TW: Might be temporary (invalid) data */ - {928, 416, 1688, 1066}, - {928, 366, 1688, 1066}, - {1008, 416, 1688, 1066}, - {1008, 366, 1688, 1066}, - {1200, 530, 1688, 1066}, - {1088, 616, 1688, 1066}, - {1312, 784, 1688, 1066}, - {1568, 1040, 1688, 1066}, - {1688, 1066, 1688, 1066} + { 928, 416, 1688,1066}, + { 928, 366, 1688,1066}, + {1008, 416, 1688,1066}, + {1008, 366, 1688,1066}, + {1200, 530, 1688,1066}, + {1088, 616, 1688,1066}, + {1312, 784, 1688,1066}, + {1568,1040, 1688,1066}, + {1688,1066, 1688,1066} }; -static const SiS310_LVDSDataStruct SiS310_LCDA1400x1050Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LCDA1400x1050Data_2[]= { /* TW: Temporary data. Not valid */ {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1950,11 +2159,11 @@ {1344, 806,1344, 806}, {1344, 806,1344, 806}, {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LCDA1600x1200Data_1[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LCDA1600x1200Data_1[]= { /* TW: Temporary data. Not valid */ {1344, 806,1344, 806}, {1344, 806,1344, 806}, @@ -1963,11 +2172,11 @@ {1344, 806,1344, 806}, {1344, 806,1344, 806}, {1344, 806,1344, 806}, - {800, 449,1280, 801}, - {800, 525,1280, 813} + { 800, 449,1280, 801}, + { 800, 525,1280, 813} }; -static const SiS310_LVDSDataStruct SiS310_LCDA1600x1200Data_2[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_LCDA1600x1200Data_2[]= { /* TW: Temporary data. Not valid */ {0, 0, 0, 0}, {0, 0, 0, 0}, @@ -1983,7 +2192,53 @@ {0, 0, 0, 0} }; -static const SiS310_LVDSDataStruct SiS310_CHTVUNTSCData[]= /* TW: New */ +/* Chrontel TV */ + +static const SiS310_LVDSDataStruct SiS310_CHTVUNTSCData[]= +{ + { 840, 600, 840, 600}, + { 840, 600, 840, 600}, + { 840, 600, 840, 600}, + { 840, 600, 840, 600}, + { 784, 600, 784, 600}, + {1064, 750,1064, 750}, + {1160, 945,1160, 945} /* TW: For Ch7019 1024 */ +}; + +static const SiS310_LVDSDataStruct SiS310_CHTVONTSCData[]= +{ + { 840, 525, 840, 525}, + { 840, 525, 840, 525}, + { 840, 525, 840, 525}, + { 840, 525, 840, 525}, + { 784, 525, 784, 525}, + {1040, 700,1040, 700}, + {1160, 840,1160, 840} /* TW: For Ch7019 1024 */ +}; + +static const SiS310_LVDSDataStruct SiS310_CHTVUPALData[]= +{ + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + { 840, 625, 840, 625}, + { 960, 750, 960, 750}, + {1400,1000,1400,1000} /* TW: For Ch7019 1024 */ +}; + +static const SiS310_LVDSDataStruct SiS310_CHTVOPALData[]= +{ + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + { 840, 625, 840, 625}, + { 944, 625, 944, 625}, + {1400, 875,1400, 875} /* TW: For Ch7019 1024 */ +}; + +static const SiS310_LVDSDataStruct SiS310_CHTVUPALMData[]= { { 840, 600, 840, 600}, { 840, 600, 840, 600}, @@ -1994,7 +2249,7 @@ {1160, 945,1160, 945} /* TW: For Ch7019 1024 */ }; -static const SiS310_LVDSDataStruct SiS310_CHTVONTSCData[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_CHTVOPALMData[]= { { 840, 525, 840, 525}, { 840, 525, 840, 525}, @@ -2005,7 +2260,7 @@ {1160, 840,1160, 840} /* TW: For Ch7019 1024 */ }; -static const SiS310_LVDSDataStruct SiS310_CHTVUPALData[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_CHTVUPALNData[]= { {1008, 625,1008, 625}, {1008, 625,1008, 625}, @@ -2016,7 +2271,7 @@ {1400,1000,1400,1000} /* TW: For Ch7019 1024 */ }; -static const SiS310_LVDSDataStruct SiS310_CHTVOPALData[]= /* TW: New */ +static const SiS310_LVDSDataStruct SiS310_CHTVOPALNData[]= { {1008, 625,1008, 625}, {1008, 625,1008, 625}, @@ -2027,15 +2282,24 @@ {1400, 875,1400, 875} /* TW: For Ch7019 1024 */ }; +static const SiS310_LVDSDataStruct SiS310_CHTVSOPALData[]= /* TW: (super overscan - no effect on 7019) */ +{ + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + {1008, 625,1008, 625}, + { 840, 625, 840, 625}, + { 944, 625, 944, 625}, + {1400, 875,1400, 875} +}; + typedef struct _SiS310_LVDSDesStruct { USHORT LCDHDES; USHORT LCDVDES; } SiS310_LVDSDesStruct; -/* TW: PanelType arrays taken from 650/LVDS BIOS 1.10.0 */ - -static const SiS310_LVDSDesStruct SiS310_PanelType00_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType00_1[]= { { 0, 0}, { 0, 0}, @@ -2048,7 +2312,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType01_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType01_1[]= { { 0, 0}, { 0, 0}, @@ -2061,7 +2325,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType02_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType02_1[]= { { 0, 0}, { 0, 0}, @@ -2076,7 +2340,7 @@ }; -static const SiS310_LVDSDesStruct SiS310_PanelType03_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType03_1[]= { { 0, 0}, { 0, 0}, @@ -2089,7 +2353,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType04_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType04_1[]= { {1343, 798}, {1343, 794}, @@ -2102,7 +2366,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType05_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType05_1[]= { {1343, 798}, {1343, 794}, @@ -2115,7 +2379,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType06_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType06_1[]= { {1343, 798}, {1343, 794}, @@ -2128,7 +2392,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType07_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType07_1[]= { {1343, 798}, {1343, 794}, @@ -2141,7 +2405,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType08_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType08_1[]= /* 1400x1050 */ { { 0, 0}, { 0, 0}, @@ -2156,32 +2420,37 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType09_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType09_1[]= /* 1280x768 */ { - { 0, 448}, - { 0, 448}, - { 0, 448}, - { 0, 448}, - { 0, 524}, - { 0, 627}, - { 0, 805}, - { 0, 805}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0a_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0a_1[]= /* 1600x1200 */ { - {1059, 626}, - {1059, 624}, - {1059, 626}, - {1059, 624}, - {1059, 624}, - { 0, 627}, - { 0, 627}, - { 0, 0}, - { 0, 0} + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0b_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0b_1[]= { {1343, 798}, {1343, 794}, @@ -2194,7 +2463,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0c_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0c_1[]= { {1343, 798}, {1343, 794}, @@ -2207,7 +2476,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0d_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0d_1[]= { {1343, 798}, {1343, 794}, @@ -2220,7 +2489,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0e_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0e_1[]= { {1343, 798}, {1343, 794}, @@ -2233,7 +2502,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0f_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0f_1[]= { {1343, 798}, {1343, 794}, @@ -2246,7 +2515,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType00_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType00_2[]= { {980, 528}, {980, 503}, @@ -2259,7 +2528,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType01_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType01_2[]= { {1152, 622}, {1152, 597}, @@ -2272,7 +2541,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType02_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType02_2[]= { {1368, 754}, {1368, 729}, @@ -2287,7 +2556,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType03_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType03_2[]= { { 0, 0}, { 0, 0}, @@ -2298,7 +2567,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType04_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType04_2[]= { { 0, 0}, { 0, 0}, @@ -2311,7 +2580,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType05_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType05_2[]= { {1152, 622}, {1152, 597}, @@ -2324,7 +2593,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType06_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType06_2[]= { {1152, 622}, {1152, 597}, @@ -2337,7 +2606,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType07_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType07_2[]= { {1152, 622}, {1152, 597}, @@ -2350,8 +2619,20 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType08_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType08_2[]= /* 1400x1050 */ { + {1308, 741}, + {1308, 716}, + {1308, 741}, + {1308, 716}, + {1308, 781}, + {1388, 841}, + {1500, 925}, + {1628,1053}, + { 0,1065}, + { 0, 0}, + { 0, 0} +#if 0 {976, 527}, {976, 502}, {976, 527}, @@ -2361,34 +2642,37 @@ { 0, 627}, { 0, 0}, { 0, 0} +#endif }; -static const SiS310_LVDSDesStruct SiS310_PanelType09_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType09_2[]= /* 1280x768 */ { - { 0, 0}, - { 0, 0}, - { 0, 0}, - { 0, 0}, - { 0, 0}, - { 0, 0}, - { 0, 0}, - { 0, 0} + {1083, 622}, + {1083, 597}, + {1083, 622}, + {1083, 597}, + {1083, 662}, + {1163, 722}, + {1286, 805}, + { 0, 794}, + { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0a_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0a_2[]= /* 1600x1200 */ { - {976, 527}, - {976, 502}, - {976, 527}, - {976, 502}, - {976, 567}, - { 0, 627}, - { 0, 627}, - { 0, 0}, - { 0, 0} + {1568, 850}, + {1568, 825}, + {1568, 850}, + {1568, 825}, + {1568, 890}, + {1648, 950}, + {1760,1034}, + {1888,1162}, + {1948,1175}, + { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0b_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0b_2[]= { {1152, 622}, {1152, 597}, @@ -2401,7 +2685,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0c_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0c_2[]= { {1152, 622}, {1152, 597}, @@ -2414,7 +2698,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0d_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0d_2[]= { {1152, 622}, {1152, 597}, @@ -2427,7 +2711,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0e_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0e_2[]= { {1152, 622}, {1152, 597}, @@ -2440,7 +2724,7 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType0f_2[] = /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType0f_2[] = { {1152, 622}, {1152, 597}, @@ -2453,7 +2737,35 @@ { 0, 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1076_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelTypeNS_1[]= +{ + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 8, 0}, + { 0, 0}, + { 0, 0}, + { 0, 0}, + { 0, 806}, + { 0, 0 } +}; + +static const SiS310_LVDSDesStruct SiS310_PanelTypeNS_2[] = +{ + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0}, + { 0 , 0} +}; + +static const SiS310_LVDSDesStruct SiS310_PanelType1076_1[]= { /* 1024x768 - Checked (1.10.6s) */ { 0 , 0}, { 0 , 0}, @@ -2466,7 +2778,7 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1076_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1076_2[]= { /* 1024x768 - Checked (1.10.6s) */ { 1184, 622 }, { 1184, 597 }, @@ -2479,7 +2791,7 @@ { 0, 0 } }; -static const SiS310_LVDSDesStruct SiS310_PanelType1210_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1210_1[]= { /* 1280x1024 - Checked (1.10.6s) */ { 0 , 0}, { 0 , 0}, @@ -2492,7 +2804,7 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1210_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1210_2[]= { /* 1280x1024 - Checked (1.10.6s) */ { 0 , 0}, { 0 , 0}, @@ -2505,7 +2817,7 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1296_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1296_1[]= { /* 1400x1050 - Checked (1.10.6s) */ { 0 , 0}, { 0 , 0}, @@ -2518,7 +2830,7 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1296_2[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1296_2[]= { /* 1400x1050 - Checked (1.10.6s) - looks heavily invalid */ { 808 , 740}, { 0 , 715}, @@ -2531,7 +2843,7 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1600_1[]= /* TW: New */ +static const SiS310_LVDSDesStruct SiS310_PanelType1600_1[]= { /* 1600x1200 - Checked (1.10.6s) */ { 0 , 0}, { 0 , 0}, @@ -2545,8 +2857,8 @@ { 0 , 0} }; -static const SiS310_LVDSDesStruct SiS310_PanelType1600_2[]= /* TW: New */ -{ /* 1600x1200 - Checked (1.10.6s) - looks heavily invalid */ +static const SiS310_LVDSDesStruct SiS310_PanelType1600_2[]= +{ /* 1600x1200 - Checked (1.10.6s) - looks heavily invalid, not copied */ { 0 , 0}, { 0 , 0}, { 0 , 0}, @@ -2736,6 +3048,7 @@ #endif }; +/* 1 2 4 5 6 1c 1d 1f 20 21 23 25 */ static const SiS310_Part2PortTblStruct SiS310_CRT2Part2_1280x1024_3[] = { /* TW: Temporary data, invalid */ {{0x43,0x24,0x21,0x29,0x19,0xea,0x23,0x0a,0x07,0x32,0xc6,0x42}}, @@ -2750,7 +3063,7 @@ }; static const SiS310_Part2PortTblStruct SiS310_CRT2Part2_1400x1050_3[] = -{ /* TW: Temporary data, invalid */ +{ {{0x43,0x24,0x21,0x29,0x19,0xea,0x23,0x0a,0x07,0x32,0xc6,0x42}}, {{0x43,0x24,0x21,0x29,0x19,0xea,0x23,0x0a,0x07,0x32,0xc6,0x42}}, {{0x43,0x24,0x21,0x29,0x19,0xea,0x23,0x0a,0x07,0x32,0xc6,0x42}}, @@ -3385,7 +3698,7 @@ 0x00 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_1[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_1[] = { {{0x6b,0x4f,0x8f,0x55,0x85,0xaa,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, @@ -3407,7 +3720,7 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_1[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_1[] = { {{0x73,0x4f,0x97,0x53,0x84,0xb4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x05, @@ -3432,35 +3745,35 @@ 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_1[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_1[] = { - {{0x7e,0x4f,0x82,0x56,0x04,0xb8,0x1f, + {{0x7e,0x4f,0x82,0x58,0x04,0xb8,0x1f, 0x90,0x84,0x8f,0xb9,0x30,0x00,0x06, - 0x00 }}, - {{0x7e,0x4f,0x82,0x56,0x04,0x86,0x1f, + 0x00}}, + {{0x7e,0x4f,0x82,0x58,0x04,0x86,0x1f, 0x5e,0x82,0x5d,0x87,0x10,0x00,0x06, - 0x00 }}, - {{0x7e,0x4f,0x82,0x56,0x04,0xb8,0x1f, + 0x00}}, + {{0x7e,0x4f,0x82,0x58,0x04,0xb8,0x1f, 0x90,0x84,0x8f,0xb9,0x30,0x00,0x06, - 0x00 }}, - {{0x7e,0x4f,0x82,0x56,0x04,0x86,0x1f, + 0x00}}, + {{0x7e,0x4f,0x82,0x58,0x04,0x86,0x1f, 0x5e,0x82,0x5d,0x87,0x10,0x00,0x06, - 0x00 }}, - {{0x7e,0x4f,0x82,0x56,0x04,0x08,0x3e, + 0x00}}, + {{0x7e,0x4f,0x82,0x58,0x04,0x08,0x3e, 0xe0,0x84,0xdf,0x09,0x00,0x00,0x06, - 0x00 }}, - {{0x92,0x63,0x96,0x6a,0x18,0x80,0xf0, + 0x00}}, + {{0x92,0x63,0x96,0x6c,0x18,0x80,0xf0, 0x58,0x8c,0x57,0x81,0x20,0x00,0x06, - 0x01 }}, - {{0xae,0x7f,0x92,0x86,0x94,0x28,0xf5, + 0x01}}, + {{0xae,0x7f,0x92,0x88,0x94,0x28,0xf5, 0x00,0x84,0xff,0x29,0x10,0x00,0x02, - 0x01 }}, - {{0xce,0x9f,0x92,0xa6,0x14,0x28,0x5a, + 0x01}}, + {{0xce,0x9f,0x92,0xa8,0x14,0x28,0x5a, 0x00,0x84,0xff,0x29,0x09,0x00,0x07, 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_1_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_1_H[] = { {{0x43,0x27,0x87,0x2d,0x1d,0xaa,0x1f, 0x90,0x85,0x8f,0xab,0x30,0x00,0x05, @@ -3482,7 +3795,7 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_1_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_1_H[] = { {{0x4b,0x27,0x8f,0x2b,0x1c,0xb4,0x1f, 0x92,0x89,0x8f,0xb5,0x30,0x00,0x05, @@ -3507,32 +3820,32 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_1_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_1_H[] = { - {{0x56,0x27,0x9a,0x2e,0x1c,0xb8,0x1f, + {{0x56,0x27,0x9a,0x31,0x1c,0xb8,0x1f, 0x90,0x84,0x8f,0xb9,0x30,0x00,0x05, - 0x00 }}, - {{0x56,0x27,0x9a,0x2e,0x1c,0x86,0x1f, + 0x00}}, + {{0x56,0x27,0x9a,0x31,0x1c,0x86,0x1f, 0x5e,0x82,0x5d,0x87,0x10,0x00,0x05, - 0x00 }}, - {{0x56,0x27,0x9a,0x2e,0x1c,0xb8,0x1f, + 0x00}}, + {{0x56,0x27,0x9a,0x31,0x1c,0xb8,0x1f, 0x90,0x84,0x8f,0xb9,0x30,0x00,0x05, - 0x00 }}, - {{0x56,0x27,0x9a,0x2e,0x1c,0x86,0x1f, + 0x00}}, + {{0x56,0x27,0x9a,0x31,0x1c,0x86,0x1f, 0x5e,0x82,0x5d,0x87,0x10,0x00,0x05, - 0x01 }}, - {{0x56,0x27,0x9a,0x2e,0x1c,0x08,0x3e, + 0x01}}, + {{0x56,0x27,0x9a,0x31,0x1c,0x08,0x3e, 0xe0,0x84,0xdf,0x09,0x00,0x00,0x05, - 0x00 }}, - {{0x60,0x31,0x84,0x38,0x86,0x80,0xf0, + 0x00}}, + {{0x60,0x31,0x84,0x3a,0x86,0x80,0xf0, 0x58,0x8c,0x57,0x81,0x20,0x00,0x01, - 0x01 }}, - {{0x6e,0x3f,0x92,0x46,0x94,0x28,0xf5, + 0x01}}, + {{0x6e,0x3f,0x92,0x48,0x94,0x28,0xf5, 0x00,0x84,0xff,0x29,0x10,0x00,0x01, - 0x01 }} + 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_2[]= /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_2[]= { {{0x7f,0x4f,0x83,0x62,0x12,0x72,0x3e, 0xff,0x84,0x8f,0x73,0x00,0x00,0x06, @@ -3554,7 +3867,7 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_2[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_2[] = { {{0xa3,0x4f,0x87,0x6e,0x9f,0x24,0xbb, 0x57,0x8e,0x8f,0x25,0x30,0x00,0x06, @@ -3579,36 +3892,36 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_2[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_2[] = { - {{0xce,0x4f,0x92,0x81,0x0f,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x03, - 0x00 }}, - {{0xce,0x4f,0x92,0x81,0x0f,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x03, - 0x00 }}, - {{0xce,0x4f,0x92,0x81,0x0f,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x03, - 0x00 }}, - {{0xce,0x4f,0x92,0x81,0x0f,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x03, - 0x00 }}, - {{0xce,0x4f,0x92,0x81,0x0f,0x28,0x9e, - 0x03,0x87,0xdf,0x29,0x01,0x00,0x03, - 0x00 }}, + {{0xce,0x72,0x91,0x81,0x8f,0x28,0x92, + 0xc8,0x8c,0x5d,0x5c,0x01,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x81,0x8f,0x28,0x92, + 0xaf,0x83,0x44,0x43,0x21,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x81,0x8f,0x28,0x92, + 0xc8,0x8c,0x5d,0x5c,0x01,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x81,0x8f,0x28,0x92, + 0xaf,0x83,0x44,0x43,0x21,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x81,0x8f,0x28,0x92, + 0xf0,0x84,0x85,0x84,0x11,0x00,0x02, + 0x01}}, {{0xce,0x63,0x92,0x8b,0x19,0x28,0xd4, 0x3f,0x83,0x57,0x29,0x01,0x00,0x03, - 0x01 }}, + 0x01}}, {{0xce,0x7f,0x92,0x99,0x07,0x28,0xd4, 0x93,0x87,0xff,0x29,0x21,0x00,0x07, - 0x01 }}, - {{0xce,0x9f,0x92,0xa6,0x14,0x28,0x5a, + 0x01}}, + {{0xce,0x9f,0x92,0xa8,0x14,0x28,0x5a, 0x00,0x84,0xff,0x29,0x09,0x00,0x07, 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_2_H[] = /* TW: New */ -{ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1800x600_2_H[] = +{ {{0x57,0x27,0x9b,0x3a,0x0a,0x72,0x3e, 0xff,0x84,0x8f,0x73,0x00,0x00,0x01, 0x00 }}, @@ -3629,7 +3942,7 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_2_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x768_2_H[] = { {{0x7b,0x27,0x9f,0x46,0x97,0x24,0xbb, 0x57,0x8e,0x8f,0x25,0x30,0x00,0x01, @@ -3654,32 +3967,32 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_2_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x1024_2_H[] = { - {{0xa6,0x27,0x8a,0x59,0x87,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x06, - 0x00 }}, - {{0xa6,0x27,0x8a,0x59,0x87,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x06, - 0x00 }}, - {{0xa6,0x27,0x8a,0x59,0x87,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x06, - 0x00 }}, - {{0xa6,0x27,0x8a,0x59,0x87,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x06, - 0x00 }}, - {{0xa6,0x27,0x8a,0x59,0x87,0x28,0x9e, - 0x03,0x87,0xdf,0x29,0x01,0x00,0x06, - 0x00 }}, + {{0xa6,0x4a,0x89,0x59,0x07,0x28,0x92, + 0xc8,0x8c,0x5d,0x5c,0x01,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x59,0x07,0x28,0x92, + 0xaf,0x83,0x44,0x43,0x21,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x59,0x07,0x28,0x92, + 0xc8,0x8c,0x5d,0x5c,0x01,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x59,0x07,0x28,0x92, + 0xfa,0x83,0x44,0x43,0x31,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x59,0x07,0x28,0x92, + 0xf0,0x84,0x85,0x84,0x11,0x00,0x06, + 0x01}}, {{0x9c,0x31,0x80,0x59,0x87,0x28,0xd4, 0x3f,0x83,0x57,0x29,0x01,0x00,0x06, - 0x01 }}, - {{0x8e,0x3f,0x92,0x79,0x07,0x28,0xd4, + 0x01}}, + {{0x8e,0x3f,0x92,0x59,0x07,0x28,0xd4, 0x93,0x87,0xff,0x29,0x21,0x00,0x06, 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1XXXxXXX_1[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1XXXxXXX_1[] = { {{0x5f,0x4f,0x82,0x55,0x81,0xbf,0x1f, 0x9c,0x8e,0x96,0xb9,0x30,0x00,0x05, @@ -3702,12 +4015,15 @@ {{0xa3,0x7f,0x87,0x86,0x97,0x24,0xf5, 0x02,0x88,0xff,0x25,0x10,0x00,0x02, 0x01}}, + {{0xce,0x9f,0x92,0xa8,0x14,0x28,0x5a, + 0x00,0x84,0xff,0x29,0x09,0x00,0x07, + 0x01}}, {{0xce,0x9f,0x92,0xa9,0x17,0x24,0xf5, 0x02,0x88,0xff,0x25,0x10,0x00,0x07, 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1XXXxXXX_1_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT1XXXxXXX_1_H[] = { {{0x38,0x27,0x9c,0x2c,0x80,0xbf,0x1f, 0x9c,0x8e,0x96,0xb9,0x30,0x00,0x00, @@ -3732,8 +4048,36 @@ 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_1[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_1[] = { + {{0x6f,0x4f,0x93,0x54,0x82,0x9e,0x1f, + 0x8f,0x81,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x6c,0x1f, + 0x5e,0x81,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x9e,0x1f, + 0x90,0x83,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x6c,0x1f, + 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x6f,0x4f,0x93,0x54,0x82,0xee,0x1f, + 0xdf,0x82,0xdf,0xef,0x10,0x00,0x05, + 0x00}}, + {{0x83,0x63,0x87,0x68,0x16,0x66,0xf0, + 0x57,0x8e,0x57,0x67,0x20,0x00,0x06, + 0x01}}, + {{0x9f,0x7f,0x83,0x84,0x92,0x0e,0xf1, + 0xff,0x86,0xff,0x0f,0x10,0x00,0x02, + 0x01,}}, + {{0xbf,0x9f,0x83,0xa4,0x12,0x0e,0xde, + 0xff,0x86,0xff,0x0f,0x01,0x00,0x07, + 0x01}}, + {{0xce,0xae,0x92,0xb3,0x01,0x28,0x10, + 0x19,0x80,0x19,0x29,0x0f,0x00,0x03, + 0x00}} +#if 0 {{0x6f,0x4f,0x93,0x54,0x82,0x9e,0x1f, 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, 0x00}}, @@ -3761,11 +4105,40 @@ {{0xce,0xae,0x92,0xb3,0x01,0x28,0x10, 0x1a,0x80,0x19,0x29,0x0f,0x00,0x03, 0x00}} +#endif }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_1_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_1_H[] = { {{0x47,0x27,0x8b,0x2c,0x1a,0x9e,0x1f, + 0x8f,0x81,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, + 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x30,0x1e,0x9e,0x1f, + 0x90,0x83,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, + 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0xee,0x1f, + 0xdf,0x86,0xdf,0xef,0x10,0x00,0x05, + 0x00}}, + {{0x51,0x31,0x95,0x36,0x04,0x66,0xf0, + 0x57,0x8e,0x57,0x67,0x20,0x00,0x01, + 0x01}}, + {{0x5f,0x3f,0x83,0x44,0x92,0x0e,0xf1, + 0xff,0x86,0xff,0x0f,0x10,0x00,0x01, + 0x01}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x0e,0x5a, + 0x02,0x86,0xff,0x0f,0x09,0x00,0x05, + 0x01}}, + {{0x76,0x56,0x9a,0x5b,0x89,0x28,0x10, + 0x1c,0x80,0x19,0x29,0x0b,0x00,0x05, + 0x00}} +#if 0 + {{0x47,0x27,0x8b,0x2c,0x1a,0x9e,0x1f, 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, 0x00}}, {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, @@ -3792,10 +4165,39 @@ {{0x76,0x56,0x9a,0x5b,0x89,0x28,0x10, 0x1c,0x80,0x19,0x29,0x0b,0x00,0x05, 0x00}} +#endif }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_2[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_2[] = { + {{0xce,0x72,0x91,0x84,0x92,0x28,0x92, + 0xd7,0x8b,0x5d,0x5c,0x21,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x84,0x92,0x28,0x92, + 0xbe,0x82,0x44,0x43,0x01,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x84,0x92,0x28,0x92, + 0xd7,0x8b,0x5d,0x5c,0x21,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x84,0x92,0x28,0x92, + 0xbe,0x82,0x44,0x43,0x01,0x00,0x02, + 0x01}}, + {{0xce,0x72,0x91,0x84,0x92,0x28,0x92, + 0xff,0x83,0x85,0x84,0x11,0x00,0x02, + 0x01}}, + {{0xce,0x63,0x92,0x8e,0x1c,0x28,0xd4, + 0x3f,0x83,0x57,0x29,0x01,0x00,0x03, + 0x01}}, + {{0xce,0x7f,0x92,0x9c,0x0a,0x28,0xd4, + 0x93,0x87,0xff,0x29,0x21,0x00,0x07, + 0x01}}, + {{0xce,0x9f,0x92,0xac,0x1a,0x28,0x5a, + 0x13,0x87,0xff,0x29,0x29,0x00,0x07, + 0x01}}, + {{0xce,0xae,0x92,0xbc,0x0a,0x28,0x10, + 0x20,0x84,0x19,0x29,0x0f,0x00,0x03, + 0x00}} +#if 0 {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9a, 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x03, 0x00}}, @@ -3823,10 +4225,39 @@ {{0xce,0xae,0x92,0xbc,0x0a,0x28,0x10, 0x20,0x84,0x19,0x29,0x0f,0x00,0x03, 0x00}} +#endif }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_2_H[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11400x1050_2_H[] = { + {{0xa6,0x4a,0x89,0x5c,0x0a,0x28,0x92, + 0xd7,0x8b,0x5d,0x5c,0x21,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x5c,0x0a,0x28,0x92, + 0xbe,0x82,0x44,0x43,0x01,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x5c,0x0a,0x28,0x92, + 0xd7,0x8b,0x5d,0x5c,0x21,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x5c,0x0a,0x28,0x92, + 0xbe,0x82,0x44,0x43,0x01,0x00,0x06, + 0x01}}, + {{0xa6,0x4a,0x89,0x5c,0x0a,0x28,0x92, + 0xff,0x83,0x85,0x84,0x11,0x00,0x06, + 0x01}}, + {{0x9c,0x31,0x80,0x5c,0x8a,0x28,0xd4, + 0x3f,0x83,0x57,0x29,0x01,0x00,0x06, + 0x01}}, + {{0x8e,0x3f,0x92,0x5c,0x0a,0x28,0xd4, + 0x93,0x87,0xff,0x29,0x21,0x00,0x06, + 0x01}}, + {{0x7e,0x4f,0x82,0x5c,0x0a,0x28,0x5a, + 0x13,0x87,0xff,0x29,0x29,0x00,0x06, + 0x01}}, + {{0x76,0x56,0x9a,0x64,0x92,0x28,0x10, + 0x20,0x84,0x19,0x29,0x0f,0x00,0x05, + 0x00}} +#if 0 {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9a, 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x06, 0x00}}, @@ -3854,6 +4285,131 @@ {{0x76,0x56,0x9a,0x64,0x92,0x28,0x10, 0x20,0x84,0x19,0x29,0x0f,0x00,0x05, 0x00}} +#endif +}; + +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x768_1[] = +{ + {{0x5b,0x4f,0x9f,0x55,0x19,0xb4,0x1f, + 0x9c,0x8e,0x8f,0xb5,0x10,0x00,0x01, + 0x00}}, + {{0x5b,0x4f,0x9f,0x55,0x19,0x82,0x1f, + 0x6a,0x8c,0x5d,0x83,0x30,0x00,0x01, + 0x00}}, + {{0x5b,0x4f,0x9f,0x55,0x19,0xb4,0x1f, + 0x9c,0x8e,0x8f,0xb5,0x10,0x00,0x01, + 0x00}}, + {{0x5b,0x4f,0x9f,0x55,0x19,0x82,0x1f, + 0x6a,0x8c,0x5d,0x83,0x30,0x00,0x01, + 0x00}}, + {{0x5b,0x4f,0x9f,0x55,0x19,0x04,0x3e, + 0xec,0x8e,0xdf,0x05,0x20,0x00,0x01, + 0x00}}, + {{0x6f,0x63,0x93,0x69,0x8d,0x7c,0xf0, + 0x64,0x86,0x57,0x7d,0x20,0x00,0x05, + 0x01}}, + {{0x8b,0x7f,0x8f,0x85,0x09,0x24,0xf5, + 0x0c,0x8e,0xff,0x25,0x30,0x00,0x02, + 0x01}}, + {{0xab,0x9f,0x8f,0xa5,0x89,0x24,0xf5, + 0x0c,0x8e,0xff,0x25,0x30,0x00,0x06, + 0x01}}, + {{0xab,0x9f,0x8f,0xa5,0x89,0x24,0xf5, + 0x0c,0x8e,0xff,0x25,0x30,0x00,0x06, + 0x01}} +}; + +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x768_1_H[] = +{ + {{0x47,0x27,0x8b,0x2c,0x1a,0x9e,0x1f, + 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, + 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x30,0x1e,0x9e,0x1f, + 0x92,0x86,0x8f,0x9f,0x30,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, + 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + 0x00}}, + {{0x47,0x27,0x8b,0x2c,0x1a,0xee,0x1f, + 0xe2,0x86,0xdf,0xef,0x10,0x00,0x05, + 0x00}}, + {{0x51,0x31,0x95,0x36,0x04,0x66,0xf0, + 0x5a,0x8e,0x57,0x67,0x20,0x00,0x01, + 0x01}}, + {{0x5f,0x3f,0x83,0x44,0x92,0x0e,0xf5, + 0x02,0x86,0xff,0x0f,0x10,0x00,0x01, + 0x01}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x0e,0x5a, + 0x02,0x86,0xff,0x0f,0x09,0x00,0x05, + 0x01}}, + {{0x6f,0x4f,0x93,0x54,0x82,0x0e,0x5a, + 0x02,0x86,0xff,0x0f,0x09,0x00,0x05, + 0x01}} +}; + +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x768_2[] = +{ + {{0xab,0x60,0x9f,0x80,0x04,0x24,0xbb, + 0x54,0x86,0xdb,0xda,0x00,0x00,0x02, + 0x00}}, + {{0xab,0x60,0x9f,0x80,0x04,0x24,0xbb, + 0x3b,0x8d,0xc2,0xc1,0x00,0x00,0x02, + 0x00}}, + {{0xab,0x60,0x9f,0x80,0x04,0x24,0xbb, + 0x54,0x86,0xdb,0xda,0x00,0x00,0x02, + 0x00}}, + {{0xab,0x60,0x9f,0x80,0x04,0x24,0xbb, + 0x3b,0x8d,0xc2,0xc1,0x00,0x00,0x02, + 0x00}}, + {{0xab,0x60,0x9f,0x80,0x04,0x24,0xb3, + 0x7c,0x8e,0x03,0x02,0x10,0x00,0x02, + 0x01}}, + {{0xab,0x63,0x8f,0x8a,0x8e,0x24,0xf1, + 0xb6,0x88,0x57,0x25,0x10,0x00,0x02, + 0x01}}, + {{0xab,0x7f,0x8f,0x98,0x9c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x02, + 0x01}}, + {{0xab,0x9f,0x8f,0xa8,0x8c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x06, + 0x01}}, + {{0xab,0x9f,0x8f,0xa8,0x8c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x06, + 0x01}} +}; + +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11280x768_2_H[] = +{ + {{0x83,0x38,0x97,0x58,0x9c,0x24,0xbb, + 0x54,0x86,0xdb,0xda,0x00,0x00,0x01, + 0x00}}, + {{0x83,0x38,0x97,0x58,0x9c,0x24,0xbb, + 0x3b,0x8d,0xc2,0xc1,0x00,0x00,0x01, + 0x00}}, + {{0x83,0x38,0x97,0x58,0x9c,0x24,0xbb, + 0x54,0x86,0xdb,0xda,0x00,0x00,0x01, + 0x00}}, + {{0x83,0x38,0x97,0x58,0x9c,0x24,0xbb, + 0x3b,0x8d,0xc2,0xc1,0x00,0x00,0x01, + 0x00}}, + {{0x83,0x38,0x97,0x58,0x9c,0x24,0xb3, + 0x7c,0x8e,0x03,0x02,0x10,0x00,0x01, + 0x01}}, + {{0x79,0x31,0x9d,0x58,0x9c,0x24,0xf1, + 0xb6,0x88,0x57,0x25,0x10,0x00,0x01, + 0x01}}, + {{0x6b,0x3f,0x8f,0x58,0x9c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x01, + 0x01}}, + {{0xab,0x9f,0x8f,0xa8,0x8c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x06, + 0x01}}, + {{0xab,0x9f,0x8f,0xa8,0x8c,0x24,0xf5, + 0x0a,0x8c,0xff,0x25,0x30,0x00,0x06, + 0x01}} }; static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11024x600_1[] = @@ -4057,131 +4613,143 @@ }; static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11600x1200_1[] = -{ /* TW: Temporary data - invalid */ - {{0x6f,0x4f,0x93,0x54,0x82,0x9e,0x1f, - 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, +{ + {{0x83,0x4f,0x87,0x51,0x09,0xc0,0x1f, + 0x90,0x84,0x8f,0xc1,0x30,0x00,0x06, 0x00}}, - {{0x6f,0x4f,0x93,0x54,0x82,0x6c,0x1f, - 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + {{0x83,0x4f,0x87,0x51,0x09,0x8e,0x1f, + 0x5e,0x82,0x5d,0x8f,0x10,0x00,0x06, 0x00}}, - {{0x6f,0x4f,0x93,0x54,0x82,0x9e,0x1f, - 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, + {{0x83,0x4f,0x87,0x51,0x09,0xc0,0x1f, + 0x90,0x84,0x8f,0xc1,0x30,0x00,0x06, 0x00}}, - {{0x6f,0x4f,0x93,0x54,0x82,0x6c,0x1f, - 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + {{0x83,0x4f,0x87,0x51,0x09,0x8e,0x1f, + 0x5e,0x82,0x5d,0x8f,0x10,0x00,0x06, 0x00}}, - {{0x6f,0x4f,0x93,0x54,0x82,0xee,0x1f, - 0xe2,0x86,0xdf,0xef,0x10,0x00,0x05, + {{0x83,0x4f,0x87,0x51,0x09,0x10,0x3e, + 0xe0,0x84,0xdf,0x11,0x00,0x00,0x06, 0x00}}, - {{0x83,0x63,0x87,0x68,0x16,0x66,0xf0, - 0x5a,0x8e,0x57,0x67,0x20,0x00,0x06, + {{0x97,0x63,0x9b,0x65,0x1d,0x88,0xf0, + 0x58,0x8c,0x57,0x89,0x20,0x00,0x06, 0x01}}, - {{0x9f,0x7f,0x83,0x84,0x92,0x0e,0xf5, - 0x02,0x86,0xff,0x0f,0x10,0x00,0x02, + {{0xb3,0x7f,0x97,0x81,0x99,0x30,0xf5, + 0x00,0x84,0xff,0x31,0x10,0x00,0x02, 0x01}}, - {{0xbf,0x9f,0x83,0xa4,0x12,0x0e,0x5a, - 0x02,0x86,0xff,0x0f,0x09,0x00,0x07, + {{0xd3,0x9f,0x97,0xa1,0x19,0x30,0x5a, + 0x00,0x84,0xff,0x31,0x09,0x00,0x07, 0x01}}, - {{0xce,0xae,0x92,0xb3,0x01,0x28,0x10, - 0x1a,0x80,0x19,0x29,0x0f,0x00,0x03, + {{0xe2,0xae,0x86,0xb0,0x88,0x4a,0x10, + 0x1a,0x8e,0x19,0x4b,0x2f,0x00,0x03, + 0x00}}, + {{0xfb,0xc7,0x9f,0xc9,0x81,0xe0,0x10, + 0xb0,0x84,0xaf,0xe1,0x2f,0x00,0x07, 0x00}} }; static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11600x1200_1_H[] = -{ /* TW: Temporary data - invalid */ - {{0x47,0x27,0x8b,0x2c,0x1a,0x9e,0x1f, - 0x93,0x86,0x8f,0x9f,0x30,0x00,0x05, +{ + {{0x5b,0x27,0x9f,0x29,0x01,0xc0,0x1f, + 0x90,0x84,0x8f,0xc1,0x30,0x00,0x01, 0x00}}, - {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, - 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + {{0x5b,0x27,0x9f,0x29,0x01,0x8e,0x1f, + 0x5e,0x82,0x5d,0x8f,0x10,0x00,0x01, 0x00}}, - {{0x47,0x27,0x8b,0x30,0x1e,0x9e,0x1f, - 0x92,0x86,0x8f,0x9f,0x30,0x00,0x05, + {{0x5b,0x27,0x9f,0x29,0x01,0xc0,0x1f, + 0x90,0x84,0x8f,0xc1,0x30,0x00,0x01, 0x00}}, - {{0x47,0x27,0x8b,0x2c,0x1a,0x6c,0x1f, - 0x60,0x84,0x5d,0x6d,0x10,0x00,0x05, + {{0x5b,0x27,0x9f,0x29,0x01,0x8e,0x1f, + 0x5e,0x82,0x5d,0x8f,0x10,0x00,0x01, 0x00}}, - {{0x47,0x27,0x8b,0x2c,0x1a,0xee,0x1f, - 0xe2,0x86,0xdf,0xef,0x10,0x00,0x05, + {{0x5b,0x27,0x9f,0x29,0x01,0x10,0x3e, + 0xe0,0x84,0xdf,0x11,0x00,0x00,0x01, 0x00}}, - {{0x51,0x31,0x95,0x36,0x04,0x66,0xf0, - 0x5a,0x8e,0x57,0x67,0x20,0x00,0x01, + {{0x65,0x31,0x89,0x33,0x8b,0x88,0xf0, + 0x58,0x8c,0x57,0x89,0x20,0x00,0x01, 0x01}}, - {{0x5f,0x3f,0x83,0x44,0x92,0x0e,0xf5, - 0x02,0x86,0xff,0x0f,0x10,0x00,0x01, + {{0x73,0x3f,0x97,0x41,0x99,0x30,0xf5, + 0x00,0x84,0xff,0x31,0x10,0x00,0x01, 0x01}}, - {{0x6f,0x4f,0x93,0x54,0x82,0x0e,0x5a, - 0x02,0x86,0xff,0x0f,0x09,0x00,0x05, + {{0x83,0x4f,0x87,0x51,0x09,0x30,0x5a, + 0x00,0x84,0xff,0x31,0x09,0x00,0x06, 0x01}}, - {{0x76,0x56,0x9a,0x5b,0x89,0x28,0x10, - 0x1c,0x80,0x19,0x29,0x0b,0x00,0x05, + {{0x8a,0x56,0x8e,0x58,0x10,0x4a,0x10, + 0x1a,0x8e,0x19,0x4b,0x2f,0x00,0x06, + 0x00}}, + {{0x97,0x63,0x9b,0x65,0x1d,0xe0,0x10, + 0xb0,0x84,0xaf,0xe1,0x2f,0x00,0x06, 0x00}} }; static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11600x1200_2[] = -{ /* TW: Temporary data - invalid */ - {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x03, - 0x00}}, - {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x03, +{ + {{0xfb,0x88,0x87,0x90,0x08,0xe0,0x96, + 0x20,0x84,0xb9,0xb8,0x01,0x00,0x07, 0x01}}, - {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x03, - 0x00}}, - {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x03, - 0x00}}, - {{0xce,0x4f,0x92,0x8c,0x1a,0x28,0x9e, - 0x03,0x87,0xdf,0x29,0x01,0x00,0x03, - 0x00}}, - {{0xce,0x63,0x92,0x96,0x04,0x28,0xd4, - 0x3f,0x83,0x57,0x29,0x01,0x00,0x07, + {{0xfb,0x88,0x87,0x90,0x08,0xe0,0x96, + 0x07,0x8b,0xa0,0x9f,0x01,0x00,0x07, 0x01}}, - {{0xce,0x7f,0x92,0xa4,0x12,0x28,0xd4, - 0x93,0x87,0xff,0x29,0x21,0x00,0x07, + {{0xfb,0x88,0x87,0x90,0x08,0xe0,0x96, + 0x20,0x84,0xb9,0xb8,0x01,0x00,0x07, 0x01}}, - {{0xce,0x9f,0x92,0xb4,0x02,0x28,0x5a, - 0x13,0x87,0xff,0x29,0x29,0x00,0x03, + {{0xfb,0x88,0x87,0x90,0x08,0xe0,0x96, + 0x07,0x8b,0xa0,0x9f,0x01,0x00,0x07, 0x01}}, - {{0xce,0xae,0x92,0xbc,0x0a,0x28,0x10, - 0x20,0x84,0x19,0x29,0x0f,0x00,0x03, + {{0xfb,0x88,0x87,0x90,0x08,0xe0,0x96, + 0x48,0x8c,0xe1,0xe0,0x11,0x00,0x07, + 0x01}}, + {{0xfb,0x63,0x9f,0x9a,0x92,0xe0,0xd4, + 0x9b,0x8f,0x9d,0x9c,0x21,0x00,0x07, + 0x01}}, + {{0xfb,0x7f,0x9f,0xa8,0x80,0xe0,0xd4, + 0xef,0x83,0xff,0xe1,0x21,0x00,0x03, + 0x01}}, + {{0xfb,0x9f,0x9f,0xb8,0x90,0xe0,0x5a, + 0x6f,0x83,0xff,0xe1,0x29,0x00,0x03, + 0x01}}, + {{0xfb,0xae,0x9f,0xbf,0x97,0xe0,0x10, + 0x7c,0x80,0x19,0xe1,0x0f,0x00,0x03, + 0x00}}, + {{0xfb,0xc7,0x9f,0xc9,0x84,0xe0,0x10, + 0xc7,0x8b,0xaf,0xe1,0x0f,0x00,0x07, 0x00}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11600x1200_2_H[] = /* TW: New */ -{ /* TW: Temporary data - invalid */ - {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x06, - 0x00}}, - {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x06, - 0x00}}, - {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9a, - 0xdb,0x8f,0x8f,0x29,0x21,0x00,0x06, - 0x00}}, - {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9a, - 0xc2,0x86,0x5d,0x29,0x01,0x00,0x06, - 0x00}}, - {{0xa6,0x27,0x8a,0x64,0x92,0x28,0x9e, - 0x03,0x87,0xdf,0x29,0x01,0x00,0x06, - 0x00}}, - {{0x9c,0x31,0x80,0x64,0x92,0x28,0xd4, - 0x3f,0x83,0x57,0x29,0x01,0x00,0x06, +static const SiS310_LVDSCRT1DataStruct SiS310_LVDSCRT11600x1200_2_H[] = +{ + {{0xd3,0x60,0x9f,0x68,0x00,0xe0,0x96, + 0x20,0x84,0xb9,0xb8,0x01,0x00,0x02, 0x01}}, - {{0x8e,0x3f,0x92,0x64,0x12,0x28,0xd4, - 0x93,0x87,0xff,0x29,0x21,0x00,0x06, + {{0xd3,0x60,0x9f,0x68,0x00,0xe0,0x96, + 0x07,0x8b,0xa0,0x9f,0x01,0x00,0x02, 0x01}}, - {{0x7e,0x4f,0x82,0x64,0x12,0x28,0x5a, - 0x13,0x87,0xff,0x29,0x29,0x00,0x06, + {{0xd3,0x60,0x9f,0x68,0x00,0xe0,0x96, + 0x20,0x84,0xb9,0xb8,0x01,0x00,0x02, 0x01}}, - {{0x76,0x56,0x9a,0x64,0x92,0x28,0x10, - 0x20,0x84,0x19,0x29,0x0f,0x00,0x05, + {{0xd3,0x60,0x9f,0x68,0x00,0xe0,0x96, + 0x07,0x8b,0xa0,0x9f,0x01,0x00,0x02, + 0x01}}, + {{0xd3,0x60,0x9f,0x68,0x00,0xe0,0x96, + 0x48,0x8c,0xe1,0xe0,0x11,0x00,0x02, + 0x01}}, + {{0xc9,0x31,0x8d,0x68,0x00,0xe0,0xd4, + 0x9b,0x8f,0x9d,0x9c,0x21,0x00,0x03, + 0x01}}, + {{0xbb,0x3f,0x9f,0x68,0x80,0xe0,0xd4, + 0xef,0x83,0xff,0xe1,0x21,0x00,0x02, + 0x01}}, + {{0xab,0x4f,0x8f,0x68,0x80,0xe0,0x5a, + 0x6f,0x83,0xff,0xe1,0x29,0x00,0x02, + 0x01}}, + {{0xa3,0x56,0x87,0x67,0x9f,0xe0,0x10, + 0x7c,0x80,0x19,0xe1,0x0f,0x00,0x06, + 0x00}}, + {{0x97,0x63,0x9b,0x68,0x00,0xe0,0x10, + 0xc7,0x8b,0xaf,0xe1,0x0f,0x00,0x02, 0x00}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1UNTSC[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1UNTSC[] = { {{0x64,0x4f,0x88,0x56,0x9f,0x56,0x3e, 0xe8,0x84,0x8f,0x57,0x20,0x00,0x01, @@ -4206,7 +4774,7 @@ 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1ONTSC[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1ONTSC[] = { {{0x63,0x4f,0x87,0x5a,0x9f,0x0b,0x3e, 0xc0,0x84,0x8f,0x0c,0x20,0x00,0x01, @@ -4231,7 +4799,7 @@ 0x01 }} }; -static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1UPAL[] = /* TW: New */ +static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1UPAL[] = { {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, 0xf8,0x83,0x8f,0x70,0x20,0x00,0x05, @@ -4256,8 +4824,8 @@ 0x01}} }; -static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1OPAL[] = /* TW: New */ -{ +static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1OPAL[] = +{ {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, 0x00 }}, @@ -4281,13 +4849,39 @@ 0x01 }} }; -/* TW: New data for Chrontel 7019 (From 650/LVDS BIOS 1.10.0) */ +static const SiS310_LVDSCRT1DataStruct SiS310_CHTVCRT1SOPAL[] = +{ + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xde,0x81,0x5d,0x70,0x00,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xf0,0x83,0x8f,0x70,0x20,0x00,0x05, + 0x00 }}, + {{0x79,0x4f,0x9d,0x5a,0x90,0x6f,0x3e, + 0xde,0x81,0x5d,0x70,0x00,0x00,0x05, + 0x00 }}, + {{0x64,0x4f,0x88,0x58,0x9d,0x6f,0xba, + 0x15,0x83,0xdf,0x70,0x00,0x00,0x01, + 0x00 }}, + {{0x71,0x63,0x95,0x69,0x8c,0x6f,0xf0, + 0x5a,0x8b,0x57,0x70,0x20,0x00,0x05, + 0x01 }}, + {{0xaa,0x7f,0x8e,0x8f,0x96,0x69,0xf5, /* TW: 1024x768 */ + 0x28,0x88,0xff,0x6a,0x10,0x00,0x02, + 0x01 }} +}; + +/* TW: Data for Chrontel 7019 */ typedef struct _SiS310_CHTVRegDataStruct { UCHAR Reg[16]; } SiS310_CHTVRegDataStruct; -static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UNTSC[] = { +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UNTSC[] = +{ {{0x4a,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x4a,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x4a,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, @@ -4297,7 +4891,8 @@ {{0xee,0x77,0xbb,0x66,0x87,0x32,0x01,0x5a,0x04,0x00,0x80,0x1b,0xd3,0xf2,0x36,0x00}} }; -static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_ONTSC[] = { +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_ONTSC[] = +{ {{0x49,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x49,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x49,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, @@ -4307,7 +4902,8 @@ {{0xed,0x77,0xbb,0x66,0x8c,0x21,0x02,0x5a,0x04,0x00,0x80,0x1f,0x9f,0xc1,0x0c,0x00}} }; -static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UPAL[] = { +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UPAL[] = +{ {{0x41,0x7f,0xb7,0x34,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x41,0x7f,0xb7,0x80,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x41,0x7f,0xb7,0x34,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, @@ -4317,7 +4913,8 @@ {{0xe5,0x7f,0xb7,0x1d,0xa7,0x3e,0x04,0x5a,0x05,0x00,0x80,0x20,0x3e,0xe4,0x22,0x00}} }; -static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_OPAL[] = { +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_OPAL[] = +{ {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x41,0x7f,0xb7,0x86,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, @@ -4327,6 +4924,61 @@ {{0xe4,0x7f,0xb7,0x1e,0xaf,0x29,0x37,0x5a,0x05,0x00,0x80,0x25,0x8c,0xb2,0x2a,0x00}} }; +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_SOPAL[] = +{ + {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x86,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x86,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x61,0x7f,0xb7,0x99,0x84,0x35,0x04,0x5a,0x05,0x00,0x80,0x26,0x2a,0x55,0x5d,0x00}}, + {{0xc1,0x7f,0xb7,0x4d,0x8c,0x1e,0x31,0x5a,0x05,0x00,0x80,0x26,0x78,0x19,0x34,0x00}}, + {{0xe4,0x7f,0xb7,0x1e,0xaf,0x29,0x37,0x5a,0x05,0x00,0x80,0x25,0x8c,0xb2,0x2a,0x00}} +}; + +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UPALM[] = +{ + {{0x52,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x52,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x52,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x52,0x77,0xbb,0x94,0x84,0x48,0xfe,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x72,0x77,0xbb,0x6e,0x84,0x2e,0x02,0x5a,0x04,0x00,0x80,0x20,0x76,0xdb,0x6e,0x00}}, + {{0xd7,0x77,0xb7,0xc8,0x84,0x3b,0x02,0x5a,0x04,0x00,0x80,0x19,0x84,0x0a,0xc7,0x00}}, + {{0xf6,0x77,0xbb,0x66,0x87,0x32,0x01,0x5a,0x04,0x00,0x80,0x1b,0xdc,0xb0,0x8d,0x00}} +}; + +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_OPALM[] = +{ + {{0x51,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x51,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x51,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x51,0x77,0xbb,0x7b,0x84,0x34,0x00,0x50,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x71,0x77,0xbb,0x6e,0x84,0x1e,0x00,0x5a,0x04,0x00,0x80,0x25,0x1a,0x1f,0x59,0x00}}, + {{0xd6,0x77,0xb7,0xb6,0x83,0x2c,0x02,0x5a,0x04,0x00,0x80,0x1b,0xf8,0x1f,0x82,0x00}}, + {{0xf5,0x77,0xbb,0x66,0x8c,0x21,0x02,0x5a,0x04,0x00,0x80,0x1f,0x58,0x46,0x9f,0x00}} +}; + +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_UPALN[] = +{ + {{0x41,0x7f,0xb7,0x34,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x80,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x34,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x12,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x61,0x7f,0xb7,0x99,0x84,0x35,0x04,0x5a,0x05,0x00,0x80,0x1f,0x0d,0x54,0x5e,0x00}}, + {{0xc3,0x7f,0xb7,0x7a,0x84,0x40,0x02,0x5a,0x05,0x00,0x80,0x19,0x78,0xef,0x35,0x00}}, + {{0xe5,0x7f,0xb7,0x1d,0xa7,0x3e,0x04,0x5a,0x05,0x00,0x80,0x1a,0x33,0x3f,0x2f,0x00}} +}; + +static const SiS310_CHTVRegDataStruct SiS310_CHTVReg_OPALN[] = +{ + {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x86,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x36,0xad,0x50,0x34,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x41,0x7f,0xb7,0x86,0x85,0x50,0x00,0x83,0x05,0x00,0x80,0x00,0x00,0x00,0x00,0x01}}, + {{0x61,0x7f,0xb7,0x99,0x84,0x35,0x04,0x5a,0x05,0x00,0x80,0x1f,0x0d,0x54,0x5e,0x00}}, + {{0xc1,0x7f,0xb7,0x4d,0x8c,0x1e,0x31,0x5a,0x05,0x00,0x80,0x1f,0x15,0xc0,0x1e,0x00}}, + {{0xe4,0x7f,0xb7,0x1e,0xaf,0x29,0x37,0x5a,0x05,0x00,0x80,0x1d,0xf1,0x6c,0xcb,0x00}} +}; + static const UCHAR SiS310_CHTVVCLKUNTSC[] = {0x41,0x41,0x41,0x41,0x42,0x46,0x53}; static const UCHAR SiS310_CHTVVCLKONTSC[] = {0x48,0x48,0x48,0x48,0x45,0x43,0x51}; @@ -4334,4 +4986,14 @@ static const UCHAR SiS310_CHTVVCLKUPAL[] = {0x47,0x47,0x47,0x47,0x48,0x4a,0x54}; static const UCHAR SiS310_CHTVVCLKOPAL[] = {0x47,0x47,0x47,0x47,0x48,0x4f,0x52}; -/* TW: New end */ + +static const UCHAR SiS310_CHTVVCLKSOPAL[] = {0x47,0x47,0x47,0x47,0x48,0x4f,0x52}; + +static const UCHAR SiS310_CHTVVCLKUPALM[] = {0x41,0x41,0x41,0x41,0x42,0x46,0x53}; + +static const UCHAR SiS310_CHTVVCLKOPALM[] = {0x48,0x48,0x48,0x48,0x45,0x43,0x51}; + +static const UCHAR SiS310_CHTVVCLKUPALN[] = {0x47,0x47,0x47,0x47,0x48,0x4a,0x54}; + +static const UCHAR SiS310_CHTVVCLKOPALN[] = {0x47,0x47,0x47,0x47,0x48,0x4f,0x52}; + diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/sis/init301.c linux.21rc1-ac2/drivers/video/sis/init301.c --- linux.21rc1/drivers/video/sis/init301.c 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/sis/init301.c 2003-04-22 16:44:38.000000000 +0100 @@ -1,17 +1,27 @@ /* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/sis/init301.c,v 1.3 2002/22/04 01:16:16 dawes Exp $ */ /* - * Mode switching code (CRT2 section) for SiS 300/540/630/730/315/550/650/740 + * Mode switching code (CRT2 section) for SiS 300/540/630/730/315/550/650/740/330 * (Universal module for Linux kernel framebuffer, XFree86 4.x) * * Assembler-To-C translation - * Parts Copyright 2002 by Thomas Winischhofer + * Copyright 2002, 2003 by Thomas Winischhofer + * Minor parts Copyright SiS, Inc. * * Based on BIOS - * 1.10.07, 1.10a for SiS650/LVDS+CH7019 - * 1.07.1b, 1.10.6s for SiS650/301(B/LV), 650/301LVx - * 2.04.50 (I) and 2.04.5c (II) for SiS630/301(B) + * 1.10.07, 1.10a for 650/CH7019 + * 1.11.21a for 740/CH7019 + * 1.11.05 for 650/LVDS (w/o Chrontel) + * 1.07.1b, 1.10.6s, 1.11.6w, 1.11.7w, 1.11.8r for 650/301(B/LV), 650/302LV + * 2.04.50 (I) and 2.04.5c (II) for 630/301(B) * 2.02.3b, 2.03.02, 2.04.2c, 2.04.5c, 2.07a and 2.08.b3 for 630/LVDS/LVDS+CH7005 + * 2.04.5c, 2.04.6c for 730+LVDS+CH7005 * 1.09b for 315/301(B) + * 1.16.51 for 300+301LV (ECS A907) + * 1.01.03 for 330 (Xabre 400) + * + * Known bugs: + * 1024x768 panel, expanding (CR37=1): Mode 640x480 does not work on SOME panels + * therefore, we always do the scaling ourselves for now. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -31,7 +41,19 @@ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * + * TW says: This code looks awful, I know. But please don't do anything about + * this otherwise debugging will be hell. + * The code is extremely fragile as regards the different chipsets, different + * video bridges and combinations thereof. If anything is changed, extreme + * care has to be taken that that change doesn't break it for other chipsets, + * bridges or combinations thereof. + * All comments in this file are by me, regardless if they are marked TW or not. + * */ + +#if 1 +#define NEWCH701x +#endif #include "init301.h" @@ -39,10 +61,6 @@ #define TWNEWPANEL #endif -#if 1 /* TW: Emulate 650/301LVx BIOS 1.10.6s (should be set) */ -#define SIS650301NEW -#endif - #ifdef SIS300 #include "oem300.h" #endif @@ -52,7 +70,7 @@ #endif #define SiS_I2CDELAY 1000 -#define SiS_I2CDELAYSHORT 333 +#define SiS_I2CDELAYSHORT 150 BOOLEAN SiS_SetCRT2Group301(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, @@ -63,7 +81,11 @@ SiS_Pr->SiS_SetFlag |= ProgrammingCRT2; - SiS_SearchModeID(SiS_Pr,ROMAddr,&ModeNo,&ModeIdIndex); + if(!SiS_Pr->UseCustomMode) { + SiS_SearchModeID(SiS_Pr,ROMAddr,&ModeNo,&ModeIdIndex); + } else { + ModeIdIndex = 0; + } /* TW: Used for shifting CR33 */ SiS_Pr->SiS_SelectCRT2Rate = 4; @@ -76,26 +98,43 @@ if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { SiS_DisableBridge(SiS_Pr,HwDeviceExtension,BaseAddr); + if((SiS_Pr->SiS_IF_DEF_LVDS == 1) && (HwDeviceExtension->jChipType == SIS_730)) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,0x80); + } SiS_SetCRT2ModeRegs(SiS_Pr,BaseAddr,ModeNo,ModeIdIndex,HwDeviceExtension); } if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { SiS_LockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); SiS_DisplayOn(SiS_Pr); - return(FALSE); + return(TRUE); } + if(SiS_Pr->UseCustomMode) return(FALSE); + SiS_GetCRT2Data(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); - /* LVDS, 650/301LV(LCDA) and 630/301B BIOS set up Panel Link */ - if((SiS_Pr->SiS_IF_DEF_LVDS == 1) || (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { + /* Set up Panel Link for LVDS, 301BDH and 650/30xLV(for LCDA) */ + if( (SiS_Pr->SiS_IF_DEF_LVDS == 1) || + ((SiS_Pr->SiS_VBType & VB_NoLCD) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) || + ((HwDeviceExtension->jChipType >= SIS_315H) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) ) { SiS_GetLVDSDesData(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); } else { SiS_Pr->SiS_LCDHDES = SiS_Pr->SiS_LCDVDES = 0; } +#ifdef LINUX_XF86 +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "(init301: LCDHDES 0x%03x LCDVDES 0x%03x)\n", SiS_Pr->SiS_LCDHDES, SiS_Pr->SiS_LCDVDES); + xf86DrvMsg(0, X_INFO, "(init301: HDE 0x%03x VDE 0x%03x)\n", SiS_Pr->SiS_HDE, SiS_Pr->SiS_VDE); + xf86DrvMsg(0, X_INFO, "(init301: VGAHDE 0x%03x VGAVDE 0x%03x)\n", SiS_Pr->SiS_VGAHDE, SiS_Pr->SiS_VGAVDE); + xf86DrvMsg(0, X_INFO, "(init301: HT 0x%03x VT 0x%03x)\n", SiS_Pr->SiS_HT, SiS_Pr->SiS_VT); + xf86DrvMsg(0, X_INFO, "(init301: VGAHT 0x%03x VGAVT 0x%03x)\n", SiS_Pr->SiS_VGAHT, SiS_Pr->SiS_VGAVT); +#endif +#endif + if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { SiS_SetGroup1(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, HwDeviceExtension,RefreshRateTableIndex); @@ -114,91 +153,112 @@ SiS_SetGroup5(SiS_Pr,HwDeviceExtension, BaseAddr,ROMAddr, ModeNo,ModeIdIndex); - /* TW: 630/301B BIOS does all this: */ - if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - if(!((SiS_Pr->SiS_SetFlag & CRT2IsVGA) && ((ModeNo == 0x03) || (ModeNo = 0x10)))) { - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - SiS_ModCRT1CRTC(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, - RefreshRateTableIndex,HwDeviceExtension); - } - } - } - SiS_SetCRT2ECLK(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, - RefreshRateTableIndex,HwDeviceExtension); - } + /* TW: For 301BDH (Panel link initialization): */ + if((SiS_Pr->SiS_VBType & VB_NoLCD) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(!((SiS_Pr->SiS_SetFlag & SetDOSMode) && ((ModeNo == 0x03) || (ModeNo = 0x10)))) { + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { + SiS_ModCRT1CRTC(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, + RefreshRateTableIndex,HwDeviceExtension); + } + } + } + SiS_SetCRT2ECLK(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, + RefreshRateTableIndex,HwDeviceExtension); } - } } else { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - if (SiS_Pr->SiS_IF_DEF_TRUMPION == 0) { - SiS_ModCRT1CRTC(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, - RefreshRateTableIndex,HwDeviceExtension); - } + if(SiS_Pr->SiS_IF_DEF_TRUMPION == 0) { + SiS_ModCRT1CRTC(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, + RefreshRateTableIndex,HwDeviceExtension); + } } if(SiS_Pr->SiS_IF_DEF_FSTN == 0) { - SiS_SetCRT2ECLK(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, - RefreshRateTableIndex,HwDeviceExtension); + SiS_SetCRT2ECLK(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, + RefreshRateTableIndex,HwDeviceExtension); } if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { - if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - /* TW: Inserted from 650/LVDS BIOS */ - if (SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) { - if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { +#ifdef SIS315H SiS_SetCH701xForLCD(SiS_Pr,HwDeviceExtension,BaseAddr); - } - } - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - /* TW: Set Chrontel registers only if CRT2 is TV */ - SiS_SetCHTVReg(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, +#endif + } + } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + SiS_SetCHTVReg(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, RefreshRateTableIndex); - } - } + } + } } } #ifdef SIS300 - if ( (HwDeviceExtension->jChipType==SIS_540)|| - (HwDeviceExtension->jChipType==SIS_630)|| - (HwDeviceExtension->jChipType==SIS_730)|| - (HwDeviceExtension->jChipType==SIS_300) ) + if ( (HwDeviceExtension->jChipType == SIS_540) || + (HwDeviceExtension->jChipType == SIS_630) || + (HwDeviceExtension->jChipType == SIS_730) || + (HwDeviceExtension->jChipType == SIS_300) ) { if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { - SiS_OEM300Setting(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo); + if(SiS_Pr->SiS_UseOEM) { + if((SiS_Pr->SiS_UseROM) && ROMAddr && (SiS_Pr->SiS_UseOEM == -1)) { + if((ROMAddr[0x233] == 0x12) && (ROMAddr[0x234] == 0x34)) { + SiS_OEM300Setting(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo); + } + } else { + SiS_OEM300Setting(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo); + } + } } } #endif #ifdef SIS315H - if ( (HwDeviceExtension->jChipType==SIS_315H)|| - (HwDeviceExtension->jChipType==SIS_315PRO)|| - (HwDeviceExtension->jChipType==SIS_550) || - (HwDeviceExtension->jChipType==SIS_640) || - (HwDeviceExtension->jChipType==SIS_740) || - (HwDeviceExtension->jChipType==SIS_650)) + if ( (HwDeviceExtension->jChipType == SIS_315H) || + (HwDeviceExtension->jChipType == SIS_315) || + (HwDeviceExtension->jChipType == SIS_315PRO)|| + (HwDeviceExtension->jChipType == SIS_550) || + (HwDeviceExtension->jChipType == SIS_740) || + (HwDeviceExtension->jChipType == SIS_650) || + (HwDeviceExtension->jChipType == SIS_330) ) { if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { -#ifdef SIS650301NEW SiS_FinalizeLCD(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, HwDeviceExtension); -#else +#if 0 /* Instead of FinalizeLCD(), older BIOSes (A92x) used OEMLCD() */ SiS_OEMLCD(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); #endif - SiS_OEM310Setting(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); + if(SiS_Pr->SiS_UseOEM) { + SiS_OEM310Setting(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); + } SiS_CRT2AutoThreshold(SiS_Pr,BaseAddr); } } #endif + if(HwDeviceExtension->jChipType < SIS_315H) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + if(HwDeviceExtension->jChipType != SIS_730) { + SiS_DisplayOn(SiS_Pr); + } + } + } + if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + if(HwDeviceExtension->jChipType == SIS_730) { + SiS_DisplayOn(SiS_Pr); + } + } SiS_EnableBridge(SiS_Pr,HwDeviceExtension,BaseAddr); - SiS_DisplayOn(SiS_Pr); } + SiS_DisplayOn(SiS_Pr); + if(SiS_Pr->SiS_IF_DEF_CH70xx == 1) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { /* TW: Disable LCD panel when using TV */ @@ -209,8 +269,6 @@ } } - SiS_DisplayOn(SiS_Pr); - if(SiS_LowModeStuff(SiS_Pr,ModeNo,HwDeviceExtension)) { SiS_LockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); } @@ -218,7 +276,6 @@ return 1; } -/* TW: Checked with 650/LVDS (1.10.07) and 630+301B/LVDS BIOS */ BOOLEAN SiS_LowModeStuff(SiS_Private *SiS_Pr, USHORT ModeNo, PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -234,7 +291,8 @@ temp2 = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x00); SiS_SetReg1(SiS_Pr->SiS_P3d4,0x00,temp1); SiS_SetReg1(SiS_Pr->SiS_P3d4,0x11,temp); - if(HwDeviceExtension->jChipType >= SIS_315H) { + if((HwDeviceExtension->jChipType >= SIS_315H) || + (HwDeviceExtension->jChipType == SIS_300)) { if(temp2 == 0x55) return(0); else return(1); } else { @@ -247,17 +305,15 @@ } /* TW: Set Part1 registers */ -/* TW: Checked with 650/LVDS (1.10.07), 650/301LV (II) and 630/301B (II) BIOS */ -/* TW: Pass 2: Checked with 650/301LVx 1.10.6s, 630/301B 2.04.5a */ void SiS_SetGroup1(SiS_Private *SiS_Pr,USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT RefreshRateTableIndex) { - USHORT temp=0, tempax=0, tempbx=0, tempcx=0, tempbl=0; + USHORT temp=0, tempax=0, tempbx=0, tempcx=0; USHORT pushbx=0, CRT1Index=0; #ifdef SIS315H - USHORT pushcx=0; + USHORT pushcx=0, tempbl=0; #endif USHORT modeflag, resinfo=0; @@ -269,18 +325,14 @@ modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; } - /* TW: Removed 301B301LV.. check here; LCDA exists with LVDS as well */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - /* TW: From 650/LVDS BIOS; 301(B+LV) version does not set Sync */ - if (SiS_Pr->SiS_IF_DEF_LVDS == 1) { - SiS_SetCRT2Sync(SiS_Pr,BaseAddr,ROMAddr,ModeNo, - RefreshRateTableIndex,HwDeviceExtension); - } - + SiS_SetCRT2Sync(SiS_Pr,BaseAddr,ROMAddr,ModeNo, + RefreshRateTableIndex,HwDeviceExtension); +#ifdef SIS315H SiS_SetGroup1_LCDA(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, HwDeviceExtension,RefreshRateTableIndex); - +#endif } else { if( (HwDeviceExtension->jChipType >= SIS_315H) && @@ -293,7 +345,7 @@ } else { SiS_SetCRT2Offset(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, - RefreshRateTableIndex,HwDeviceExtension); + RefreshRateTableIndex,HwDeviceExtension); if (HwDeviceExtension->jChipType < SIS_315H ) { #ifdef SIS300 @@ -312,8 +364,7 @@ if (HwDeviceExtension->jChipType < SIS_315H ) { -#ifdef SIS300 - /* ------------- 300 series --------------*/ +#ifdef SIS300 /* ------------- 300 series --------------*/ temp = (SiS_Pr->SiS_VGAHT - 1) & 0x0FF; /* BTVGA2HT 0x08,0x09 */ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x08,temp); /* TW: CRT2 Horizontal Total */ @@ -332,7 +383,7 @@ if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC){ - CRT1Index &= 0x3F; + /* CRT1Index &= 0x3F; - Not any longer */ tempbx = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[4]; tempbx |= ((SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14] & 0xC0) << 2); tempbx = (tempbx - 1) << 3; @@ -340,7 +391,9 @@ tempcx &= 0x1F; temp = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[15]; temp = (temp & 0x04) << (6-2); - tempcx = ((tempcx | temp) - 1) << 3; + tempcx = (tempcx | temp); + tempcx--; + tempcx <<= 3; } if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (resinfo == 0x08)){ @@ -353,16 +406,29 @@ temp = tempbx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0B,temp); /* TW: CRT2 Horizontal Retrace Start */ -#endif +#endif /* SIS300 */ } else { -#ifdef SIS315H - /* ---------------------- 310 series ------------------*/ +#ifdef SIS315H /* ----------------- 310/325/330 series ------------- */ tempcx = SiS_Pr->SiS_VGAHT; /* BTVGA2HT 0x08,0x09 */ pushcx = tempcx; - if(modeflag & HalfDCLK) tempcx >>= 1; + if(modeflag & HalfDCLK) { +#ifndef NEWCH701x + if((SiS_Pr->SiS_IF_DEF_LVDS == 1) && (SiS_Pr->SiS_IF_DEF_CH70xx == 0)) { +#endif + tempax = SiS_Pr->SiS_VGAHDE >> 1; + tempcx = SiS_Pr->SiS_HT - SiS_Pr->SiS_HDE + tempax; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { + tempcx = SiS_Pr->SiS_HT - tempax; + } +#ifndef NEWCH701x + } else { + tempcx >>= 1; + } +#endif + } tempcx--; temp = tempcx & 0xff; @@ -398,13 +464,17 @@ tempcx &= 0x1F; temp = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[15]; temp = (temp & 0x04) << (5-2); /* VGAHRE D[5] */ - tempcx = ((tempcx | temp) - 3) << 3; /* (VGAHRE-3)*8 */ + tempcx = (tempcx | temp); /* (VGAHRE-3)*8 */ + tempcx -= 3; + tempcx <<= 3; + tempcx &= 0x00FF; + tempcx |= (tempbx & 0xFF00); tempbx += 16; tempcx += 16; tempax = SiS_Pr->SiS_VGAHT; - if (modeflag & HalfDCLK) tempax >>= 1; + if(modeflag & HalfDCLK) tempax >>= 1; tempax--; - if (tempcx > tempax) tempcx = tempax; + if(tempcx > tempax) tempcx = tempax; } if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (resinfo == 0x08)){ if(!(SiS_Pr->SiS_VBInfo & SetPALTV)){ @@ -412,7 +482,7 @@ tempcx = 1042; } } - /* TW: Makes no sense, but is in 650/301LVx 1.10.6s */ + /* TW: Makes no sense, but is in 650/302LV 1.10.6s */ if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (resinfo == 0x08)){ if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { @@ -425,102 +495,9 @@ temp = tempbx & 0xff; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0B,temp); /* TW: CRT2 Horizontal Retrace Start */ - -#if 0 /* TW: Old code */ - if (modeflag & HalfDCLK) { /* for low resolution modes */ - - temp = ((SiS_Pr->SiS_VGAHT / 2) - 1) & 0xFF; /* BTVGA2HT 0x08,0x09 */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x08,temp); /* TW: CRT2 Horizontal Total */ - - temp = ((((SiS_Pr->SiS_VGAHT / 2) - 1) & 0xFF00) >> 8) << 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x09,0x0F,temp); /* TW: CRT2 Horizontal Total Overflow [7:4] */ - - temp = ((SiS_Pr->SiS_VGAHDE / 2) + 16) & 0xFF; /* BTVGA2HDEE 0x0A,0x0C */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0A,temp); /* TW: CRT2 Horizontal Display Enable End */ - - pushbx = (SiS_Pr->SiS_VGAHDE / 2) + 16; - tempcx = ((SiS_Pr->SiS_VGAHT - SiS_Pr->SiS_VGAHDE) / 2) >> 2; /* cx */ - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) - tempcx >>= 1; /* TW: From LVDS 1.10.07; not done on 301(LV) */ - tempbx = pushbx + tempcx; /* bx BTVGA@HRS 0x0B,0x0C */ - tempcx += tempbx; - - if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC){ - tempbx = SiS_CRT1Table[CRT1Index].CR[4]; - tempbx |= ((SiS_CRT1Table[CRT1Index].CR[14] & 0xC0) << 2); - tempbx = (tempbx - 3) << 3; /*(VGAHRS-3)*8 */ - tempcx = SiS_CRT1Table[CRT1Index].CR[5]; - tempcx &= 0x1F; - temp = SiS_CRT1Table[CRT1Index].CR[15]; - temp = (temp & 0x04) << (5-2); /* VGAHRE D[5] */ - tempcx =((tempcx | temp) - 3) << 3; /* (VGAHRE-3)*8 */ - } - /* TW: The following is not done in 650/LVDS BIOS */ - tempbx += 4; - tempcx += 4; - - if (tempcx > (SiS_Pr->SiS_VGAHT / 2)) - tempcx = SiS_Pr->SiS_VGAHT / 2; - } - - temp = tempbx & 0x00FF; - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0B,temp); /* TW: CRT2 Horizontal Retrace Start */ - - } else { /* for high resolution modes */ - - temp = (SiS_Pr->SiS_VGAHT - 1) & 0xFF; /* BTVGA2HT 0x08,0x09 */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x08,temp); /* TW: CRT2 Horizontal Total */ - - temp = (((SiS_Pr->SiS_VGAHT - 1) & 0xFF00) >> 8 ) << 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x09,0x0F,temp); /* TW: CRT2 Horizontal Total Overflow [7:4] */ - - temp = (SiS_Pr->SiS_VGAHDE + 16) & 0xFF; /* BTVGA2HDEE 0x0A,0x0C */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0A,temp); /* TW: CRT2 Horizontal Display Enable End */ - - pushbx = SiS_Pr->SiS_VGAHDE + 16; - tempcx = (SiS_Pr->SiS_VGAHT - SiS_Pr->SiS_VGAHDE) >> 2; /* cx */ - - /* TW: Done in 650/301LVx 1.10.6s */ - /* if(SiS_Pr->SiS_IF_DEF_LVDS == 1) */ - tempcx >>= 1; /* TW: From LVDS 1.10.07; not done on 301(LV), done in 301LVx 1.10.6s */ - - tempbx = pushbx + tempcx; /* bx BTVGA@HRS 0x0B,0x0C */ - tempcx += tempbx; - - if(SiS_Pr->SiS_IF_DEF_LVDS==0) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC){ - tempbx = SiS_CRT1Table[CRT1Index].CR[4]; - tempbx |= ((SiS_CRT1Table[CRT1Index].CR[14] & 0xC0) << 2); - tempbx = (tempbx - 3) << 3; /*(VGAHRS-3)*8 */ - tempcx = SiS_CRT1Table[CRT1Index].CR[5]; - tempcx &= 0x1F; - temp = SiS_CRT1Table[CRT1Index].CR[15]; - temp = (temp & 0x04) << (5-2); /* VGAHRE D[5] */ - tempcx = ((tempcx | temp) - 3) << 3; /* (VGAHRE-3)*8 */ - tempbx += 16; - tempcx += 16; - } - /* TW: The entire following section is not done in 650/LVDS BIOS */ - if (tempcx > SiS_Pr->SiS_VGAHT) - tempcx = SiS_Pr->SiS_VGAHT; - - if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (resinfo == 0x08)){ - if(!(SiS_Pr->SiS_VBInfo & SetPALTV)){ - tempbx = 1040; - tempcx = 1042; - } - } - } - - temp = tempbx & 0x00FF; - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0B,temp); /* TW: CRT2 Horizontal Retrace Start */ - - } /* halfdclk */ -#endif #endif /* SIS315H */ - } /* 310 series */ + } /* 310/325/330 series */ /* TW: The following is done for all bridge/chip types/series */ @@ -539,21 +516,18 @@ tempcx = SiS_Pr->SiS_VGAVT - 1; temp = tempcx & 0x00FF; - /* TW: Matches 650/301LV, 650/LVDS, 630/LVDS(CLEVO), 630/LVDS(no-Ch7005) */ if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { if(HwDeviceExtension->jChipType < SIS_315H) { if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - if(SiS_Pr->SiS_VBInfo & (SetCRT2ToSVIDEO|SetCRT2ToAVIDEO)) { + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToSVIDEO | SetCRT2ToAVIDEO)) { temp--; } } } else { - if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { temp--; - } } } else if(HwDeviceExtension->jChipType >= SIS_315H) { - /* TW: Inserted from 650/301LVx 1.10.6s */ + /* TW: 650/30xLV 1.10.6s */ temp--; } SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0E,temp); /* TW: CRT2 Vertical Total */ @@ -566,7 +540,7 @@ temp |= ((tempcx & 0xFF00) >> 8); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x12,temp); /* TW: Overflow (and HWCursor Test Mode) */ - /* TW: For 650/LVDS (1.10.07), 650/301LVx (1.10.6s) */ + /* TW: 650/LVDS (1.10.07), 650/30xLV (1.10.6s) */ if(HwDeviceExtension->jChipType >= SIS_315H) { tempbx++; tempax = tempbx; @@ -579,7 +553,7 @@ tempcx += tempbx; tempcx++; } else { - /* TW: For 630/LVDS/301B: */ + /* TW: 300 series, LVDS/301B: */ tempbx = (SiS_Pr->SiS_VGAVT + SiS_Pr->SiS_VGAVDE) >> 1; /* BTVGA2VRS 0x10,0x11 */ tempcx = ((SiS_Pr->SiS_VGAVT - SiS_Pr->SiS_VGAVDE) >> 4) + tempbx + 1; /* BTVGA2VRE 0x11 */ } @@ -605,22 +579,28 @@ /* 3. Panel compensation delay */ - if (HwDeviceExtension->jChipType < SIS_315H ) { + if(HwDeviceExtension->jChipType < SIS_315H) { - /* ---------- 300 series -------------- */ +#ifdef SIS300 /* ---------- 300 series -------------- */ if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { temp = 0x20; -#if 0 /* TW: Not in 630/301B BIOS */ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) temp = 0x24; -#endif - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x08; -#ifdef oldHV /* TW: Not in 630/301B BIOS */ + + if(HwDeviceExtension->jChipType == SIS_300) { + temp = 0x10; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) temp = 0x2c; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) temp = 0x20; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) temp = 0x24; + } + if(SiS_Pr->SiS_VBType & VB_SIS301) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) temp = 0x20; + } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) temp = 0x24; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x08; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) temp = 0x2c; - else temp = 0x20; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) temp = 0x2c; + else temp = 0x20; } -#endif if((ROMAddr) && (SiS_Pr->SiS_UseROM) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { if(ROMAddr[0x220] & 0x80) { if(SiS_Pr->SiS_VBInfo & (SetCRT2ToTV-SetCRT2ToHiVisionTV)) @@ -634,46 +614,82 @@ temp &= 0x3c; } } - if(HwDeviceExtension->pdc) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(HwDeviceExtension->pdc) { temp = HwDeviceExtension->pdc & 0x3c; + } } } else { temp = 0x20; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) temp = 0x04; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) temp = 0x04; + } if((ROMAddr) && SiS_Pr->SiS_UseROM) { if(ROMAddr[0x220] & 0x80) { temp = ROMAddr[0x220] & 0x3c; } } - if(HwDeviceExtension->pdc) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(HwDeviceExtension->pdc) { temp = HwDeviceExtension->pdc & 0x3c; + } } } SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,~0x03C,temp); /* TW: Panel Link Delay Compensation; (Software Command Reset; Power Saving) */ +#endif /* SIS300 */ + } else { - /* ----------- 310/325 series ---------------*/ +#ifdef SIS315H /* ----------- 310/325/330 series ---------------*/ if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { temp = 0x10; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) temp = 0x2c; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) temp = 0x20; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) temp = 0x24; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x08; - tempbl = 0xF0; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + temp = 0x08; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + switch(SiS_Pr->SiS_HiVision) { + case 2: + case 1: + case 0: + temp = 0x08; + break; + default: + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) temp = 0x2c; + else temp = 0x20; + } + } + } + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + tempbl = 0x00; + if((ROMAddr) && (SiS_Pr->SiS_UseROM)) { + if(HwDeviceExtension->jChipType < SIS_330) { + if(ROMAddr[0x13c] & 0x80) tempbl = 0xf0; + } else { + if(ROMAddr[0x1bc] & 0x80) tempbl = 0xf0; + } + } + } else { /* LV (550/301LV checks ROM byte, other LV BIOSes do not) */ + tempbl = 0xF0; + } } else { - temp = 0x00; + if(HwDeviceExtension->jChipType == SIS_740) { + temp = 0x03; + } else { + temp = 0x00; + } if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x0a; tempbl = 0xF0; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) tempbl = 0x0F; - } -#if 0 /* TW: Not done in 650/301LVx 1.10.6s */ - if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - temp >>= 2; + if(HwDeviceExtension->jChipType == SIS_650) { + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) tempbl = 0x0F; + } + } } -#endif SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,tempbl,temp); /* TW: Panel Link Delay Compensation */ tempax = 0; @@ -681,15 +697,17 @@ if (modeflag & HalfDCLK) tempax |= 0x40; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2C,0x3f,tempax); +#endif /* SIS315H */ + } } /* Slavemode */ if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - /* TW: 630/301B BIOS sets up Panel Link, too! (650/LV does not) */ - if( (HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) - && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) { + /* TW: For 301BDH, we set up the Panel Link */ + if( (SiS_Pr->SiS_VBType & VB_NoLCD) && + (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) { SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, HwDeviceExtension,RefreshRateTableIndex); @@ -703,22 +721,27 @@ } else { if(HwDeviceExtension->jChipType < SIS_315H) { - SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, + + SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, HwDeviceExtension,RefreshRateTableIndex); } else { - /* TW: For 650/LVDS */ - if((!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) || (SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { - SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, - HwDeviceExtension,RefreshRateTableIndex); - } + + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if((!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) || (SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { + SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, + HwDeviceExtension,RefreshRateTableIndex); + } + } else { + SiS_SetGroup1_LVDS(SiS_Pr,BaseAddr,ROMAddr,ModeNo,ModeIdIndex, + HwDeviceExtension,RefreshRateTableIndex); + } + } } } /* LCDA */ } -/* TW: Checked against 650/301LV and 630/301B (II) BIOS */ -/* TW: Pass 2: Checked with 650/301LVx (1.10.6s) and 630/301B (2.04.5a) */ void SiS_SetGroup1_301(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT RefreshRateTableIndex) @@ -727,7 +750,7 @@ USHORT tempax,tempbx,tempcx,temp; USHORT resinfo,modeflag; - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; } else { @@ -740,21 +763,19 @@ tempax = 0xFFFF; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) tempax = SiS_GetVGAHT2(SiS_Pr); - /* TW: 630/301B does not check this flag, assumes it is set */ - /* 650/LV and 650/301LVx BIOS do not check this either; so we set it... */ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { modeflag |= Charx8Dot; } if(modeflag & Charx8Dot) tempcx = 0x08; - else tempcx = 0x09; + else tempcx = 0x09; if(tempax >= SiS_Pr->SiS_VGAHT) tempax = SiS_Pr->SiS_VGAHT; if(modeflag & HalfDCLK) tempax >>= 1; tempax = (tempax / tempcx) - 5; - tempbx = tempax & 0xFF; + tempbx = tempax & 0x00FF; temp = 0xFF; /* set MAX HT */ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x03,temp); @@ -763,26 +784,26 @@ if(modeflag & HalfDCLK) tempax >>= 1; tempax = (tempax / tempcx) - 1; tempbx |= ((tempax & 0x00FF) << 8); - temp = tempax & 0xFF; + temp = tempax & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x04,temp); temp = (tempbx & 0xFF00) >> 8; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV){ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { temp += 2; } -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { - if(resinfo == 7) temp -= 2; - } -#endif + } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if(SiS_Pr->SiS_HiVision == 3) { + if(resinfo == 7) temp -= 2; + } } SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x05,temp); /* 0x05 Horizontal Display Start */ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x06,0x03); /* 0x06 Horizontal Blank end */ -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if((SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3)) { temp = (tempbx & 0x00FF) - 1; if(!(modeflag & HalfDCLK)) { temp -= 6; @@ -792,7 +813,6 @@ } } } else { -#endif tempcx = tempbx & 0x00FF; tempbx = (tempbx & 0xFF00) >> 8; tempcx = (tempcx + tempbx) >> 1; @@ -803,46 +823,44 @@ if((modeflag & Charx8Dot)){ temp += 4; if(SiS_Pr->SiS_VGAHDE >= 800) temp -= 6; - /* TW: Inserted from 650/301 BIOS, 630/301B/301 don't do this */ if(HwDeviceExtension->jChipType >= SIS_315H) { - if(SiS_Pr->SiS_VGAHDE == 800) temp += 2; + if(SiS_Pr->SiS_VGAHDE == 800) temp += 2; } } } } else { if(!(modeflag & HalfDCLK)) { - temp -= 4; - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { - if(SiS_Pr->SiS_VGAHDE >= 800){ - temp -= 7; - if(HwDeviceExtension->jChipType < SIS_315H) { - /* 650/301LV(x) does not do this, 630/301B does */ - if(SiS_Pr->SiS_ModeType == ModeEGA){ - if(SiS_Pr->SiS_VGAVDE == 1024){ - temp += 15; - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024) temp += 7; - } - } - } - if(SiS_Pr->SiS_VGAHDE >= 1280){ - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) temp += 28; - } + temp -= 4; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { + if(SiS_Pr->SiS_VGAHDE >= 800) { + temp -= 7; + if(HwDeviceExtension->jChipType < SIS_315H) { + /* 650/301LV(x) does not do this, 630/301B, 300/301LV do */ + if(SiS_Pr->SiS_ModeType == ModeEGA) { + if(SiS_Pr->SiS_VGAVDE == 1024) { + temp += 15; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024) + temp += 7; + } + } + } + if(SiS_Pr->SiS_VGAHDE >= 1280) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) temp += 28; + } + } } - } - } + } } } -#ifdef oldHV } -#endif SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x07,temp); /* 0x07 Horizontal Retrace Start */ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x08,0x00); /* 0x08 Horizontal Retrace End */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(SiS_Pr->SiS_SetFlag & TVSimuMode) { - if(ModeNo <= 1) { + if(ModeNo <= 0x01) { SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x07,0x2a); if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x08,0x61); @@ -882,8 +900,16 @@ } } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if(SiS_Pr->SiS_HiVision & 0x03) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x07,0xb2); + if(SiS_Pr->SiS_HiVision & 0x02) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x07,0xab); + } + } + } - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x03); /* 0x18 SR08 */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x03); /* 0x18 SR08 (FIFO Threshold?) */ SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x19,0xF0); @@ -919,7 +945,7 @@ tempbx--; temp = tempbx & 0x00FF; #if 0 - /* TW: Missing code from 630/301B 2.04.5a and 650/301LVx 1.10.6s (calles int 2f) */ + /* TW: Missing code from 630/301B 2.04.5a and 650/302LV 1.10.6s (calles int 2f) */ if(xxx()) { if(temp == 0xdf) temp = 0xda; } @@ -961,9 +987,9 @@ push1 = tempax; if(HwDeviceExtension->jChipType >= SIS_315H) { - /* TW: Inserted from 650/301LVx 1.10.6s */ + /* TW: 650/30xLV 1.10.6s */ if(ModeNo > 0x13) { - if(resinfo != 0x09) { + if(resinfo != 0x09) { /* 1280x1024 */ tempax <<= 1; tempbx += tempax; } @@ -977,11 +1003,11 @@ tempax <<= 1; tempbx += tempax; } -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3) ) { tempbx -= 10; } else { -#endif if(SiS_Pr->SiS_SetFlag & TVSimuMode) { if(SiS_Pr->SiS_VBInfo & SetPALTV) { if(!(SiS_Pr->SiS_HiVision & 0x03)) { @@ -992,9 +1018,7 @@ } } } -#ifdef oldHV } -#endif tempax = push1; tempax >>= 2; tempax++; @@ -1016,7 +1040,7 @@ if(tempbx & 0x0100) tempcx |= 0x0008; if(tempbx & 0x0200) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x0B,0x20); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x0B,0x20); } tempbx++; @@ -1041,48 +1065,50 @@ if(SiS_Pr->SiS_VGAVDE == 480) temp = 0xa3; } } - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0A,temp); /* 0x0A CR07 */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0A,temp); /* 0x0A CR07 */ temp = (tempcx & 0xFF00) >> 8; - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,temp); /* 0x17 SR0A */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,temp); /* 0x17 SR0A */ tempax = modeflag; temp = (tempax & 0xFF00) >> 8; temp = (temp >> 1) & 0x09; - /* TW: Inserted from 630/301B and 650/301(LV/LVX) BIOS; not in 630/301 BIOS */ if(!(SiS_Pr->SiS_VBType & VB_SIS301)) { + /* Only use 8 dot clock */ temp |= 0x01; } - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,temp); /* 0x16 SR01 */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,temp); /* 0x16 SR01 */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0F,0x00); /* 0x0F CR14 */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0F,0x00); /* 0x0F CR14 */ - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x12,0x00); /* 0x12 CR17 */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x12,0x00); /* 0x12 CR17 */ if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { - if(HwDeviceExtension->jChipType >= SIS_315H) { - /* TW: Inserted from 650/301LVx 1.10.6s */ + if(IS_SIS650) { + /* TW: 650/30xLV 1.10.6s */ if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { temp = 0x80; } } else temp = 0x80; } else temp = 0x00; - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1A,temp); /* 0x1A SR0E */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1A,temp); /* 0x1A SR0E */ return; } -/* TW: Checked against 650/LVDS 1.10.07, 630/301B (I,II) and 630/LVDS BIOS */ void SiS_SetGroup1_LVDS(SiS_Private *SiS_Pr,USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT RefreshRateTableIndex) { USHORT modeflag, resinfo; - USHORT push1, push2, tempax, tempbx, tempcx, temp, pushcx; + USHORT push1, push2, tempax, tempbx, tempcx, temp; +#ifdef SIS315H + USHORT pushcx; +#endif ULONG tempeax=0, tempebx, tempecx, tempvcfact=0; - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; } else { @@ -1090,23 +1116,14 @@ resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; } -#ifdef LINUX_XF86 -#ifdef TWDEBUG - xf86DrvMsg(0, X_INFO, "(init301: LCDHDES 0x%03x LCDVDES 0x%03x)\n", SiS_Pr->SiS_LCDHDES, SiS_Pr->SiS_LCDVDES); - xf86DrvMsg(0, X_INFO, "(init301: HDE 0x%03x VDE 0x%03x)\n", SiS_Pr->SiS_HDE, SiS_Pr->SiS_VDE); - xf86DrvMsg(0, X_INFO, "(init301: VGAHDE 0x%03x VGAVDE 0x%03x)\n", SiS_Pr->SiS_VGAHDE, SiS_Pr->SiS_VGAVDE); - xf86DrvMsg(0, X_INFO, "(init301: HT 0x%03x VT 0x%03x)\n", SiS_Pr->SiS_HT, SiS_Pr->SiS_VT); - xf86DrvMsg(0, X_INFO, "(init301: VGAHT 0x%03x VGAVT 0x%03x)\n", SiS_Pr->SiS_VGAHT, SiS_Pr->SiS_VGAVT); -#endif -#endif - /* TW: Set up Panel Link */ /* 1. Horizontal setup */ tempax = SiS_Pr->SiS_LCDHDES; - if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) && (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode))) { + if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) && + (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) ) { tempax -= 8; } @@ -1114,15 +1131,19 @@ tempbx = SiS_Pr->SiS_HDE; /* Horiz. Display End */ - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { - if(!SiS_Pr->SiS_IF_DEF_DSTN) { - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempbx = 800; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 1024; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempbx = 1024; /* TW: not done in BIOS */ - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempbx = 1152; /* TW: not done in BIOS */ - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempbx = 1280; /* TW */ - else if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) tempbx = 1400; /* TW */ - } + if(!(SiS_Pr->SiS_LCDInfo & LCDPass11)) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { + if((!SiS_Pr->SiS_IF_DEF_DSTN) && (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480)) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempbx = 800; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempbx = 1024; /* TW */ + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 1024; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempbx = 1152; /* TW */ + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempbx = 1280; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempbx = 1280; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempbx = 1400; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempbx = 1600; + } + } } tempcx = (tempcx - tempbx) >> 2; /* HT-HDE / 4 */ @@ -1133,29 +1154,33 @@ if(tempax >= SiS_Pr->SiS_HT) tempax -= SiS_Pr->SiS_HT; push2 = tempax; - - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if(!SiS_Pr->SiS_IF_DEF_DSTN){ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x0028; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0030; - else if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) || - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) ) { - if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - tempcx = 0x0017; + + if(!(SiS_Pr->SiS_LCDInfo & LCDPass11)) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if((!SiS_Pr->SiS_IF_DEF_DSTN) && (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480)) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x0028; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempcx = 0x0018; + else if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) ) { + if(HwDeviceExtension->jChipType < SIS_315H) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + tempcx = 0x0017; #ifdef TWNEWPANEL - tempcx = 0x0018; + tempcx = 0x0018; #endif - } else { - tempcx = 0x0017; /* A901; other 301B BIOS 0x0018; */ - } - } else { - tempcx = 0x0018; - } - } - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempcx = 0x0018; - else if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) tempcx = 0x0030; - } + } else { + tempcx = 0x0017; /* A901; sometimes 0x0018; */ + } + } else { + tempcx = 0x0018; + } + } + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 0x0028; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 0x0030; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0030; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 0x0040; + } + } } tempcx += tempax; /* lcdhrs */ @@ -1165,23 +1190,31 @@ temp = tempax & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x14,temp); /* Part1_14h; TW: Panel Link Horizontal Retrace Start */ - temp = (tempax & 0x00FF) + 10; - - /* TW: Inserted this entire "if"-section from 650/LVDS BIOS */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if(!SiS_Pr->SiS_IF_DEF_DSTN){ - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - temp += 6; - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel800x600) { - temp++; - if(HwDeviceExtension->jChipType >= SIS_315H) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1024x768) { - temp -= 3; - } - } - } + if(SiS_Pr->SiS_LCDInfo & LCDPass11) { + temp = (tempax & 0x00FF) + 2; + } else { + temp = (tempax & 0x00FF) + 10; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(!SiS_Pr->SiS_IF_DEF_DSTN) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + temp += 6; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel800x600) { + temp++; + if(HwDeviceExtension->jChipType >= SIS_315H) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1024x768) { + temp += 7; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1600x1200) { + temp -= 0x14; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x768) { + temp -= 10; + } + } + } + } + } + } } - } + } } temp &= 0x1F; @@ -1197,41 +1230,36 @@ tempcx >>= 3; /* BPLHDES */ temp = (tempcx & 0x00FF); - if(ModeNo == 0x5b) temp--; /* fix fstn mode=5b */ + if(ModeNo == 0x5b) temp--; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,temp); /* Part1_16h; TW: Panel Link Horizontal Display Enable Start */ - if(HwDeviceExtension->jChipType < SIS_315H) { /* TW: Not done in LVDS BIOS 1.10.07 */ - if(tempbx & 0x07) tempbx += 8; /* TW: Done in 630/301B and 630/LVDS BIOSes */ + if(HwDeviceExtension->jChipType < SIS_315H) { + if(tempbx & 0x07) tempbx += 8; } tempbx >>= 3; /* BPLHDEE */ temp = tempbx & 0x00FF; - if(ModeNo == 0x5b) temp--; /* fix fstn mode=5b */ + if(ModeNo == 0x5b) temp--; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,temp); /* Part1_17h; TW: Panel Link Horizontal Display Enable End */ /* 2. Vertical setup */ if(HwDeviceExtension->jChipType < SIS_315H) { - - /* TW: This entire section from 630/301B and 630/LVDS/LVDS+CH BIOS */ - tempcx = SiS_Pr->SiS_VGAVT; - tempbx = SiS_Pr->SiS_VGAVDE; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - tempbx = 600; - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel800x600) { - tempbx = 768; - if( (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1024x768) && - (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1152x768) ) { - tempbx = 600; - } - } - } - } - tempcx -= tempbx; + tempcx = SiS_Pr->SiS_VGAVT; + tempbx = SiS_Pr->SiS_VGAVDE; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempbx = 600; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempbx = 600; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempbx = 768; + else tempbx = 1024; + } + } + tempcx -= tempbx; } else { - tempcx = SiS_Pr->SiS_VGAVT - SiS_Pr->SiS_VGAVDE; /* VGAVT-VGAVDE */ + tempcx = SiS_Pr->SiS_VGAVT - SiS_Pr->SiS_VGAVDE; /* VGAVT-VGAVDE */ } @@ -1240,19 +1268,19 @@ tempax = SiS_Pr->SiS_VGAVDE; - if((SiS_Pr->SiS_IF_DEF_TRUMPION == 0) && (!(SiS_Pr->SiS_LCDInfo & LCDPass11)) - && (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480)) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if(!SiS_Pr->SiS_IF_DEF_DSTN){ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempax = 600; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempax = 768; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempax = 600; /* TW */ - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempax = 768; /* TW */ - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempax = 1024; /* TW */ - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempax = 1050; /* TW */ - else tempax = 600; - } - } + if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (!SiS_Pr->SiS_IF_DEF_DSTN)) { + if( (SiS_Pr->SiS_IF_DEF_TRUMPION == 0) && + (!(SiS_Pr->SiS_LCDInfo & LCDPass11)) && + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) ) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempax = 600; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempax = 600; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempax = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempax = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempax = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempax = 1024; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempax = 1050; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempax = 1200; + } } tempbx += tempax; @@ -1262,28 +1290,30 @@ tempcx >>= 1; - if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480)){ - if(!SiS_Pr->SiS_IF_DEF_DSTN){ - if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) || - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) ) { /* TW: @@@ TEST - not in BIOS! */ - tempcx = 0x0001; - } else if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) || - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) ) { - if(HwDeviceExtension->jChipType < SIS_315H) { + if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480)) { + if(!(SiS_Pr->SiS_LCDInfo & LCDPass11)) { + if(!SiS_Pr->SiS_IF_DEF_DSTN) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x0001; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempcx = 0x0001; + else if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768)) { + if(HwDeviceExtension->jChipType < SIS_315H) { if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { tempcx = 0x0002; #ifdef TWNEWPANEL tempcx = 0x0003; #endif } else { - tempcx = 0x0002; /* TW: A901; other 301B BIOS sets 0x0003; */ + tempcx = 0x0002; /* TW: A901; sometimes 0x0003; */ } - } else tempcx = 0x0003; - } - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 0x0003; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 0x0001; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0001; - else tempcx = 0x0057; + } else tempcx = 0x0003; + } + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 0x0003; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 0x0001; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0001; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 0x0001; + else tempcx = 0x0057; + } } } @@ -1294,19 +1324,24 @@ } if(tempbx >= SiS_Pr->SiS_VT) tempbx -= SiS_Pr->SiS_VT; + temp = tempbx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,temp); /* Part1_18h; TW: Panel Link Vertical Retrace Start */ tempcx >>= 3; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if( (HwDeviceExtension->jChipType < SIS_315H) && - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) ) tempcx = 0x0001; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0002; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x0003; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempcx = 0x0005; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempcx = 0x0005; - else if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(!(SiS_Pr->SiS_LCDInfo & LCDPass11)) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if( (HwDeviceExtension->jChipType < SIS_315H) && + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) ) tempcx = 0x0001; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x0003; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) tempcx = 0x0005; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) tempcx = 0x0005; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 0x0011; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 0x0005; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x0002; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 0x0011; + else if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { if(HwDeviceExtension->jChipType < SIS_315H) { if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { tempcx = 0x0004; @@ -1319,6 +1354,7 @@ } else { tempcx = 0x0005; } + } } } @@ -1329,9 +1365,9 @@ temp = ((tempbx & 0x0700) >> 8) << 3; /* BPLDESKEW =0 */ if(SiS_Pr->SiS_VGAVDE != SiS_Pr->SiS_VDE) temp |= 0x40; if(SiS_Pr->SiS_SetFlag & EnableLVDSDDA) temp |= 0x40; - if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { if(HwDeviceExtension->jChipType >= SIS_315H) { - if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { /* TW: Inserted from 650/LVDS 1.10.07 */ + if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { temp |= 0x80; } } else { @@ -1342,12 +1378,12 @@ } } } - } /* TW: in follwing line, 0x87 was 0x07 (modified according to 650/LVDS BIOS) */ + } SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x1A,0x87,temp); /* Part1_1Ah; TW: Panel Link Control Signal (7:3); Vertical Retrace Start (2:0) */ if (HwDeviceExtension->jChipType < SIS_315H) { - /* 300 series */ +#ifdef SIS300 /* 300 series */ tempeax = SiS_Pr->SiS_VGAVDE << 6; temp = (USHORT)(tempeax % (ULONG)SiS_Pr->SiS_VDE); @@ -1356,17 +1392,23 @@ tempebx = tempeax; /* BPLVCFACT */ if(SiS_Pr->SiS_SetFlag & EnableLVDSDDA) { - tempebx = 0x003F; + tempebx = 0x003F; } temp = (USHORT)(tempebx & 0x00FF); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1E,temp); /* Part1_1Eh; TW: Panel Link Vertical Scaling Factor */ +#endif /* SIS300 */ + } else { - /* 310/325 series */ +#ifdef SIS315H /* 310/325 series */ +#ifdef NEWCH701x + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1E,0x03); +#else SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1E,0x23); +#endif tempeax = SiS_Pr->SiS_VGAVDE << 18; temp = (USHORT)(tempeax % (ULONG)SiS_Pr->SiS_VDE); @@ -1382,19 +1424,21 @@ if(SiS_Pr->SiS_VDE == SiS_Pr->SiS_VGAVDE) temp |= 0x04; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x35,temp); /* Part1_35h; TW: Panel Link Vertical Scaling Factor */ +#endif /* SIS315H */ + } - tempbx = push2; /* p bx temppush1 BPLVDEE */ + tempbx = push2; /* BPLVDEE */ tempcx = push1; - push1 = temp; /* TW: For 630/301B and 630/LVDS */ + push1 = temp; if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { if(!SiS_Pr->SiS_IF_DEF_DSTN){ if(HwDeviceExtension->jChipType < SIS_315H) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { if(resinfo == 15) tempcx++; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { if(resinfo == 7) tempcx++; } } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) { @@ -1445,10 +1489,9 @@ } tempecx = tempeax; - if (HwDeviceExtension->jChipType >= SIS_315H) { + if(HwDeviceExtension->jChipType >= SIS_315H) { tempeax = SiS_Pr->SiS_VGAHDE; - if(modeflag & HalfDCLK) - tempeax >>= 1; + if(modeflag & HalfDCLK) tempeax >>= 1; tempeax <<= 16; tempeax = (tempeax / tempecx) - 1; } else { @@ -1460,7 +1503,7 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1F,temp); /* Part1_1Fh; TW: Panel Link DDA Operational Number in each horiz. line */ tempbx = SiS_Pr->SiS_VDE; - if (HwDeviceExtension->jChipType >= SIS_315H) { + if(HwDeviceExtension->jChipType >= SIS_315H) { tempeax = (SiS_Pr->SiS_VGAVDE << 18) / tempvcfact; tempbx = (USHORT)(tempeax & 0x0FFFF); } else { @@ -1493,8 +1536,9 @@ /* 630/301B and 630/LVDS do something for 640x480 panels here */ +#ifdef SIS315H /* TW: DSTN/FSTN initialisation - hardcoded for 320x480 panel */ - if(SiS_Pr->SiS_IF_DEF_DSTN){ + if(SiS_Pr->SiS_IF_DEF_DSTN) { SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1E,0x01); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x25,0x00); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x26,0x00); @@ -1569,6 +1613,7 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1e,0x7d); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x2e,0xe0); } +#endif /* SIS315H */ return; @@ -1583,8 +1628,8 @@ #endif -/* TW: For LVDS / 302b/lv - LCDA (this must only be called on 310/325 series!) */ -/* TW: Double-checked against 650/LVDS and 650/301 BIOS */ +#ifdef SIS315H +/* TW: For LVDS / 302B/30xLV - LCDA (this must only be called on 310/325 series!) */ void SiS_SetGroup1_LCDA(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT RefreshRateTableIndex) @@ -1593,17 +1638,27 @@ USHORT push1,push2,tempax,tempbx,tempcx,temp; ULONG tempeax=0,tempebx,tempecx,tempvcfact; - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) /* TW: From 650/LVDS BIOS */ - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xfb,0x04); /* TW: From 650/LVDS BIOS */ - - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) /* TW: From 650/LVDS 1.10.07 */ - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2D,0x00); /* TW: From 650/LVDS 1.10.07 */ - else - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2D,0x0f); /* TW: From 650/301Lvx 1.10.6s */ - - if(ModeNo<=0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; + if(IS_SIS330) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2D,0x10); /* Xabre 1.01.03 */ + } else if(IS_SIS740) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* 740/LVDS */ + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xfb,0x04); /* 740/LVDS */ + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2D,0x03); + } else { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2D,0x10); /* 740/301LV 1.10.1i */ + } + } else { + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* 650/LVDS */ + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xfb,0x04); /* 650/LVDS */ + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2D,0x00); /* 650/LVDS 1.10.07 */ + } else { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2D,0x0f); /* 650/30xLv 1.10.6s */ + } + } + + if(ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; @@ -1613,12 +1668,11 @@ tempbx = SiS_Pr->SiS_HDE; tempcx = SiS_Pr->SiS_HT; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 1024; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempbx = 1400; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempbx = 1600; else tempbx = 1280; - } tempcx -= tempbx; /* HT-HDE */ push1 = tempax; @@ -1630,14 +1684,16 @@ tempcx >>= 2; - /* TW: Inserted from 650/301LVx 1.10.6s */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x28; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x30; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempcx = 0x18; - else tempcx = 0x30; - } + /* TW: 650/30xLV 1.10.6s, 740/LVDS */ + if( ((SiS_Pr->SiS_IF_DEF_LVDS == 0) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) || + ((SiS_Pr->SiS_IF_DEF_LVDS == 1) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) ) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 0x28; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempcx = 0x18; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 0x30; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 0x40; + else tempcx = 0x30; + } } tempcx += tempax; /* lcdhrs */ @@ -1649,6 +1705,22 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x14,temp); /* Part1_14h */ temp += 10; + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + temp += 6; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel800x600) { + temp++; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1024x768) { + temp += 7; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1600x1200) { + temp -= 10; + } + } + } + } + } + } temp &= 0x1F; temp |= ((tempcx & 0x07) << 5); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x15,temp); /* Part1_15h */ @@ -1663,7 +1735,9 @@ temp = tempcx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,temp); /* Part1_16h */ - if(tempbx & 0x07) tempbx += 8; + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + if(tempbx & 0x07) tempbx += 8; + } tempbx >>= 3; /* BPLHDEE */ temp = tempbx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,temp); /* Part1_17h */ @@ -1672,57 +1746,67 @@ tempbx = SiS_Pr->SiS_VGAVDE; tempcx -= tempbx; /* GAVT-VGAVDE */ tempbx = SiS_Pr->SiS_LCDVDES; /* VGAVDES */ - push1 = tempbx; /* push bx temppush1 */ - if(SiS_Pr->SiS_IF_DEF_TRUMPION == 0){ + push1 = tempbx; + if(SiS_Pr->SiS_IF_DEF_TRUMPION == 0) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempax = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempax = 768; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempax = 1024; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempax = 1050; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempax = 1200; else tempax = 960; -#if 0 /* TW: Removed (650/LVDS BIOS) */ - if(SiS_Pr->SiS_IF_DEF_CH70xx == 1) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - tempax = SiS_Pr->SiS_VGAVDE; - } - } -#endif } else tempax = SiS_Pr->SiS_VGAVDE; /* Trumpion */ tempbx += tempax; tempax = SiS_Pr->SiS_VT; /* VT */ if(tempbx >= tempax) tempbx -= tempax; - push2 = tempbx; /* push bx temppush2 */ - tempcx >>= 2; /* TO CHECK - was 1 */ - - /* TW: Inserted from 650/301LVx 1.10.6s */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 1; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempcx = 3; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 3; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 1; - else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 1; - else tempcx = 0x0057; - } + push2 = tempbx; + + tempcx >>= 2; + + /* TW: 650/30xLV 1.10.6s, 740/LVDS */ + if( ((SiS_Pr->SiS_IF_DEF_LVDS == 0) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) || + ((SiS_Pr->SiS_IF_DEF_LVDS == 1) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) ) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 1; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempcx = 3; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 3; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 1; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 1; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 1; + else tempcx = 0x0057; + } } tempbx += tempcx; - tempbx++; /* BPLVRS */ + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + tempbx++; /* BPLVRS */ + } if(tempbx >= tempax) tempbx -= tempax; temp = tempbx & 0x00FF; - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,temp); /* Part1_18h */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,temp); /* Part1_18h */ tempcx >>= 3; + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) tempcx = 3; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempcx = 5; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempcx = 5; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempcx = 5; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempcx = 2; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempcx = 2; + } + } + } tempcx += tempbx; tempcx++; /* BPLVRE */ temp = tempcx & 0x00FF; temp &= 0x0F; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - /* TW: Inserted from 650/LVDS BIOS */ - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0xf0,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0xF0,temp); } else { - /* TW: Inserted from 650/301LVx 1.10.6s */ + /* TW: 650/30xLV 1.10.6s, Xabre */ temp |= 0xC0; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0xF0,temp); /* Part1_19h */ } @@ -1734,31 +1818,34 @@ if(tempbx != SiS_Pr->SiS_VDE) temp |= 0x40; if(SiS_Pr->SiS_SetFlag & EnableLVDSDDA) temp |= 0x40; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - /* TW: Inserted from 650/LVDS 1.10.07 */ - if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) temp |= 0x80; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x1A,0x87,temp); /* Part1_1Ah */ + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { + if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) temp |= 0x80; + } } else { - /* TW: Inserted from 650/301LVx 1.10.6s */ - if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { - if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) temp |= 0x80; - } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x1A,0x87,temp); /* Part1_1Ah */ + if(IS_SIS650) { + /* TW: 650/30xLV 1.10.6s */ + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { + if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) temp |= 0x80; + } + } else { + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) temp |= 0x80; + } } + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x1A,0x87,temp); /* Part1_1Ah */ - tempbx = push2; /* p bx temppush2 BPLVDEE */ - tempcx = push1; /* pop cx temppush1 NPLVDES */ + tempbx = push2; /* BPLVDEE */ + tempcx = push1; /* NPLVDES */ push1 = (USHORT)(tempeax & 0xFFFF); if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) { if(resinfo == 7) tempcx++; } - /* TW: Inserted from 650/301LVx+LVDS BIOSes */ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { - tempbx = SiS_Pr->SiS_VGAVDE; - tempcx = tempbx; - tempbx--; - } + } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { + tempbx = SiS_Pr->SiS_VGAVDE; + tempcx = tempbx; + tempbx--; } temp = (tempbx & 0xFF00) >> 8; @@ -1780,7 +1867,7 @@ tempeax <<= 18; temp = (USHORT)(tempeax % tempebx); tempeax = tempeax / tempebx; - if(temp != 0) tempeax++; + if(temp) tempeax++; tempebx = tempeax; /* BPLVCFACT */ tempvcfact = tempeax; temp = (USHORT)(tempebx & 0x00FF); @@ -1794,6 +1881,7 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x35,temp); tempecx = SiS_Pr->SiS_VGAHDE; + if(modeflag & HalfDCLK) tempecx >>= 1; tempebx = SiS_Pr->SiS_HDE; tempeax = tempecx; tempeax <<= 16; @@ -1803,6 +1891,7 @@ if(tempebx == tempecx) tempeax = 0xFFFF; tempecx = tempeax; tempeax = SiS_Pr->SiS_VGAHDE; + if(modeflag & HalfDCLK) tempeax >>= 1; tempeax <<= 16; tempeax = tempeax / tempecx; tempecx <<= 16; @@ -1836,21 +1925,21 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x23,temp); #if 0 - /* TW: Missing code (calles int 2f) (650/301LVx 1.10.6s) */ + /* TW: Missing code (calles int 2f) (650/302LV 1.10.6s; 1.10.7w doesn't do this) */ if(xxx()) { SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x0e,0xda); } #endif - /* TW: Only for 650/LVDS and 30xLV/30xLVX */ - if((SiS_Pr->SiS_IF_DEF_LVDS == 1) || (SiS_Pr->SiS_VBInfo & (VB_SIS30xLV|VB_SIS30xNEW))){ + /* TW: Only for LVDS and 301LV/302LV */ + if((SiS_Pr->SiS_IF_DEF_LVDS == 1) || (SiS_Pr->SiS_VBInfo & VB_SIS301LV302LV)){ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1e,0x20); } return; } +#endif /* SIS 315 */ -/* TW: Double-checked against 650/LVDS (1.10.07) and 650/301 BIOS */ void SiS_SetCRT2Offset(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex ,USHORT RefreshRateTableIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -1862,6 +1951,10 @@ offset = SiS_GetOffset(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); +#if 0 + if(SiS_Pr->LCDResInfo == 13) offset >>= 1; + if(SiS_Pr->LCDResInfo == 12) offset >>= 1; +#endif temp = (UCHAR)(offset & 0xFF); SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x07,temp); temp = (UCHAR)((offset & 0xFF00) >> 8); @@ -1870,54 +1963,41 @@ SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x03,temp); } -/* TW: Checked with 650/LVDS and 650/301 BIOS */ USHORT SiS_GetOffset(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT temp,colordepth; USHORT modeinfo,index,infoflag; -#if 0 - USHORT mode960low, mode960high; - USHORT ColorDepth[] = { 0x01, 0x02, 0x04 }; -#endif - modeinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeInfo; - infoflag = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_InfoFlag; - if (HwDeviceExtension->jChipType < SIS_315H ) { - index = (modeinfo >> 4) & 0xFF; -#if 0 /* TW: Modes 1280x960 changed number, so this is redundant */ - mode960low = 0x7c; - mode960high = 0x7e; -#endif + if(SiS_Pr->UseCustomMode) { + infoflag = SiS_Pr->CInfoFlag; + temp = SiS_Pr->CHDisplay / 16; } else { + infoflag = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_InfoFlag; + modeinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeInfo; + + if(HwDeviceExtension->jChipType < SIS_315H ) { + index = (modeinfo >> 4) & 0xFF; + } else { index = (modeinfo >> 8) & 0xFF; -#if 0 /* TW: In 650 BIOS (LVDS AND 301), 1280x960 modes are 7b-7d! */ - mode960low = 0x7c; /* TW: This is a bug in both BIOS versions ! */ - mode960high = 0x7e; /* TW: Corrected here in LVDS BIOS 1.10.07, but not in tables! */ -#endif - } + } -#if 0 - /* TW: Not doing this strange stuff makes 1280x960 at least work on CRT1 */ - if((ModeNo >= mode960low) && (ModeNo <= mode960high)) { - temp = ModeNo - mode960low; - colordepth = ColorDepth[temp]; - temp = 0x6b; /* TW: Why the heck? */ - } else { -#endif - temp = SiS_Pr->SiS_ScreenOffset[index]; - colordepth = SiS_GetColorDepth(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex); -#if 0 + temp = SiS_Pr->SiS_ScreenOffset[index]; } -#endif + + colordepth = SiS_GetColorDepth(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex); if(infoflag & InterlaceMode) temp <<= 1; temp *= colordepth; - /* TW: For 1400x1050 */ - if((ModeNo >= 0x26) && (ModeNo <= 0x28)) { + /* TW: For 1400x1050 and 856x480 */ + if( ( ((ModeNo >= 0x26) && (ModeNo <= 0x28)) || + ModeNo == 0x3f || + ModeNo == 0x42 || + ModeNo == 0x45 ) || + (SiS_Pr->UseCustomMode && (SiS_Pr->CHDisplay % 16)) ) { colordepth >>= 1; temp += colordepth; } @@ -1925,7 +2005,6 @@ return(temp); } -/* TW: Checked with 650/LVDS BIOS */ USHORT SiS_GetColorDepth(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { @@ -1933,17 +2012,20 @@ SHORT index; USHORT modeflag; - if(ModeNo <= 0x13) + if(SiS_Pr->UseCustomMode) { + modeflag = SiS_Pr->CModeFlag; + } else { + if(ModeNo <= 0x13) modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - else + else modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + } index = (modeflag & ModeInfoFlag) - ModeEGA; if(index < 0) index = 0; return(ColorDepth[index]); } -/* TW: Checked against 630/301/301B/LVDS, 650/301LVx/LVDS */ void SiS_SetCRT2Sync(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -1957,33 +2039,33 @@ if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* LVDS */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - tempah = SiS_Pr->SiS_LCDInfo; - if(HwDeviceExtension->jChipType >= SIS_315H) { - tempbl = tempah & 0xc0; - } - if(SiS_Pr->SiS_LCDInfo & LCDSync) { - flag = 1; - } - } - if(flag != 1) tempah = infoflag >> 8; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + tempah = 0; + } else if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (SiS_Pr->SiS_LCDInfo & LCDSync)) { + tempah = SiS_Pr->SiS_LCDInfo; + } else tempah = infoflag >> 8; + tempah &= 0xC0; + tempah |= 0x20; if(!(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit)) tempah |= 0x10; - if (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { - /* TW: BIOS does something here @@@ */ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + if(HwDeviceExtension->jChipType >= SIS_315H) { + tempah >>= 3; + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xE7,tempah); + } + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0x0F,tempah); } - tempah &= 0x3f; - tempah |= tempbl; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0x0F,tempah); - } else { if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 630 - 301B */ +#ifdef SIS300 /* ---- 300 series --- */ + + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 630 - 301B(-DH) */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { tempah = SiS_Pr->SiS_LCDInfo; @@ -1993,8 +2075,8 @@ } if(flag != 1) tempah = infoflag >> 8; tempah &= 0xC0; + tempah |= 0x20; - if(!(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit)) tempah |= 0x10; if (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { @@ -2009,7 +2091,7 @@ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { tempah = SiS_Pr->SiS_LCDInfo; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpandingShift) { /* ! */ + if(SiS_Pr->SiS_LCDInfo & DontExpandLCDShift) { /* ! */ flag = 1; } } @@ -2020,9 +2102,13 @@ } +#endif /* SIS300 */ + } else { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 310/325 - 301B et al */ +#ifdef SIS315H /* ----- 310/325 series ---- */ + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { /* 310/325 - 30xLV */ tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x37); tempah &= 0xC0; @@ -2030,38 +2116,47 @@ if(!(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit)) tempah |= 0x10; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0x0F,tempah); - } else { /* 310/325 - 301 */ + } else { /* 310/325 - 301, 301B */ tempah = infoflag >> 8; tempah &= 0xC0; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_LCDInfo & LCDSync) { + tempah = SiS_Pr->SiS_LCDInfo; + tempah &= 0xC0; + } + } + tempah |= 0x20; - if(!(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit)) tempah |= 0x10; - - if (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { +#if 0 + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { /* TW: BIOS does something here @@@ */ } - - tempah &= 0x3f; - tempah |= tempbl; +#endif SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x19,0x0F,tempah); - } + } + +#endif /* SIS315H */ } } } -/* TW: Set FIFO on 630/730 - not to be called on SiS300 */ -/* TW: Checked against 630/301B BIOS; does not set PCI registers */ +/* TW: Set CRT2 FIFO on 300/630/730 */ +#ifdef SIS300 void SiS_SetCRT2FIFO_300(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo, PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT temp,index; USHORT modeidindex,refreshratetableindex; - USHORT VCLK,MCLK,colorth=0,data2; + USHORT VCLK=0,MCLK,colorth=0,data2=0; + USHORT tempal, tempah, tempbx, tempcl, tempax; + USHORT CRT1ModeNo,CRT2ModeNo; + USHORT SelectRate_backup; ULONG data,eax; - UCHAR LatencyFactor[] = { + const UCHAR LatencyFactor[] = { 97, 88, 86, 79, 77, 00, /*; 64 bit BQ=2 */ 00, 87, 85, 78, 76, 54, /*; 64 bit BQ=1 */ 97, 88, 86, 79, 77, 00, /*; 128 bit BQ=2 */ @@ -2069,81 +2164,247 @@ 80, 72, 69, 63, 61, 00, /*; 64 bit BQ=2 */ 00, 70, 68, 61, 59, 37, /*; 64 bit BQ=1 */ 86, 77, 75, 68, 66, 00, /*; 128 bit BQ=2 */ - 00, 68, 66, 59, 57, 37}; /*; 128 bit BQ=1 */ + 00, 68, 66, 59, 57, 37 /*; 128 bit BQ=1 */ + }; + const UCHAR LatencyFactor730[] = { + 69, 63, 61, + 86, 79, 77, + 103, 96, 94, + 120,113,111, + 137,130,128, /* <-- last entry, data below */ + 137,130,128, /* to avoid using illegal values */ + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + 137,130,128, + }; + const UCHAR ThLowB[] = { + 81, 4, 72, 6, 88, 8,120,12, + 55, 4, 54, 6, 66, 8, 90,12, + 42, 4, 45, 6, 55, 8, 75,12 + }; + const UCHAR ThTiming[] = { + 1, 2, 2, 3, 0, 1, 1, 2 + }; + + SelectRate_backup = SiS_Pr->SiS_SelectCRT2Rate; - SiS_SearchModeID(SiS_Pr,ROMAddr,&ModeNo,&modeidindex); - SiS_Pr->SiS_SetFlag &= (~ProgrammingCRT2); - SiS_Pr->SiS_SelectCRT2Rate = 0; - refreshratetableindex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,ModeNo,modeidindex,HwDeviceExtension); - - if(ModeNo >= 0x13) { - index = SiS_Pr->SiS_RefIndex[refreshratetableindex].Ext_CRTVCLK; - index &= 0x3F; - VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK; - index = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x1A); + if(!SiS_Pr->CRT1UsesCustomMode) { + + CRT1ModeNo = SiS_Pr->SiS_CRT1Mode; /* get CRT1 ModeNo */ + SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT1ModeNo,&modeidindex); + SiS_Pr->SiS_SetFlag &= (~ProgrammingCRT2); + SiS_Pr->SiS_SelectCRT2Rate = 0; + refreshratetableindex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT1ModeNo, + modeidindex,HwDeviceExtension); + + if(CRT1ModeNo >= 0x13) { + index = SiS_Pr->SiS_RefIndex[refreshratetableindex].Ext_CRTVCLK; + index &= 0x3F; + VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get VCLK */ + data2 = SiS_Pr->SiS_ModeType - 2; + } + + } else { + + CRT1ModeNo = 0xfe; + VCLK = SiS_Pr->CSRClock; /* Get VCLK */ + data2 = (SiS_Pr->CModeFlag & ModeInfoFlag) - 2; + + } + + if(CRT1ModeNo >= 0x13) { + if(HwDeviceExtension->jChipType == SIS_300) { + index = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x3A); + } else { + index = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x1A); + } index &= 0x07; - MCLK = SiS_Pr->SiS_MCLKData_0[index].CLOCK; - data2 = SiS_Pr->SiS_ModeType - 0x02; - switch (data2) { + MCLK = SiS_Pr->SiS_MCLKData_0[index].CLOCK; /* Get MCLK */ + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: CRT1Mode 0x%x VCLK %d MCLK %d modetype-2 = %d\n", + CRT1ModeNo, VCLK, MCLK, data2); +#endif + + switch(data2) { /* Get color depth */ case 0 : colorth = 1; break; case 1 : colorth = 1; break; case 2 : colorth = 2; break; case 3 : colorth = 2; break; case 4 : colorth = 3; break; case 5 : colorth = 4; break; + default: colorth = 2; break; } data2 = (colorth * VCLK) / MCLK; temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); - temp = ((temp&0x00FF)>>6)<<1; - if(temp == 0) temp=1; + temp = ((temp & 0x00FF) >> 6) << 1; + if(temp == 0) temp = 1; temp <<= 2; + temp &= 0xff; data2 = temp - data2; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: data2 (step1) = %d\n", + data2); +#endif - if((28*16) % data2) { + if((28 * 16) % data2) { data2 = (28 * 16) / data2; data2++; } else { data2 = (28 * 16) / data2; } + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: data2 (step2) = %d\n", + data2); +#endif - index = 0; - temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); - if(temp & 0x0080) index += 12; + if(HwDeviceExtension->jChipType == SIS_300) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x18); + tempah &= 0x62; + tempah >>= 1; + tempal = tempah; + tempah >>= 3; + tempal |= tempah; + tempal &= 0x07; + tempcl = ThTiming[tempal]; + tempbx = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x16); + tempbx >>= 6; + tempah = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); + tempah >>= 4; + tempah &= 0x0c; + tempbx |= tempah; + tempbx <<= 1; + tempal = ThLowB[tempbx + 1]; + tempal *= tempcl; + tempal += ThLowB[tempbx]; + data = tempal; + + } else if(HwDeviceExtension->jChipType == SIS_730) { + #ifndef LINUX_XF86 - SiS_SetReg4(0xcf8,0x800000A0); - eax=SiS_GetReg3(0xcfc); + SiS_SetReg4(0xcf8,0x80000050); + eax = SiS_GetReg3(0xcfc); #else - /* TW: We use pci functions X offers. We use tag 0, because - * we want to read/write to the host bridge (which is always - * 00:00.0 on 630, 730 and 540), not the VGA device. - */ - eax = pciReadLong(0x00000000, 0xA0); + eax = pciReadLong(0x00000000, 0x50); #endif - temp=(USHORT)(eax>>24); - if(!(temp&0x01)) index += 24; + tempal = (USHORT)(eax >> 8); + tempal &= 0x06; + tempal <<= 5; #ifndef LINUX_XF86 - SiS_SetReg4(0xcf8,0x80000050); - eax=SiS_GetReg3(0xcfc); + SiS_SetReg4(0xcf8,0x800000A0); + eax = SiS_GetReg3(0xcfc); #else - eax = pciReadLong(0x00000000, 0x50); + eax = pciReadLong(0x00000000, 0xA0); #endif - temp=(USHORT)(eax >> 24); - if(temp & 0x01) index += 6; + temp = (USHORT)(eax >> 28); + temp &= 0x0F; + tempal |= temp; - temp = (temp & 0x0F) >> 1; - index += temp; - data = LatencyFactor[index]; - data += 15; - temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); - if(!(temp & 0x80)) data += 5; +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: Latencyfactorindex = 0x%x\n", tempal); +#endif + + tempbx = tempal; /* BIOS BUG (2.04.5d, 2.04.6a use ah here, which is unset!) */ + tempbx = 0; /* -- do it like the BIOS anyway... */ + tempax = tempbx; + tempbx &= 0xc0; + tempbx >>= 6; + tempax &= 0x0f; + tempax *= 3; + tempbx += tempax; + + data = LatencyFactor730[tempbx]; + data += 15; + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); + if(!(temp & 0x80)) data += 5; + + } else { + + index = 0; + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); + if(temp & 0x0080) index += 12; + +#ifndef LINUX_XF86 + SiS_SetReg4(0xcf8,0x800000A0); + eax = SiS_GetReg3(0xcfc); +#else + /* TW: We use pci functions X offers. We use tag 0, because + * we want to read/write to the host bridge (which is always + * 00:00.0 on 630, 730 and 540), not the VGA device. + */ + eax = pciReadLong(0x00000000, 0xA0); +#endif + temp = (USHORT)(eax >> 24); + if(!(temp&0x01)) index += 24; + +#ifndef LINUX_XF86 + SiS_SetReg4(0xcf8,0x80000050); + eax = SiS_GetReg3(0xcfc); +#else + eax = pciReadLong(0x00000000, 0x50); +#endif + temp=(USHORT)(eax >> 24); + if(temp & 0x01) index += 6; + + temp = (temp & 0x0F) >> 1; + index += temp; + + data = LatencyFactor[index]; + data += 15; + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x14); + if(!(temp & 0x80)) data += 5; + } + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: latencyfactor (CRT1) = %d\n", data); +#endif - data += data2; + data += data2; /* CRT1 Request Period */ + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: CRT1 request period = %d\n", data); +#endif + CRT2ModeNo = ModeNo; SiS_Pr->SiS_SetFlag |= ProgrammingCRT2; + SiS_Pr->SiS_SelectCRT2Rate = SelectRate_backup; + SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT2ModeNo,&modeidindex); + + refreshratetableindex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT2ModeNo, + modeidindex,HwDeviceExtension); + + index = SiS_GetVCLK2Ptr(SiS_Pr,ROMAddr,CRT2ModeNo,modeidindex, + refreshratetableindex,HwDeviceExtension); + VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get VCLK */ + + data2 = SiS_Pr->SiS_ModeType - 2; + switch(data2) { /* Get color depth */ + case 0 : colorth = 1; break; + case 1 : colorth = 1; break; + case 2 : colorth = 2; break; + case 3 : colorth = 2; break; + case 4 : colorth = 3; break; + case 5 : colorth = 4; break; + default: colorth = 2; break; + } + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: CRT2Mode 0x%x VCLK %d MCLK %d modetype-2 = %d, colorth %d\n", + CRT2ModeNo, VCLK, MCLK, data2, colorth); +#endif data = data * VCLK * colorth; if(data % (MCLK << 4)) { @@ -2152,32 +2413,50 @@ } else { data = data / (MCLK << 4); } + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "FIFO2: data (unclipped) = 0x%x\n", data); +#endif + + if(data <= 6) data = 6; + if(data > 0x14) data = 0x14; - /* TW: Inserted this entire section */ temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x01); - if( ( (HwDeviceExtension->jChipType == SIS_630) || - (HwDeviceExtension->jChipType == SIS_730) ) && - (HwDeviceExtension->jChipRevision >= 0x30) ) /* 630s or 730(s?) */ - { - temp = (temp & (~0x1F)) | 0x1b; + if(HwDeviceExtension->jChipType == SIS_300) { + if(data <= 0x0f) temp = (temp & (~0x1F)) | 0x13; + else temp = (temp & (~0x1F)) | 0x16; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { + temp = (temp & (~0x1F)) | 0x13; + } } else { - temp = (temp & (~0x1F)) | 0x16; + if( ( (HwDeviceExtension->jChipType == SIS_630) || + (HwDeviceExtension->jChipType == SIS_730) ) && + (HwDeviceExtension->jChipRevision >= 0x30) ) /* 630s or 730(s?) */ + { + temp = (temp & (~0x1F)) | 0x1b; + } else { + temp = (temp & (~0x1F)) | 0x16; + } } SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0xe0,temp); - if(data <= 6) data = 6; - if(data > 0x14) data = 0x14; if( (HwDeviceExtension->jChipType == SIS_630) && (HwDeviceExtension->jChipRevision >= 0x30) ) /* 630s, NOT 730 */ { if(data > 0x13) data = 0x13; } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x02,~0x01F,data); - /* TW end */ + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x02,0xe0,data); + + } else { /* If mode <= 0x13, we just restore everything */ + + SiS_Pr->SiS_SetFlag |= ProgrammingCRT2; + SiS_Pr->SiS_SelectCRT2Rate = SelectRate_backup; + } } +#endif -/* TW: Set FIFO on 310 series */ +/* TW: Set FIFO on 310/325/330 series */ #ifdef SIS315H void SiS_SetCRT2FIFO_310(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo, @@ -2195,31 +2474,50 @@ USHORT ModeIdIndex; USHORT RefreshRateTableIndex; USHORT SelectRate_backup; - + + SelectRate_backup = SiS_Pr->SiS_SelectCRT2Rate; + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x01,0x3B); - CRT1ModeNo = SiS_Pr->SiS_CRT1Mode; /* get CRT1 ModeNo */ - SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT1ModeNo,&ModeIdIndex); + if(!SiS_Pr->CRT1UsesCustomMode) { + + CRT1ModeNo = SiS_Pr->SiS_CRT1Mode; /* get CRT1 ModeNo */ + SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT1ModeNo,&ModeIdIndex); - SiS_Pr->SiS_SetFlag &= (~ProgrammingCRT2); - SelectRate_backup = SiS_Pr->SiS_SelectCRT2Rate; - SiS_Pr->SiS_SelectCRT2Rate = 0; + SiS_Pr->SiS_SetFlag &= (~ProgrammingCRT2); + SiS_Pr->SiS_SelectCRT2Rate = 0; - /* Set REFIndex for crt1 refreshrate */ - RefreshRateTableIndex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT1ModeNo, + /* Get REFIndex for crt1 refreshrate */ + RefreshRateTableIndex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT1ModeNo, ModeIdIndex,HwDeviceExtension); - index = SiS_GetVCLK2Ptr(SiS_Pr,ROMAddr,CRT1ModeNo,ModeIdIndex, + index = SiS_GetVCLK2Ptr(SiS_Pr,ROMAddr,CRT1ModeNo,ModeIdIndex, RefreshRateTableIndex,HwDeviceExtension); - tempax = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get DCLK (VCLK?) */ - - tempbx = SiS_GetColorDepth(SiS_Pr,ROMAddr,CRT1ModeNo,ModeIdIndex); /* Get colordepth */ - tempbx >>= 1; - if(!tempbx) tempbx++; - + tempax = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get VCLK */ + + tempbx = SiS_GetColorDepth(SiS_Pr,ROMAddr,CRT1ModeNo,ModeIdIndex); /* Get colordepth */ + tempbx >>= 1; + if(!tempbx) tempbx++; + + } else { + + tempax = SiS_Pr->CSRClock; /* Get VCLK */ + tempbx = (SiS_Pr->CModeFlag & ModeInfoFlag) - 2; + switch(tempbx) { /* Get color depth */ + case 0 : tempbx = 1; break; + case 1 : tempbx = 1; break; + case 2 : tempbx = 2; break; + case 3 : tempbx = 2; break; + case 4 : tempbx = 3; break; + case 5 : tempbx = 4; break; + default: tempbx = 2; break; + } + + } + tempax *= tempbx; - tempbx = SiS_GetMCLK(SiS_Pr,ROMAddr, HwDeviceExtension); /* Get MCLK */ + tempbx = SiS_GetMCLK(SiS_Pr,ROMAddr, HwDeviceExtension); /* Get MCLK */ tempax /= tempbx; @@ -2258,20 +2556,24 @@ tempcx += temp3; /* CRT1 Request Period */ - CRT2ModeNo = ModeNo; /* get CRT2 ModeNo */ - SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT2ModeNo,&ModeIdIndex); /* Get ModeID Table */ + CRT2ModeNo = ModeNo; /* get CRT2 ModeNo */ + SiS_SearchModeID(SiS_Pr,ROMAddr,&CRT2ModeNo,&ModeIdIndex); /* Get ModeID Table */ SiS_Pr->SiS_SetFlag |= ProgrammingCRT2; SiS_Pr->SiS_SelectCRT2Rate = SelectRate_backup; - RefreshRateTableIndex=SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT1ModeNo, + RefreshRateTableIndex = SiS_GetRatePtrCRT2(SiS_Pr,ROMAddr,CRT2ModeNo, ModeIdIndex,HwDeviceExtension); index = SiS_GetVCLK2Ptr(SiS_Pr,ROMAddr,CRT2ModeNo,ModeIdIndex, RefreshRateTableIndex,HwDeviceExtension); - tempax = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get VCLK */ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { + tempax = SiS_Pr->SiS_VCLKData[index].CLOCK; /* Get VCLK */ + } else { + tempax = SiS_Pr->SiS_VBVCLKData[index].CLOCK; /* Get VCLK */ + } - tempbx = SiS_GetColorDepth(SiS_Pr,ROMAddr,CRT2ModeNo,ModeIdIndex); /* Get colordepth */ + tempbx = SiS_GetColorDepth(SiS_Pr,ROMAddr,CRT2ModeNo,ModeIdIndex); /* Get colordepth */ tempbx >>= 1; if(!tempbx) tempbx++; @@ -2288,11 +2590,11 @@ if (tempax > 0x37) tempax = 0x37; - /* TW: 650/LVDS (1.10.07, 1.10.00), 650/301LV overrule calculated value; 315 does not */ - if(HwDeviceExtension->jChipType == SIS_650) { + /* TW: 650/LVDS (1.10.07, 1.10.00), 650/301LV, 740, 330 overrule calculated value; 315 does not */ + if(HwDeviceExtension->jChipType >= SIS_650) { tempax = 0x04; } - + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x02,~0x3F,tempax); } @@ -2323,8 +2625,10 @@ if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) ) { +#ifdef SIS315H SiS_GetLVDSDesPtrA(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &PanelIndex,&ResIndex); + switch (PanelIndex) { case 0: PanelDesPtr = SiS_Pr->LVDS1024x768Des_1; break; /* --- expanding --- */ @@ -2335,7 +2639,9 @@ case 5: PanelDesPtr = SiS_Pr->LVDS1280x1024Des_2; break; case 6: PanelDesPtr = SiS_Pr->LVDS1400x1050Des_2; break; case 7: PanelDesPtr = SiS_Pr->LVDS1600x1200Des_2; break; + default: PanelDesPtr = SiS_Pr->LVDS1024x768Des_1; break; } +#endif } else { @@ -2344,7 +2650,7 @@ switch (PanelIndex) { - case 0: PanelDesPtr = SiS_Pr->SiS_PanelType00_1; break; /* --- expanding --- | Gericom 1st supersonic (310) */ + case 0: PanelDesPtr = SiS_Pr->SiS_PanelType00_1; break; /* --- */ case 1: PanelDesPtr = SiS_Pr->SiS_PanelType01_1; break; case 2: PanelDesPtr = SiS_Pr->SiS_PanelType02_1; break; case 3: PanelDesPtr = SiS_Pr->SiS_PanelType03_1; break; @@ -2356,11 +2662,11 @@ case 9: PanelDesPtr = SiS_Pr->SiS_PanelType09_1; break; case 10: PanelDesPtr = SiS_Pr->SiS_PanelType0a_1; break; case 11: PanelDesPtr = SiS_Pr->SiS_PanelType0b_1; break; - case 12: PanelDesPtr = SiS_Pr->SiS_PanelType0c_1; break; /* TW: Clevo 2202 (300) */ + case 12: PanelDesPtr = SiS_Pr->SiS_PanelType0c_1; break; case 13: PanelDesPtr = SiS_Pr->SiS_PanelType0d_1; break; - case 14: PanelDesPtr = SiS_Pr->SiS_PanelType0e_1; break; /* TW: Uniwill N271S2 (300) */ + case 14: PanelDesPtr = SiS_Pr->SiS_PanelType0e_1; break; case 15: PanelDesPtr = SiS_Pr->SiS_PanelType0f_1; break; - case 16: PanelDesPtr = SiS_Pr->SiS_PanelType00_2; break; /* --- non-expanding --- */ + case 16: PanelDesPtr = SiS_Pr->SiS_PanelType00_2; break; /* --- */ case 17: PanelDesPtr = SiS_Pr->SiS_PanelType01_2; break; case 18: PanelDesPtr = SiS_Pr->SiS_PanelType02_2; break; case 19: PanelDesPtr = SiS_Pr->SiS_PanelType03_2; break; @@ -2372,59 +2678,68 @@ case 25: PanelDesPtr = SiS_Pr->SiS_PanelType09_2; break; case 26: PanelDesPtr = SiS_Pr->SiS_PanelType0a_2; break; case 27: PanelDesPtr = SiS_Pr->SiS_PanelType0b_2; break; - case 28: PanelDesPtr = SiS_Pr->SiS_PanelType0c_2; break; /* TW: Gericom 2200C (300) */ + case 28: PanelDesPtr = SiS_Pr->SiS_PanelType0c_2; break; case 29: PanelDesPtr = SiS_Pr->SiS_PanelType0d_2; break; case 30: PanelDesPtr = SiS_Pr->SiS_PanelType0e_2; break; case 31: PanelDesPtr = SiS_Pr->SiS_PanelType0f_2; break; - case 32: PanelDesPtr = SiS_Pr->SiS_CHTVUNTSCDesData; break; - case 33: PanelDesPtr = SiS_Pr->SiS_CHTVONTSCDesData; break; - case 34: PanelDesPtr = SiS_Pr->SiS_CHTVUPALDesData; break; - case 35: PanelDesPtr = SiS_Pr->SiS_CHTVOPALDesData; break; + case 32: PanelDesPtr = SiS_Pr->SiS_PanelTypeNS_1; break; /* pass 1:1 */ + case 33: PanelDesPtr = SiS_Pr->SiS_PanelTypeNS_2; break; + case 50: PanelDesPtr = SiS_Pr->SiS_CHTVUNTSCDesData; break; /* TV */ + case 51: PanelDesPtr = SiS_Pr->SiS_CHTVONTSCDesData; break; + case 52: PanelDesPtr = SiS_Pr->SiS_CHTVUPALDesData; break; + case 53: PanelDesPtr = SiS_Pr->SiS_CHTVOPALDesData; break; + default: + if(HwDeviceExtension->jChipType < SIS_315H) + PanelDesPtr = SiS_Pr->SiS_PanelType0e_1; + else + PanelDesPtr = SiS_Pr->SiS_PanelType01_1; + break; } } SiS_Pr->SiS_LCDHDES = (PanelDesPtr+ResIndex)->LCDHDES; SiS_Pr->SiS_LCDVDES = (PanelDesPtr+ResIndex)->LCDVDES; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding){ - if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { - if(ModeNo <= 0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - if(!(modeflag & HalfDCLK)) { - SiS_Pr->SiS_LCDHDES = 632; - } - } - } else { - if(!(SiS_Pr->SiS_SetFlag & CRT2IsVGA)) { - if((HwDeviceExtension->jChipType < SIS_315H) || (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024)) { /* TW: New from 650/LVDS 1.10.07 */ - if(SiS_Pr->SiS_LCDResInfo >= SiS_Pr->SiS_Panel1024x768){ - if(ModeNo <= 0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - if(HwDeviceExtension->jChipType < SIS_315H) { - if(!(modeflag & HalfDCLK)) { - SiS_Pr->SiS_LCDHDES = 320; - } - } else { - /* TW: New from 650/LVDS 1.10.07 */ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) - SiS_Pr->SiS_LCDHDES = 480; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) - SiS_Pr->SiS_LCDHDES = 804; - if(!(modeflag & HalfDCLK)) { - SiS_Pr->SiS_LCDHDES = 320; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) - SiS_Pr->SiS_LCDHDES = 632; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD){ + if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + if(ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + if(!(modeflag & HalfDCLK)) { + SiS_Pr->SiS_LCDHDES = 632; + } + } + } else { + if(!(SiS_Pr->SiS_SetFlag & SetDOSMode)) { + if( (HwDeviceExtension->jChipType < SIS_315H) || + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024) ) { + if(SiS_Pr->SiS_LCDResInfo >= SiS_Pr->SiS_Panel1024x768){ + if(ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + if(HwDeviceExtension->jChipType < SIS_315H) { + if(!(modeflag & HalfDCLK)) SiS_Pr->SiS_LCDHDES = 320; + } else { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) + SiS_Pr->SiS_LCDHDES = 480; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) + SiS_Pr->SiS_LCDHDES = 804; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) + SiS_Pr->SiS_LCDHDES = 704; + if(!(modeflag & HalfDCLK)) { + SiS_Pr->SiS_LCDHDES = 320; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) + SiS_Pr->SiS_LCDHDES = 632; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) + SiS_Pr->SiS_LCDHDES = 542; + } + } } } - } - } + } } - } - } + } } return; } -/* TW: Checked against 630/LVDS (2.04.5c) and 650/LVDS (1.10.07) BIOS */ void SiS_GetLVDSDesPtr(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,USHORT *PanelIndex, @@ -2432,7 +2747,7 @@ { USHORT tempbx,tempal,modeflag; - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; } else { @@ -2442,70 +2757,80 @@ tempbx = 0; if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { - tempbx = 32; - if(SiS_Pr->SiS_VBInfo & SetPALTV) tempbx += 2; - if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; - } + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { + tempbx = 50; + if((SiS_Pr->SiS_VBInfo & SetPALTV) && (!SiS_Pr->SiS_CHPALM)) tempbx += 2; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + /* TW: Nothing special needed for SOverscan */ + /* PALM uses NTSC data, PALN uses PAL data */ + } } if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - tempbx = SiS_Pr->SiS_LCDTypeInfo; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 16; - /* TW: Inserted from 650/LVDS (1.10.07) BIOS */ - if(SiS_Pr->SiS_LCDInfo & LCDPass11) { - if(modeflag & HalfDCLK) tempbx += 16; - } + tempbx = SiS_Pr->SiS_LCDTypeInfo; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 16; + if(SiS_Pr->SiS_LCDInfo & LCDPass11) { + tempbx = 32; + if(modeflag & HalfDCLK) tempbx++; + } } - /* TW: Inserted from 630/LVDS and 650/LVDS (1.10.07) BIOS */ - if(SiS_Pr->SiS_SetFlag & CRT2IsVGA) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - tempal = 0x07; - if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x80) tempal++; - } - } + /* TW: 630/LVDS and 650/LVDS (1.10.07) BIOS */ + if(SiS_Pr->SiS_SetFlag & SetDOSMode) { + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { + tempal = 0x07; + if(HwDeviceExtension->jChipType < SIS_315H) { + if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x80) tempal++; + } + } } *PanelIndex = tempbx; *ResIndex = tempal & 0x1F; } +#ifdef SIS315H void SiS_GetLVDSDesPtrA(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, - USHORT RefreshRateTableIndex,USHORT *PanelIndex, - USHORT *ResIndex) + USHORT RefreshRateTableIndex,USHORT *PanelIndex,USHORT *ResIndex) { USHORT tempbx=0,tempal; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - tempbx = 3; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { - tempbx = 4; - } else tempbx = SiS_Pr->SiS_LCDResInfo - SiS_Pr->SiS_PanelMinLVDS; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempbx = 2; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempbx = 3; + else tempbx = SiS_Pr->SiS_LCDResInfo - SiS_Pr->SiS_PanelMinLVDS; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 4; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 4; - if(ModeNo<=0x13) - tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; + if(ModeNo <= 0x13) + tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; else - tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; + tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; *PanelIndex = tempbx; *ResIndex = tempal & 0x1F; } +#endif -/* TW: Checked against 650/LVDS (1.10.07), 650/301LV, 650/301LVx (!), 630/301 and 630/301B (II) BIOS */ void SiS_SetCRT2ModeRegs(SiS_Private *SiS_Pr, USHORT BaseAddr, USHORT ModeNo, USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT i,j,modeflag; - USHORT tempcl,tempah,tempbl,temp; + USHORT tempcl,tempah=0; +#ifdef SIS300 + USHORT temp; +#endif +#ifdef SIS315H + USHORT tempbl; +#endif - if(ModeNo<=0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + if(ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; } else { + if(SiS_Pr->UseCustomMode) { + modeflag = SiS_Pr->CModeFlag; + } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + } } /* TW: BIOS does not do this (neither 301 nor LVDS) */ @@ -2528,14 +2853,14 @@ if(HwDeviceExtension->jChipType < SIS_315H) { - /* ---- 300 series ---- */ +#ifdef SIS300 /* ---- 300 series ---- */ - /* TW: Inserted entire if-section from 630/301B BIOS */ - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + /* For 301BDH: */ + if(SiS_Pr->SiS_VBType & VB_NoLCD) { temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32); temp &= 0xef; temp |= 0x02; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) || (SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC)) { temp |= 0x10; temp &= 0xfd; } @@ -2547,18 +2872,20 @@ if((tempcl > 0) || (tempcl == 0)) { /* TW: tempcl is USHORT -> always true! */ tempah = ((0x10 >> tempcl) | 0x80); } - } else tempah = 0x80; + } else tempah = 0x80; if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) tempah ^= 0xA0; +#endif /* SIS300 */ + } else { - /* ---- 310 series ---- */ +#ifdef SIS315H /* ---- 310/325/330 series ---- */ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - if(SiS_Pr->SiS_VBInfo & CRT2DisplayFlag) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x08); - } + if(SiS_Pr->SiS_VBInfo & CRT2DisplayFlag) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x08); + } } if(ModeNo > 0x13) { @@ -2568,18 +2895,28 @@ if (tempah == 0) tempah = 1; tempah |= 0x40; } - } else tempah = 0x40; + } else tempah = 0x40; if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) tempah ^= 0x50; +#endif /* SIS315H */ + } if(SiS_Pr->SiS_VBInfo & CRT2DisplayFlag) tempah = 0; if(HwDeviceExtension->jChipType < SIS_315H) { - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,tempah); /* FUNCTION CONTROL */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,tempah); /* FUNCTION CONTROL */ } else { - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x00,0xa0,tempah); /* FUNCTION CONTROL */ + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x00,0xa0,tempah); /* FUNCTION CONTROL */ + } else { + if(IS_SIS740) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,tempah); /* FUNCTION CONTROL */ + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x00,0xa0,tempah); /* FUNCTION CONTROL */ + } + } } if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { @@ -2619,7 +2956,7 @@ tempah |= 0x10; } - /* TW: Inserted from 630/301 BIOS */ + /* TW: 630/301 BIOS */ if((HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_VBType & VB_SIS301)) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { tempah |= 0x80; @@ -2628,9 +2965,11 @@ tempah |= 0x80; } - if(SiS_Pr->SiS_VBInfo & (SetCRT2ToTV - SetCRT2ToHiVisionTV)){ + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToTV - SetCRT2ToHiVisionTV)) { if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - tempah |= 0x20; + if(!(SiS_Pr->SiS_HiVision & 0x03)) { + tempah |= 0x20; + } } } @@ -2638,28 +2977,26 @@ tempah = 0; if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; - tempah |= 0x40; - } else { - if(!(SiS_Pr->SiS_SetFlag & TVSimuMode)) { -#ifdef oldHV - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { -#endif - SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; - tempah |= 0x40; -#ifdef oldHV - } -#endif - } - } - } else { - SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; - tempah |= 0x40; - } + if(!(SiS_Pr->SiS_HiVision & 0x03)) { + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; + tempah |= 0x40; + } else { + if(!(SiS_Pr->SiS_SetFlag & TVSimuMode)) { + SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; + tempah |= 0x40; + } + } + } + } else { + SiS_Pr->SiS_SetFlag |= RPLLDIV2XO; + tempah |= 0x40; + } + } } - /* TW: Inserted from 630/301LVx BIOS 1.10.6s */ + /* TW: For 302LV dual-channel */ if(HwDeviceExtension->jChipType >= SIS_315H) { if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) @@ -2667,8 +3004,8 @@ } } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024 || - SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) { + if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960)) { tempah |= 0x80; } @@ -2678,7 +3015,6 @@ /* TW: 3. for LVDS */ - /* TW: Inserted if-statement - Part1Port 0x2e not assigned on 300 series */ if(HwDeviceExtension->jChipType >= SIS_315H) { /* TW: Inserted this entire section (BIOS 650/LVDS); added ModeType check @@ -2707,7 +3043,7 @@ } else { - /* TW: Inserted entire section from 630/LVDS BIOS (added ModeType check) */ + /* TW: (added ModeType check) */ tempah = 0; if( (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) && (SiS_Pr->SiS_ModeType > ModeVGA) ) { tempah |= 0x02; @@ -2730,77 +3066,128 @@ if(HwDeviceExtension->jChipType >= SIS_315H) { -#ifdef SIS650301NEW /* TW: This is done in 650/301LVx 1.10.6s BIOS */ - tempah = 0x04; - tempbl = 0xfb; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { - tempah = 0x00; - if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) - tempbl = 0xff; - } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,tempbl,tempah); +#ifdef SIS315H + if(!(IS_SIS740)) { + tempah = 0x04; /* For all bridges */ + tempbl = 0xfb; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + tempah = 0x00; + if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) + tempbl = 0xff; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,tempbl,tempah); + } + + if(IS_SIS740) { + tempah = 0x30; + tempbl = 0xcf; + if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { + tempah = 0x00; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,tempbl,tempah); + } else { + /* TW: This in order to fix "TV-blue-bug" on 315+301 */ + if(SiS_Pr->SiS_VBType & VB_SIS301) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2c,0xCF); /* For 301 */ + } else { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,0xCF,0x30); /* For 30xLV */ + } else { + tempah = 0x30; /* For 301B */ + tempbl = 0xcf; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + tempah = 0x00; + if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) { + tempbl = 0xff; + } + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,tempbl,tempah); + } + } + } - /* TW: This in order to fix "TV-blue-bug" on 315+301 */ - if(SiS_Pr->SiS_VBType & VB_SIS301) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2c,0xCF); /* RIGHT for 315+301 */ + if(IS_SIS740) { + tempah = 0xc0; + tempbl = 0x3f; + if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { + tempah = 0x00; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,tempbl,tempah); } else { - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,0xCF,0x30); /* WRONG for 315+301 */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { /* For 30xLV */ + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,0x3f,0xc0); + } else { /* For 301, 301B */ + tempah = 0xc0; + tempbl = 0x3f; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + tempah = 0x00; + if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) { + tempbl = 0xff; + } + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,tempbl,tempah); + } } - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,0x3f,0xc0); - - tempah = 0x00; - tempbl = 0x7f; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { - tempbl = 0xff; - if(!(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr))) - tempah |= 0x80; - } - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,tempbl,tempah); + if(IS_SIS740) { + tempah = 0x80; + tempbl = 0x7f; + if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { + tempah = 0x00; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,tempbl,tempah); + } else { + tempah = 0x00; /* For all bridges */ + tempbl = 0x7f; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + tempbl = 0xff; + if(!(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr))) + tempah |= 0x80; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,tempbl,tempah); + } -#else - /* This is done in 650/301LV BIOS instead: */ - tempah = 0x30; - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) tempah = 0; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,0xcf,tempah); - - tempah = 0xc0; - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) tempah = 0; - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,0x3f,tempah); - - tempah = 0x80; - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) tempah = 0; - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,0x7F,tempah); -#endif +#endif /* SIS315H */ } else if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x21,0x3f); - if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | DisableCRT2Display)) + if((SiS_Pr->SiS_VBInfo & DisableCRT2Display) || + ( (SiS_Pr->SiS_VBType & VB_NoLCD) && + (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) ) { SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x23,0x7F); - else + } else { SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x23,0x80); + } } } else { /* LVDS */ +#ifdef SIS315H if(HwDeviceExtension->jChipType >= SIS_315H) { - tempah = 0x04; - tempbl = 0xfb; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { - tempah = 0x00; - if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) - tempbl = 0xff; - } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,tempbl,tempah); - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xfb,0x00); + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + + tempah = 0x04; + tempbl = 0xfb; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + tempah = 0x00; + if(SiS_IsDualEdge(SiS_Pr, HwDeviceExtension, BaseAddr)) + tempbl = 0xff; + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,tempbl,tempah); + + if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,0xfb,0x00); + + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,0xcf,0x30); + + } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2c,0xcf,0x30); } +#endif } @@ -2811,27 +3198,40 @@ USHORT RefreshRateTableIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - SiS_GetCRT2DataLVDS(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, + + SiS_GetCRT2DataLVDS(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); } else { - if((HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)){ - SiS_GetCRT2Data301(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, - HwDeviceExtension); - /* TW: Need LVDS Data for LCD on 630/301B! */ + + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && + (SiS_Pr->SiS_VBType & VB_NoLCD) ) { + + /* TW: Need LVDS Data for LCD on 301BDH */ SiS_GetCRT2DataLVDS(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); - } else { + + } else { + SiS_GetCRT2Data301(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); - } + } + } - } else + + } else { + SiS_GetCRT2Data301(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); + } + } else { + SiS_GetCRT2DataLVDS(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, HwDeviceExtension); } @@ -2847,9 +3247,21 @@ const SiS_LVDSDataStruct *LVDSData = NULL; SiS_GetCRT2ResInfo(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,HwDeviceExtension); + + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + SiS_Pr->SiS_RVBHCMAX = 1; + SiS_Pr->SiS_RVBHCFACT = 1; + SiS_Pr->SiS_NewFlickerMode = 0; + SiS_Pr->SiS_RVBHRS = 50; + SiS_Pr->SiS_RY1COE = 0; + SiS_Pr->SiS_RY2COE = 0; + SiS_Pr->SiS_RY3COE = 0; + SiS_Pr->SiS_RY4COE = 0; + } if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { +#ifdef SIS315H SiS_GetCRT2PtrA(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &CRT2Index,&ResIndex); @@ -2864,24 +3276,26 @@ case 7: LVDSData = SiS_Pr->SiS_LVDS1280x960Data_2; break; case 8: LVDSData = SiS_Pr->SiS_LCDA1400x1050Data_2; break; case 9: LVDSData = SiS_Pr->SiS_LCDA1600x1200Data_2; break; + default: LVDSData = SiS_Pr->SiS_LVDS1024x768Data_1; break; } +#endif } else { - /* TW: SiS630/301B needs LVDS Data! */ - if( (HwDeviceExtension->jChipType < SIS_315H) && - (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && - (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) - SiS_Pr->SiS_IF_DEF_LVDS = 1; + /* TW: 301BDH needs LVDS Data */ + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && + (SiS_Pr->SiS_VBType & VB_NoLCD) ) { + SiS_Pr->SiS_IF_DEF_LVDS = 1; + } SiS_GetCRT2Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &CRT2Index,&ResIndex,HwDeviceExtension); - /* TW: SiS630/301B needs LVDS Data! */ - if( (HwDeviceExtension->jChipType < SIS_315H) && - (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && - (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) + /* TW: 301BDH needs LVDS Data */ + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && + (SiS_Pr->SiS_VBType & VB_NoLCD) ) { SiS_Pr->SiS_IF_DEF_LVDS = 0; + } switch (CRT2Index) { case 0: LVDSData = SiS_Pr->SiS_LVDS800x600Data_1; break; @@ -2891,32 +3305,42 @@ case 4: LVDSData = SiS_Pr->SiS_LVDS1024x768Data_2; break; case 5: LVDSData = SiS_Pr->SiS_LVDS1280x1024Data_2; break; case 6: LVDSData = SiS_Pr->SiS_LVDS640x480Data_1; break; - case 7: LVDSData = SiS_Pr->SiS_LVDSXXXxXXXData_1; break; /* TW: New */ - case 8: LVDSData = SiS_Pr->SiS_LVDS1400x1050Data_1; break; /* TW: New */ - case 9: LVDSData = SiS_Pr->SiS_LVDS1400x1050Data_2; break; /* TW: New */ + case 7: LVDSData = SiS_Pr->SiS_LVDSXXXxXXXData_1; break; + case 8: LVDSData = SiS_Pr->SiS_LVDS1400x1050Data_1; break; + case 9: LVDSData = SiS_Pr->SiS_LVDS1400x1050Data_2; break; case 10: LVDSData = SiS_Pr->SiS_CHTVUNTSCData; break; case 11: LVDSData = SiS_Pr->SiS_CHTVONTSCData; break; case 12: LVDSData = SiS_Pr->SiS_CHTVUPALData; break; case 13: LVDSData = SiS_Pr->SiS_CHTVOPALData; break; case 14: LVDSData = SiS_Pr->SiS_LVDS320x480Data_1; break; - case 15: LVDSData = SiS_Pr->SiS_LVDS1024x600Data_1; break; /* TW: New */ - case 16: LVDSData = SiS_Pr->SiS_LVDS1152x768Data_1; break; /* TW: New */ - case 17: LVDSData = SiS_Pr->SiS_LVDS1024x600Data_2; break; /* TW: New */ - case 18: LVDSData = SiS_Pr->SiS_LVDS1152x768Data_2; break; /* TW: New */ + case 15: LVDSData = SiS_Pr->SiS_LVDS1024x600Data_1; break; + case 16: LVDSData = SiS_Pr->SiS_LVDS1152x768Data_1; break; + case 17: LVDSData = SiS_Pr->SiS_LVDS1024x600Data_2; break; + case 18: LVDSData = SiS_Pr->SiS_LVDS1152x768Data_2; break; + case 19: LVDSData = SiS_Pr->SiS_LVDS1280x768Data_1; break; + case 20: LVDSData = SiS_Pr->SiS_LVDS1280x768Data_2; break; + case 21: LVDSData = SiS_Pr->SiS_LVDS1600x1200Data_1; break; + case 22: LVDSData = SiS_Pr->SiS_LVDS1600x1200Data_2; break; + case 90: LVDSData = SiS_Pr->SiS_CHTVUPALMData; break; + case 91: LVDSData = SiS_Pr->SiS_CHTVOPALMData; break; + case 92: LVDSData = SiS_Pr->SiS_CHTVUPALNData; break; + case 93: LVDSData = SiS_Pr->SiS_CHTVOPALNData; break; + case 99: LVDSData = SiS_Pr->SiS_CHTVSOPALData; break; /* TW: Super Overscan */ + default: LVDSData = SiS_Pr->SiS_LVDS1024x768Data_1; break; } } SiS_Pr->SiS_VGAHT = (LVDSData+ResIndex)->VGAHT; SiS_Pr->SiS_VGAVT = (LVDSData+ResIndex)->VGAVT; - SiS_Pr->SiS_HT = (LVDSData+ResIndex)->LCDHT; - SiS_Pr->SiS_VT = (LVDSData+ResIndex)->LCDVT; + SiS_Pr->SiS_HT = (LVDSData+ResIndex)->LCDHT; + SiS_Pr->SiS_VT = (LVDSData+ResIndex)->LCDVT; - if((SiS_Pr->SiS_IF_DEF_LVDS == 0) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - if(!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)){ + if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)){ if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768){ SiS_Pr->SiS_HDE = 1024; - SiS_Pr->SiS_VDE = 768; + SiS_Pr->SiS_VDE = 768; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024){ SiS_Pr->SiS_HDE = 1280; SiS_Pr->SiS_VDE = 1024; @@ -2928,7 +3352,7 @@ SiS_Pr->SiS_VDE = 1200; } else { SiS_Pr->SiS_HDE = 1280; - SiS_Pr->SiS_VDE = 960; + SiS_Pr->SiS_VDE = 960; } } @@ -2937,31 +3361,31 @@ if(SiS_Pr->SiS_IF_DEF_TRUMPION == 0) { if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (!(SiS_Pr->SiS_LCDInfo & LCDPass11))) { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) { - if((!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) || (SiS_Pr->SiS_SetFlag & CRT2IsVGA)) { + if((!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) || (SiS_Pr->SiS_SetFlag & SetDOSMode)) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) { - SiS_Pr->SiS_HDE = 800; - SiS_Pr->SiS_VDE = 600; + SiS_Pr->SiS_HDE = 800; + SiS_Pr->SiS_VDE = 600; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { + SiS_Pr->SiS_HDE = 1024; + SiS_Pr->SiS_VDE = 600; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { SiS_Pr->SiS_HDE = 1024; - SiS_Pr->SiS_VDE = 768; + SiS_Pr->SiS_VDE = 768; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { + SiS_Pr->SiS_HDE = 1152; + SiS_Pr->SiS_VDE = 768; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x864) { + SiS_Pr->SiS_HDE = 1152; + SiS_Pr->SiS_VDE = 864; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + SiS_Pr->SiS_HDE = 1280; + SiS_Pr->SiS_VDE = 768; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { SiS_Pr->SiS_HDE = 1280; SiS_Pr->SiS_VDE = 1024; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { - SiS_Pr->SiS_HDE = 1024; - SiS_Pr->SiS_VDE = 600; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { SiS_Pr->SiS_HDE = 1400; SiS_Pr->SiS_VDE = 1050; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { - SiS_Pr->SiS_HDE = 1152; - SiS_Pr->SiS_VDE = 768; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x864) { - SiS_Pr->SiS_HDE = 1152; - SiS_Pr->SiS_VDE = 864; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { - SiS_Pr->SiS_HDE = 1280; - SiS_Pr->SiS_VDE = 768; } else { SiS_Pr->SiS_HDE = 1600; SiS_Pr->SiS_VDE = 1200; @@ -2977,7 +3401,6 @@ } } -/* TW: Checked against 630/301B BIOS; does not check VDE values for LCD */ void SiS_GetCRT2Data301(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex, @@ -2989,13 +3412,14 @@ const SiS_LCDDataStruct *LCDPtr = NULL; const SiS_TVDataStruct *TVPtr = NULL; - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; } + SiS_Pr->SiS_NewFlickerMode = 0; SiS_Pr->SiS_RVBHRS = 50; SiS_Pr->SiS_RY1COE = 0; @@ -3021,11 +3445,9 @@ &CRT2Index,&ResIndex,HwDeviceExtension); switch (CRT2Index) { -#ifdef oldHV case 2: TVPtr = SiS_Pr->SiS_ExtHiTVData; break; - case 7: TVPtr = SiS_Pr->SiS_St1HiTVData; break; +/* case 7: TVPtr = SiS_Pr->SiS_St1HiTVData; break; */ case 12: TVPtr = SiS_Pr->SiS_St2HiTVData; break; -#endif case 3: TVPtr = SiS_Pr->SiS_ExtPALData; break; case 4: TVPtr = SiS_Pr->SiS_ExtNTSCData; break; case 8: TVPtr = SiS_Pr->SiS_StPALData; break; @@ -3041,27 +3463,44 @@ SiS_Pr->SiS_VDE = (TVPtr+ResIndex)->TVVDE; SiS_Pr->SiS_RVBHRS = (TVPtr+ResIndex)->RVBHRS; SiS_Pr->SiS_NewFlickerMode = (TVPtr+ResIndex)->FlickerMode; + if(modeflag & HalfDCLK) { + SiS_Pr->SiS_RVBHRS = (TVPtr+ResIndex)->HALFRVBHRS; + } - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { /* TW: NOT oldHV! */ - - if(resinfo == 0x08) SiS_Pr->SiS_NewFlickerMode = 0x40; - if(resinfo == 0x09) SiS_Pr->SiS_NewFlickerMode = 0x40; - if(resinfo == 0x12) SiS_Pr->SiS_NewFlickerMode = 0x40; - - if(SiS_Pr->SiS_VGAVDE == 350) SiS_Pr->SiS_SetFlag |= TVSimuMode; - - SiS_Pr->SiS_HT = ExtHiTVHT; - SiS_Pr->SiS_VT = ExtHiTVVT; - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if(SiS_Pr->SiS_SetFlag & TVSimuMode) { - SiS_Pr->SiS_HT = StHiTVHT; - SiS_Pr->SiS_VT = StHiTVVT; - if(!(modeflag & Charx8Dot)){ - SiS_Pr->SiS_HT = StHiTextTVHT; - SiS_Pr->SiS_VT = StHiTextTVVT; - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + + if(SiS_Pr->SiS_HiVision != 3) { + + if(resinfo == 0x08) SiS_Pr->SiS_NewFlickerMode = 0x40; + if(resinfo == 0x09) SiS_Pr->SiS_NewFlickerMode = 0x40; + if(resinfo == 0x12) SiS_Pr->SiS_NewFlickerMode = 0x40; + + } + + switch(SiS_Pr->SiS_HiVision) { + case 2: + case 1: + case 0: + SiS_Pr->SiS_HT = 0x6b4; + SiS_Pr->SiS_VT = 0x20d; + /* Don't care about TVSimuMode */ + break; + default: + if(SiS_Pr->SiS_VGAVDE == 350) SiS_Pr->SiS_SetFlag |= TVSimuMode; + + SiS_Pr->SiS_HT = ExtHiTVHT; + SiS_Pr->SiS_VT = ExtHiTVVT; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { + if(SiS_Pr->SiS_SetFlag & TVSimuMode) { + SiS_Pr->SiS_HT = StHiTVHT; + SiS_Pr->SiS_VT = StHiTVVT; + if(!(modeflag & Charx8Dot)){ + SiS_Pr->SiS_HT = StHiTextTVHT; + SiS_Pr->SiS_VT = StHiTextTVVT; + } + } } - } + } } else { @@ -3077,11 +3516,11 @@ SiS_Pr->SiS_RY4COE = 0x38; } - if(!(SiS_Pr->SiS_VBInfo & SetPALTV)){ + if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { SiS_Pr->SiS_HT = NTSCHT; -#if 0 /* TW: Not in 650/301LVx 1.10.6s */ - if((ModeNo == 0x4a) || (ModeNo == 0x38)) SiS_Pr->SiS_HT = NTSC2HT; -#endif + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + if((ModeNo == 0x4a) || (ModeNo == 0x38)) SiS_Pr->SiS_HT = NTSC2HT; + } SiS_Pr->SiS_VT = NTSCVT; } else { SiS_Pr->SiS_HT = PALHT; @@ -3094,14 +3533,13 @@ } /* TW: For LCD */ - /* TW: Checked against 650/301LV; CRT2Index different (but does not matter) */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { SiS_GetCRT2Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &CRT2Index,&ResIndex,HwDeviceExtension); - switch (CRT2Index) { + switch(CRT2Index) { case 0: LCDPtr = SiS_Pr->SiS_ExtLCD1024x768Data; break; /* VESA Timing */ case 1: LCDPtr = SiS_Pr->SiS_ExtLCD1280x1024Data; break; /* VESA Timing */ case 5: LCDPtr = SiS_Pr->SiS_StLCD1024x768Data; break; /* Obviously unused */ @@ -3112,11 +3550,15 @@ case 14: LCDPtr = SiS_Pr->SiS_NoScaleData1280x1024; break; /* Non-expanding */ case 15: LCDPtr = SiS_Pr->SiS_LCD1280x960Data; break; /* 1280x960 */ case 20: LCDPtr = SiS_Pr->SiS_ExtLCD1400x1050Data; break; /* VESA Timing */ - case 21: LCDPtr = SiS_Pr->SiS_NoScaleData1400x1050; break; /* Non-expanding */ - case 22: LCDPtr = SiS_Pr->SiS_StLCD1400x1050Data; break; /* Non-VESA Timing */ + case 21: LCDPtr = SiS_Pr->SiS_NoScaleData1400x1050; break; /* Non-expanding (let panel scale) */ + case 22: LCDPtr = SiS_Pr->SiS_StLCD1400x1050Data; break; /* Non-VESA Timing (let panel scale) */ case 23: LCDPtr = SiS_Pr->SiS_ExtLCD1600x1200Data; break; /* VESA Timing */ case 24: LCDPtr = SiS_Pr->SiS_NoScaleData1600x1200; break; /* Non-expanding */ case 25: LCDPtr = SiS_Pr->SiS_StLCD1600x1200Data; break; /* Non-VESA Timing */ + case 26: LCDPtr = SiS_Pr->SiS_ExtLCD1280x768Data; break; /* VESA Timing */ + case 27: LCDPtr = SiS_Pr->SiS_NoScaleData1280x768; break; /* Non-expanding */ + case 28: LCDPtr = SiS_Pr->SiS_StLCD1280x768Data; break; /* Non-VESA Timing */ + case 29: LCDPtr = SiS_Pr->SiS_NoScaleData; break; /* Generic no-scale data */ default: LCDPtr = SiS_Pr->SiS_ExtLCD1024x768Data; break; /* Just to avoid a crash */ } @@ -3126,12 +3568,21 @@ SiS_Pr->SiS_VGAVT = (LCDPtr+ResIndex)->VGAVT; SiS_Pr->SiS_HT = (LCDPtr+ResIndex)->LCDHT; SiS_Pr->SiS_VT = (LCDPtr+ResIndex)->LCDVT; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, + "GetCRT2Data: Index %d ResIndex %d\n", CRT2Index, ResIndex); +#endif tempax = 1024; if(SiS_Pr->SiS_SetFlag & LCDVESATiming) { - if (SiS_Pr->SiS_VGAVDE == 350) tempbx = 560; - else if(SiS_Pr->SiS_VGAVDE == 400) tempbx = 640; - else tempbx = 768; + if(HwDeviceExtension->jChipType < SIS_315H) { + if (SiS_Pr->SiS_VGAVDE == 350) tempbx = 560; + else if(SiS_Pr->SiS_VGAVDE == 400) tempbx = 640; + else tempbx = 768; + } else { + tempbx = 768; + } } else { if (SiS_Pr->SiS_VGAVDE == 357) tempbx = 527; else if(SiS_Pr->SiS_VGAVDE == 420) tempbx = 620; @@ -3141,25 +3592,33 @@ else if(SiS_Pr->SiS_VGAVDE == 400) tempbx = 640; else tempbx = 768; } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024){ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { tempax = 1280; if (SiS_Pr->SiS_VGAVDE == 360) tempbx = 768; else if(SiS_Pr->SiS_VGAVDE == 375) tempbx = 800; else if(SiS_Pr->SiS_VGAVDE == 405) tempbx = 864; else tempbx = 1024; } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960){ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) { tempax = 1280; if (SiS_Pr->SiS_VGAVDE == 350) tempbx = 700; else if(SiS_Pr->SiS_VGAVDE == 400) tempbx = 800; else if(SiS_Pr->SiS_VGAVDE == 1024) tempbx = 960; else tempbx = 960; } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050){ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { tempax = 1400; tempbx = 1050; } - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { + tempax = 1600; + tempbx = 1200; + } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { /* guessed */ + tempax = 1280; + tempbx = 768; + } + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { tempax = SiS_Pr->SiS_VGAHDE; tempbx = SiS_Pr->SiS_VGAVDE; } @@ -3174,7 +3633,7 @@ { USHORT resindex; - if(ModeNo<=0x13) + if(ModeNo <= 0x13) resindex=SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; else resindex=SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; @@ -3182,7 +3641,6 @@ return(resindex); } -/* TW: Checked against 650/301LV, 650/LVDS, 630/LVDS, 630/301 and 630/301B BIOS */ void SiS_GetCRT2ResInfo(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -3200,14 +3658,12 @@ modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; } - /* TW: Inserted entire if-section from 650/LVDS BIOS 1.10.07: */ if((HwDeviceExtension->jChipType >= SIS_315H) && (SiS_Pr->SiS_IF_DEF_LVDS == 1)) { - if((ModeNo != 0x03) && (SiS_Pr->SiS_SetFlag & CRT2IsVGA)) { + if((ModeNo != 0x03) && (SiS_Pr->SiS_SetFlag & SetDOSMode)) { if(yres == 350) yres = 400; } if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x3a) & 0x01) { - if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x34) == 0x12) - yres = 400; + if(ModeNo == 0x12) yres = 400; } } @@ -3222,38 +3678,55 @@ } if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - /* TW: Inserted from 650/301LV BIOS */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - if(xres == 720) xres = 640; + if(xres == 720) xres = 640; } else { - if(xres == 720) xres = 640; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { - if(yres == 400) yres = 405; - if(yres == 350) yres = 360; - if(SiS_Pr->SiS_SetFlag & LCDVESATiming) { - if(yres == 360) yres = 375; - } - } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768){ - if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) { - if(!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { - if(yres == 350) yres = 357; - if(yres == 400) yres = 420; - if(yres == 480) yres = 525; - } - } - } - /* TW: Inserted for 630/301B */ - if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - if(xres == 720) xres = 640; + if(SiS_Pr->SiS_VBType & VB_NoLCD) { /* TW: 301BDH */ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(xres == 720) xres = 640; + } + if(SiS_Pr->SiS_SetFlag & SetDOSMode) { + yres = 400; + if(HwDeviceExtension->jChipType >= SIS_315H) { + if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x17) & 0x80) yres = 480; + } else { + if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x80) yres = 480; + } + } + } else { + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToAVIDEO | /* (Allow 720 for VGA2) */ + SetCRT2ToSVIDEO | + SetCRT2ToSCART | + SetCRT2ToLCD | + SetCRT2ToHiVisionTV)) { + if(xres == 720) xres = 640; + } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { + if(ModeNo <= 0x13) { + /* TW: This is wrong for 640x400 *graphics* mode */ + if(yres == 400) yres = 405; + } + if(yres == 350) yres = 360; + if(SiS_Pr->SiS_SetFlag & LCDVESATiming) { + if(yres == 360) yres = 375; + } + } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768){ + if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) { + if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { + if(yres == 350) yres = 357; + if(yres == 400) yres = 420; + if(yres == 480) yres = 525; + } + } + } } } } } else { if(xres == 720) xres = 640; - /* TW: Inserted from 650/LVDS and 630/LVDS BIOS */ - if(SiS_Pr->SiS_SetFlag & CRT2IsVGA) { + if(SiS_Pr->SiS_SetFlag & SetDOSMode) { yres = 400; if(HwDeviceExtension->jChipType >= SIS_315H) { if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x17) & 0x80) yres = 480; @@ -3266,8 +3739,6 @@ SiS_Pr->SiS_VGAVDE = SiS_Pr->SiS_VDE = yres; } -/* TW: Checked against 650/301 and 650/LVDS (1.10.07) BIOS; modified for new panel resolutions */ -/* TW: Done differently in 630/301B BIOS; but same effect; checked against 630/301 */ void SiS_GetCRT2Ptr(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,USHORT *CRT2Index,USHORT *ResIndex, @@ -3276,21 +3747,49 @@ USHORT tempbx=0,tempal=0; USHORT Flag,resinfo=0; + if(ModeNo <= 0x13) { + tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; + } else { + tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; + resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; + } + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { /* LCD */ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) { tempbx = 15; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { tempbx = 20; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx = 21; - if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx = 22; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx = 21; + else if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx = 22; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { tempbx = 23; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx = 24; - if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx = 25; - } else if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { - tempbx = 13; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempbx++; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx = 24; + else if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx = 25; +#if 0 + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + tempbx = 26; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx = 27; + else if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx = 28; +#endif + } else if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 13; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempbx = 14; + else tempbx = 29; + } else { + tempbx = 29; + if(ModeNo >= 0x13) { + if(HwDeviceExtension->jChipType >= SIS_315H) { + /* 1280x768 and 1280x960 have same CRT2CRTC, + * so we change it here if 1280x960 is chosen + */ + if(resinfo == 0x0f) tempal = 10; + } + } + } } else { tempbx = SiS_Pr->SiS_LCDResInfo - SiS_Pr->SiS_Panel1024x768; if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) { @@ -3300,88 +3799,102 @@ tempbx += 5; } } - } else { -#ifdef oldHV /* TV */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV){ - if(SiS_Pr->SiS_VGAVDE > 480) SiS_Pr->SiS_SetFlag &= (~TVSimuMode); /* TW: Was "(!TVSimuMode)" - WRONG */ + + } else { /* TV */ + + if((SiS_Pr->SiS_VBType & VB_SIS301B302B) && + (SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { + if(SiS_Pr->SiS_VGAVDE > 480) SiS_Pr->SiS_SetFlag &= (~TVSimuMode); tempbx = 2; if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if(!(SiS_Pr->SiS_SetFlag & TVSimuMode)) tempbx = 12; /* TW: Was 10! - WRONG */ + if(!(SiS_Pr->SiS_SetFlag & TVSimuMode)) tempbx = 12; } } else { -#endif if(SiS_Pr->SiS_VBInfo & SetPALTV) tempbx = 3; else tempbx = 4; if(SiS_Pr->SiS_SetFlag & TVSimuMode) tempbx += 5; -#ifdef oldHV } -#endif + } - if(ModeNo <= 0x13) - tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; - else - tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; + tempal &= 0x3F; - tempal &= 0x3F; - - if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) - && (SiS_Pr->SiS_VBInfo & (SetCRT2ToTV-SetCRT2ToHiVisionTV))) { /* TW: Added -Hivision (BIOS) */ + if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && + (SiS_Pr->SiS_VBInfo & (SetCRT2ToTV-SetCRT2ToHiVisionTV))) { if(tempal == 0x06) tempal = 0x07; } - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if((ModeNo == 0x31) || (ModeNo == 0x32)) tempal = 6; + if((HwDeviceExtension->jChipType == SIS_300) && + (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { + if(ModeNo > 0x13) { + if((resinfo == 0x0c) || (resinfo == 0x0d)) /* 720 (index diff. on 310/325!) */ + tempal = 6; + } + } + + if(HwDeviceExtension->jChipType != SIS_300) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + if((ModeNo == 0x31) || (ModeNo == 0x32)) tempal = 6; + } } *CRT2Index = tempbx; *ResIndex = tempal; - } else { /* LVDS */ + } else { /* LVDS, 301B-DH (if running on LCD) */ Flag = 1; tempbx = 0; if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { Flag = 0; tempbx = 10; - if(SiS_Pr->SiS_VBInfo & SetPALTV) tempbx += 2; - if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + if(SiS_Pr->SiS_VBInfo & SetPALTV) { + tempbx += 2; + if(SiS_Pr->SiS_CHSOverScan) tempbx = 99; + if(SiS_Pr->SiS_CHPALM) { + tempbx = 90; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + } else if(SiS_Pr->SiS_CHPALN) { + tempbx = 92; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + } + + } } + } - if(Flag == 1) { - tempbx = SiS_Pr->SiS_LCDResInfo - SiS_Pr->SiS_PanelMinLVDS; + if(Flag) { + if(SiS_Pr->SiS_LCDResInfo <= SiS_Pr->SiS_Panel1280x1024) { - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 3; - } else { - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - tempbx = 8; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx++; - } - if(SiS_Pr->SiS_LCDInfo & LCDPass11) { - tempbx = 7; - } - - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) tempbx = 6; - - /* TW: Inserted from 630/LVDS 2.04.5c BIOS */ - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { - tempbx = 15; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; - } - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { - tempbx = 16; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; - } - } - } - - if(ModeNo <= 0x13) - tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; - else { - tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; - resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; + tempbx = SiS_Pr->SiS_LCDResInfo - SiS_Pr->SiS_PanelMinLVDS; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 3; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + tempbx = 18; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx++; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) { + tempbx = 6; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { + tempbx = 15; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { + tempbx = 16; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { + tempbx = 8; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx++; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { + tempbx = 21; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx++; + } + + if(SiS_Pr->SiS_LCDInfo & LCDPass11) { + tempbx = 7; + } + } if(SiS_Pr->SiS_IF_DEF_FSTN){ @@ -3391,21 +3904,23 @@ } } - /* TW: Inserted from 650/LVDS BIOS */ - if(SiS_Pr->SiS_SetFlag & CRT2IsVGA) { + if(SiS_Pr->SiS_SetFlag & SetDOSMode) { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel640x480) tempal = 7; if(HwDeviceExtension->jChipType < SIS_315H) { - /* TW: Inserted from 630/LVDS (2.04.5c) and 630/301B (II) BIOS */ if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x80) tempal++; } } - /* TW: Inserted from 630/301B BIOS */ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(ModeNo > 0x13) { - if((resinfo == 0x0c) || (resinfo == 0x0d)) /* 720 */ - tempal = 6; + if(HwDeviceExtension->jChipType < SIS_315H) { + if((resinfo == 0x0c) || (resinfo == 0x0d)) /* 720 */ + tempal = 6; + } else { + if((resinfo == 0x0d) || (resinfo == 0x0e)) /* 720 */ + tempal = 6; + } } } @@ -3414,6 +3929,7 @@ } } +#ifdef SIS315H void SiS_GetCRT2PtrA(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,USHORT *CRT2Index, @@ -3423,15 +3939,12 @@ tempbx = SiS_Pr->SiS_LCDResInfo; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { - tempbx = 4; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - tempbx = 3; - } else { - tempbx -= SiS_Pr->SiS_Panel1024x768; - } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempbx = 4; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) tempbx = 3; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) tempbx = 2; + else tempbx -= SiS_Pr->SiS_Panel1024x768; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 5; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 5; if(ModeNo <= 0x13) tempal = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; @@ -3441,8 +3954,8 @@ *CRT2Index = tempbx; *ResIndex = tempal & 0x1F; } +#endif -/* TW: New from 650/301LVx BIOS */ void SiS_GetCRT2Part2Ptr(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,USHORT *CRT2Index, @@ -3457,14 +3970,13 @@ tempbx = SiS_Pr->SiS_LCDResInfo; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 16; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 16; else if(SiS_Pr->SiS_SetFlag & LCDVESATiming) tempbx += 32; *CRT2Index = tempbx; *ResIndex = tempal & 0x3F; } -/* TW: Checked against all (incl 650/LVDS (1.10.07), 630/301B, 630/301) BIOSes */ USHORT SiS_GetRatePtrCRT2(SiS_Private *SiS_Pr, UCHAR *ROMAddr, USHORT ModeNo, USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -3476,7 +3988,9 @@ USHORT RefreshRateTableIndex,i,backup_i; USHORT modeflag,index,temp,backupindex; - if (ModeNo <= 0x13) + if(SiS_Pr->UseCustomMode) return 0; + + if(ModeNo <= 0x13) modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; else modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; @@ -3507,8 +4021,10 @@ if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) index = 0; } else { if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if(HwDeviceExtension->jChipType < SIS_315H) index = 0; - else if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) index = backupindex = 0; + if(SiS_Pr->SiS_VBType & VB_NoLCD) + index = 0; + else if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) + index = backupindex = 0; } } } @@ -3521,9 +4037,10 @@ } if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) { if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - /* TW: This is not done in 630/301B BIOS */ - temp = LCDRefreshIndex[SiS_Pr->SiS_LCDResInfo]; - if(index > temp) index = temp; + if(!(SiS_Pr->SiS_VBType & VB_NoLCD)) { + temp = LCDRefreshIndex[SiS_Pr->SiS_LCDResInfo]; + if(index > temp) index = temp; + } } else { index = 0; } @@ -3533,15 +4050,14 @@ RefreshRateTableIndex = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex; ModeNo = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].ModeID; - /* TW: Inserted from 650/LVDS 1.10.07, 650/301LVx 1.10.6s */ + /* TW: 650/LVDS 1.10.07, 650/30xLV 1.10.6s */ if(HwDeviceExtension->jChipType >= SIS_315H) { - if(!(SiS_Pr->SiS_VBInfo & DriverMode)) { - if( (SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_VESAID == 0x105) || - (SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_VESAID == 0x107) ) { - if(backupindex <= 1) - RefreshRateTableIndex++; - } - } + if(!(SiS_Pr->SiS_VBInfo & DriverMode)) { + if( (SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_VESAID == 0x105) || + (SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_VESAID == 0x107) ) { + if(backupindex <= 1) RefreshRateTableIndex++; + } + } } i = 0; @@ -3598,7 +4114,6 @@ tempax = 0; if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - /* TW: For 301, 301B, 302B, 301LV, 302LV */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { tempax |= SupportRAMDAC2; if(HwDeviceExtension->jChipType >= SIS_315H) { @@ -3615,22 +4130,20 @@ if(HwDeviceExtension->jChipType >= SIS_315H) { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1600x1200) { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1400x1050) { - if((resinfo == 6) && (SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { - tempax |= SupportAllCRT2; - (*i) = 0; /* TW: Restore RefreshTableIndex (BIOS 650/301LVx 1.10.6s) */ + if((resinfo == 6) && (SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { + (*i) = 0; return(1); } else { if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { - if((resinfo == 6) && (SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { - return(0); - } else { - if((resinfo >= 9) && (resinfo != 0x14)) { - tempax = 0; + if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960) { + if((resinfo == 6) && (SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { + return(0); + } else { + if((resinfo >= 9) && (resinfo != 0x14)) { return(0); - } - } - } + } + } + } } } } @@ -3653,19 +4166,29 @@ } } } -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { /* for HiTV */ - tempax |= SupportHiVisionTV; - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode){ - if(resinfo == 4) return(0); - if(resinfo == 3) { - if(SiS_Pr->SiS_SetFlag & TVSimuMode) return(0); - } - if(resinfo > 7) return(0); - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if(SiS_Pr->SiS_HiVision == 3) { + tempax |= SupportHiVisionTV2; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode){ + if(resinfo == 4) return(0); + if(resinfo == 3) return(0); + if(resinfo == 7) { + if(SiS_Pr->SiS_SetFlag & TVSimuMode) return(0); + } + if(resinfo > 7) return(0); + } + } else { + tempax |= SupportHiVisionTV; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode){ + if(resinfo == 4) return(0); + if((resinfo == 3) || (resinfo == 7)) { + if(SiS_Pr->SiS_SetFlag & TVSimuMode) return(0); + } + if(resinfo > 7) return(0); + } + } } else { -#endif - if(SiS_Pr->SiS_VBInfo & (SetCRT2ToAVIDEO|SetCRT2ToSVIDEO|SetCRT2ToSCART)) { + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToAVIDEO|SetCRT2ToSVIDEO|SetCRT2ToSCART)) { tempax |= SupportTV; tempax |= SupportTV1024; if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { @@ -3767,10 +4290,9 @@ } else return(0); } } - } -#ifdef oldHV + } } -#endif + } else { /* TW: for LVDS */ if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { @@ -3821,7 +4343,6 @@ return(1); } -/* Checked against 650/LVDS (1.10.07) and 650/301LV BIOS */ void SiS_SaveCRT2Info(SiS_Private *SiS_Pr, USHORT ModeNo) { @@ -3834,88 +4355,121 @@ SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x31,temp2,temp1); } -/* TW: Checked against 650+301, 650/LVDS (1.10.07) and 650/301LVx (1.10.6s) BIOS */ void SiS_GetVBInfo(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, - USHORT ModeIdIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) + USHORT ModeIdIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension, + int checkcrt2mode) { USHORT tempax,tempbx,temp; USHORT modeflag, resinfo=0; UCHAR OutputSelect = *SiS_Pr->pSiS_OutputSelect; - if (ModeNo<=0x13) + if(SiS_Pr->UseCustomMode) { + modeflag = SiS_Pr->CModeFlag; + } else { + if (ModeNo <= 0x13) modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - else { + else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; - } + } + } SiS_Pr->SiS_SetFlag = 0; SiS_Pr->SiS_ModeType = modeflag & ModeInfoFlag; tempbx = 0; - if(SiS_BridgeIsOn(SiS_Pr,BaseAddr,HwDeviceExtension) == 0) { /* TW: "== 0" inserted from 630/301B BIOS */ + if(SiS_BridgeIsOn(SiS_Pr,BaseAddr,HwDeviceExtension) == 0) { temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); - if(SiS_Pr->SiS_HiVision & 0x03) { /* TW: New from 650/301LVx 1.10.6s */ - temp &= (SetCRT2ToHiVisionTV | SwitchToCRT2 | SetSimuScanMode); /* 0x83 */ - temp |= SetCRT2ToHiVisionTV; /* 0x80 */ - } - if(SiS_Pr->SiS_HiVision & 0x04) { /* TW: New from 650/301LVx 1.10.6s */ - temp &= (SetCRT2ToHiVisionTV | SwitchToCRT2 | SetSimuScanMode); /* 0x83 */ - temp |= SetCRT2ToSVIDEO; /* 0x08 */ - } -#if 0 /* TW: Not in 650/301LVx 1.10.6s BIOS */ - if(SiS_Pr->SiS_VBType & VB_SIS30xLV) { - temp &= 0xbf; /* 301lvds disable CRT2RAMDAC */ - } -#endif +#if 0 + /* SiS_HiVision is only used on 310/325/330+30xLV */ + if(SiS_Pr->SiS_VBType & (VB_SIS301LV302LV)) { + if(SiS_Pr->SiS_HiVision & 0x03) { /* TW: New from 650/30xLV 1.10.6s */ + temp &= (SetCRT2ToHiVisionTV | SwitchToCRT2 | SetSimuScanMode); /* 0x83 */ + temp |= SetCRT2ToHiVisionTV; /* 0x80 */ + } + if(SiS_Pr->SiS_HiVision & 0x04) { /* TW: New from 650/30xLV 1.10.6s */ + temp &= (SetCRT2ToHiVisionTV | SwitchToCRT2 | SetSimuScanMode); /* 0x83 */ + temp |= SetCRT2ToSVIDEO; /* 0x08 */ + } + } +#endif if(SiS_Pr->SiS_IF_DEF_FSTN) { /* fstn must set CR30=0x21 */ temp = (SetCRT2ToLCD | SetSimuScanMode); SiS_SetReg1(SiS_Pr->SiS_P3d4,0x30,temp); } tempbx |= temp; - temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31); - tempax = temp << 8; - tempax &= (LoadDACFlag | DriverMode | SetDispDevSwitch | /* TW: Inserted from 650/LVDS+301LV BIOS */ - SetNotSimuMode | SetPALTV); /* TW: Inserted from 650/LVDS+301LV BIOS */ + tempax = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) << 8; + tempax &= (LoadDACFlag | DriverMode | SetDispDevSwitch | SetNotSimuMode | SetPALTV); tempbx |= tempax; - temp = SetCHTVOverScan | SetInSlaveMode | DisableCRT2Display; - temp = 0xFFFF ^ temp; - tempbx &= temp; + tempbx &= ~(SetCHTVOverScan | SetInSlaveMode | DisableCRT2Display);; + #ifdef SIS315H + if(HwDeviceExtension->jChipType >= SIS_315H) { - temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(SiS_Pr->SiS_VBType & (VB_SIS302B | VB_SIS30xLV | VB_SIS30xNEW)) { - if((SiS_GetReg1(SiS_Pr->SiS_P3d4, 0x36) & 0x0f) == SiS_Pr->SiS_Panel1400x1050) { - if(tempbx & SetCRT2ToLCD) { - if(ModeNo <= 0x13) { - if(SiS_CRT2IsLCD(SiS_Pr, BaseAddr)) { - if(!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & (SetNotSimuMode >> 8))) { - SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x38,0x03); - } - } - } + if(SiS_Pr->SiS_VBType & (VB_SIS302B | VB_SIS301LV | VB_SIS302LV)) { + /* From 1.10.7w, not in 1.10.8r */ + if(ModeNo == 0x03) { + /* Mode 0x03 is never in driver mode */ + SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x31,0xbf); + } + /* From 1.10.7w, not in 1.10.8r */ + if(!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & (DriverMode >> 8))) { + /* Reset LCDA setting */ + SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x38,0xfc); + } + if(IS_SIS650) { + if(SiS_Pr->SiS_UseLCDA) { + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x5f) & 0xF0) { + if((ModeNo <= 0x13) || (!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & (DriverMode >> 8)))) { + SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x38,(EnableDualEdge | SetToLCDA)); /* 3 */ + } } - } - if((temp & (EnableDualEdge | SetToLCDA)) - == (EnableDualEdge | SetToLCDA)) /* TW: BIOS only tests these bits, added "& ..." */ + } +#if 0 /* We can't detect it this way; there are machines which do not use LCDA despite + * the chip revision + */ + if((tempbx & SetCRT2ToLCD) && (SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30) & SetCRT2ToLCD)) { + if((SiS_GetReg1(SiS_Pr->SiS_P3d4, 0x36) & 0x0f) == SiS_Pr->SiS_Panel1400x1050) { + if((ModeNo <= 0x13) || (!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & (DriverMode >> 8)))) { + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x5f) & 0xF0) { + SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x38,(EnableDualEdge | SetToLCDA)); /* 3 */ + } + } + } else { + if((ModeNo <= 0x13) || (!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & (DriverMode >> 8)))) { + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x5f) & 0xF0) { + SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x38,(EnableDualEdge | SetToLCDA)); /* 3 */ + } + } + } + } +#endif + } + temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); + if((temp & (EnableDualEdge | SetToLCDA)) == (EnableDualEdge | SetToLCDA)) { tempbx |= SetCRT2ToLCDA; + } } - /* TW: Inserted from 650/LVDS 1.10.07 BIOS: */ + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); if(temp & SetToLCDA) - tempbx |= SetCRT2ToLCDA; - if(temp & EnableLVDSHiVision) - tempbx |= SetCRT2ToHiVisionTV; + tempbx |= SetCRT2ToLCDA; + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if(temp & EnableLVDSHiVision) + tempbx |= SetCRT2ToHiVisionTV; + } } } -#endif + +#endif /* SIS315H */ + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - temp = SetCRT2ToLCDA | SetCRT2ToSCART | SetCRT2ToLCD | - SetCRT2ToRAMDAC | SetCRT2ToSVIDEO | SetCRT2ToAVIDEO; /* = 0x807C; */ - if(SiS_Pr->SiS_IF_DEF_HiVision == 1) - temp |= SetCRT2ToHiVisionTV; /* = 0x80FC; */ + temp = SetCRT2ToLCDA | SetCRT2ToSCART | SetCRT2ToLCD | + SetCRT2ToRAMDAC | SetCRT2ToSVIDEO | SetCRT2ToAVIDEO | /* = 0x807C; */ + SetCRT2ToHiVisionTV; /* = 0x80FC; */ } else { if(HwDeviceExtension->jChipType >= SIS_315H) { if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) @@ -3937,23 +4491,25 @@ tempbx = 0; } - if(SiS_Pr->SiS_IF_DEF_LVDS==0) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { if(tempbx & SetCRT2ToLCDA) { tempbx &= (0xFF00|SwitchToCRT2|SetSimuScanMode); - } else if(tempbx & SetCRT2ToRAMDAC) { + } + if(tempbx & SetCRT2ToRAMDAC) { tempbx &= (0xFF00|SetCRT2ToRAMDAC|SwitchToCRT2|SetSimuScanMode); - } else if((tempbx & SetCRT2ToLCD) && (!(SiS_Pr->SiS_VBType & VB_NoLCD)) ){ + } + if((tempbx & SetCRT2ToLCD) /* && (!(SiS_Pr->SiS_VBType & VB_NoLCD)) */ ) { + /* We initialize the Panel Link of the type of bridge is DH */ tempbx &= (0xFF00|SetCRT2ToLCD|SwitchToCRT2|SetSimuScanMode); - } else if(tempbx & SetCRT2ToSCART){ + } + if(tempbx & SetCRT2ToSCART) { tempbx &= (0xFF00|SetCRT2ToSCART|SwitchToCRT2|SetSimuScanMode); tempbx |= SetPALTV; - } -#if 0 /* TW: Not done in 650/301LVx 1.10.6s BIOS */ - } else if(tempbx & SetCRT2ToHiVisionTV){ + } + if(tempbx & SetCRT2ToHiVisionTV) { tempbx &= (0xFF00|SetCRT2ToHiVisionTV|SwitchToCRT2|SetSimuScanMode); tempbx |= SetPALTV; } -#endif } else { /* LVDS */ if(HwDeviceExtension->jChipType >= SIS_315H) { if(tempbx & SetCRT2ToLCDA) @@ -3971,57 +4527,54 @@ tempbx |= SetCRT2ToLCD; } } + if(tempax & DisableCRT2Display) { if(!(tempbx & (SwitchToCRT2 | SetSimuScanMode))) { tempbx = SetSimuScanMode | DisableCRT2Display; } } + if(!(tempbx & DriverMode)){ tempbx |= SetSimuScanMode; } - /* TW: LVDS (LCD/TV) and 630+301B (LCD) can only be slave in 8bpp modes */ - if( (SiS_Pr->SiS_IF_DEF_LVDS == 1) && (SiS_Pr->SiS_ModeType <= ModeVGA) ) { - modeflag &= (~CRT2Mode); - } - if( (HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { - if(SiS_Pr->SiS_ModeType <= ModeVGA) { - if(tempbx & SetCRT2ToLCD) { - modeflag &= (~CRT2Mode); - } - } + /* TW: LVDS (LCD/TV) and 301BDH (LCD) can only be slave in 8bpp modes */ + if(SiS_Pr->SiS_ModeType <= ModeVGA) { + if( (SiS_Pr->SiS_IF_DEF_LVDS == 1) || + ((tempbx & SetCRT2ToLCD) && (SiS_Pr->SiS_VBType & VB_NoLCD)) ) { + modeflag &= (~CRT2Mode); + } } - /* TW end */ - + if(!(tempbx & SetSimuScanMode)){ - if(tempbx & SwitchToCRT2) { - if(!(modeflag & CRT2Mode)) { - if( (HwDeviceExtension->jChipType >= SIS_315H) && - (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) ) { - if(resinfo != 0x0a) - tempbx |= SetSimuScanMode; - } else { - tempbx |= SetSimuScanMode; - } - } - } else { - if(!(SiS_BridgeIsEnable(SiS_Pr,BaseAddr,HwDeviceExtension))) { - if(!(tempbx & DriverMode)) { - if(SiS_BridgeInSlave(SiS_Pr)) { - tempbx |= SetSimuScanMode; /* TW: from BIOS 650/301/301LV/LVDS */ - } - } - } - } + if(tempbx & SwitchToCRT2) { + if((!(modeflag & CRT2Mode)) && (checkcrt2mode)) { + if( (HwDeviceExtension->jChipType >= SIS_315H) && + (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) ) { + if(resinfo != 0x0a) + tempbx |= SetSimuScanMode; + } else { + tempbx |= SetSimuScanMode; + } + } + } else { + if(!(SiS_BridgeIsEnable(SiS_Pr,BaseAddr,HwDeviceExtension))) { + if(!(tempbx & DriverMode)) { + if(SiS_BridgeInSlave(SiS_Pr)) { + tempbx |= SetSimuScanMode; + } + } + } + } } if(!(tempbx & DisableCRT2Display)) { if(tempbx & DriverMode) { if(tempbx & SetSimuScanMode) { - if(!(modeflag & CRT2Mode)) { + if((!(modeflag & CRT2Mode)) && (checkcrt2mode)) { if( (HwDeviceExtension->jChipType >= SIS_315H) && - (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) ) { - if(resinfo != 0x0a) { /* TW: Inserted from 650/301 BIOS */ + (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) ) { + if(resinfo != 0x0a) { /* TW: 650/301 BIOS */ tempbx |= SetInSlaveMode; if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { if(tempbx & SetCRT2ToTV) { @@ -4029,7 +4582,7 @@ SiS_Pr->SiS_SetFlag |= TVSimuMode; } } - } /* TW: Inserted from 650/301 BIOS */ + } /* TW: 650/301 BIOS */ } else { tempbx |= SetInSlaveMode; if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { @@ -4066,9 +4619,9 @@ } } - if(SiS_Pr->SiS_IF_DEF_LVDS==0) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { #ifdef SIS300 - if((HwDeviceExtension->jChipType==SIS_630)|| + if((HwDeviceExtension->jChipType==SIS_630) || (HwDeviceExtension->jChipType==SIS_730)) { if(ROMAddr && SiS_Pr->SiS_UseROM) { OutputSelect = ROMAddr[0xfe]; @@ -4078,9 +4631,7 @@ if(tempbx & SetCRT2ToTV) { if(tempbx & SetPALTV) { temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x35); - /* temp &= 0xC0; */ /* TW: BIOS only tests 0x40, not 0x80 */ - if(temp & 0x40) - tempbx &= (~SetPALTV); + if(temp & EnablePALM) tempbx &= (~SetPALTV); } } } @@ -4088,29 +4639,49 @@ #ifdef SIS315H if(HwDeviceExtension->jChipType >= SIS_315H) { if(ROMAddr && SiS_Pr->SiS_UseROM) { - OutputSelect = ROMAddr[0xf3]; + OutputSelect = ROMAddr[0xf3]; + if(HwDeviceExtension->jChipType == SIS_330) { + OutputSelect = ROMAddr[0x11b]; + } } if(!(OutputSelect & EnablePALMN)) SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x38,0x3F); if(tempbx & SetCRT2ToTV) { if(tempbx & SetPALTV) { temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(temp & 0x40) - tempbx &= (~SetPALTV); + if(temp & EnablePALM) tempbx &= (~SetPALTV); } } } #endif } + + /* PALM/PALN on Chrontel 7019 */ + SiS_Pr->SiS_CHPALM = SiS_Pr->SiS_CHPALN = FALSE; + if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { + if(tempbx & SetCRT2ToTV) { + if(tempbx & SetPALTV) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); + if(temp & EnablePALM) SiS_Pr->SiS_CHPALM = TRUE; + else if(temp & EnablePALN) SiS_Pr->SiS_CHPALN = TRUE; + } + } + } + + SiS_Pr->SiS_VBInfo = tempbx; - SiS_Pr->SiS_VBInfo=tempbx; + if(HwDeviceExtension->jChipType == SIS_630) { + SiS_WhatIsThis(SiS_Pr, SiS_Pr->SiS_VBInfo); + } #ifdef TWDEBUG #ifdef LINUX_KERNEL - printk(KERN_INFO "sisfb: (VBInfo= 0x%04x, SetFlag=0x%04x)\n", SiS_Pr->SiS_VBInfo, SiS_Pr->SiS_SetFlag); + printk(KERN_DEBUG "sisfb: (VBInfo= 0x%04x, SetFlag=0x%04x)\n", + SiS_Pr->SiS_VBInfo, SiS_Pr->SiS_SetFlag); #endif #ifdef LINUX_XF86 - xf86DrvMsg(0, X_PROBED, "(init301: VBInfo=0x%04x, SetFlag=0x%04x)\n", SiS_Pr->SiS_VBInfo, SiS_Pr->SiS_SetFlag); + xf86DrvMsgVerb(0, X_PROBED, 3, "(init301: VBInfo=0x%04x, SetFlag=0x%04x)\n", + SiS_Pr->SiS_VBInfo, SiS_Pr->SiS_SetFlag); #endif #endif @@ -4128,7 +4699,7 @@ SiS_SetRegAND(SiS_Pr->SiS_P3d4, 0x31, 0xF7); if(ModeNo == 0x13) bp+4 = 0x03; } else { - /* From 650/301LVx BIOS: */ + /* From 650/30xLV BIOS: */ SiS_SetRegAND(SiS_Pr->SiS_P3d4, 0x31, 0xF7); if(ModeNo == 0x13) bp+4 = 0x03; else bp+4 = ModeNo; @@ -4140,14 +4711,48 @@ } void +SiS_WhatIsThis(SiS_Private *SiS_Pr, USHORT myvbinfo) +{ + unsigned long eax, temp; + unsigned short temp1; + + if(!(SiS_Pr->SiS_ChSW)) return; + +#ifndef LINUX_XF86 + SiS_SetReg4(0xcf8,0x80000874); + eax = SiS_GetReg3(0xcfc); +#else + eax = pciReadLong(0x00000800, 0x74); +#endif + eax &= 0xFFFF; + temp = eax; + eax += 0x3c; + temp1 = SiS_GetReg4((USHORT)eax); + temp1 &= 0xFEFF; + SiS_SetReg5((USHORT)eax, temp1); + temp1 = SiS_GetReg4((USHORT)eax); + eax = temp; + eax += 0x3a; + temp1 = SiS_GetReg4((USHORT)eax); + temp1 &= 0xFEFF; + if(!(myvbinfo & SetCRT2ToTV)) { + temp1 |= 0x0100; + } + SiS_SetReg5((USHORT)eax, temp1); + temp1 = SiS_GetReg4((USHORT)eax); +} + +void SiS_GetRAMDAC2DATA(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) { - USHORT tempax,tempbx,temp; - USHORT temp1,temp2,modeflag=0,tempcx; + USHORT tempax=0,tempbx=0; + USHORT temp1=0,modeflag=0,tempcx=0; USHORT StandTableIndex,CRT1Index; - USHORT ResInfo,DisplayType; +#ifdef SIS315H + USHORT ResInfo,DisplayType,temp=0; const SiS_LVDSCRT1DataStruct *LVDSCRT1Ptr = NULL; +#endif SiS_Pr->SiS_RVBHCMAX = 1; SiS_Pr->SiS_RVBHCFACT = 1; @@ -4158,12 +4763,13 @@ StandTableIndex = SiS_GetModePtr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex); tempax = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[0]; tempbx = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[6]; - temp1 = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[7]; + temp1 = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[7]; } else { if( (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) ) { +#ifdef SIS315H temp = SiS_GetLVDSCRT1Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, RefreshRateTableIndex,&ResInfo,&DisplayType); @@ -4205,33 +4811,36 @@ case 37: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_1_H; break; case 38: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_2; break; case 39: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_2_H; break; + case 99: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1OPAL; break; + default: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_1; break; } - temp1 = (LVDSCRT1Ptr+ResInfo)->CR[0]; - temp2 = (LVDSCRT1Ptr+ResInfo)->CR[14]; - tempax = (temp1 & 0xFF) | ((temp2 & 0x03) << 8); + tempax = (LVDSCRT1Ptr+ResInfo)->CR[0]; + tempax |= (LVDSCRT1Ptr+ResInfo)->CR[14] << 8; + tempax &= 0x03FF; tempbx = (LVDSCRT1Ptr+ResInfo)->CR[6]; - tempcx = (LVDSCRT1Ptr+ResInfo)->CR[13]<<8; - tempcx = tempcx & 0x0100; - tempcx = tempcx << 2; - tempbx = tempbx | tempcx; - temp1 = (LVDSCRT1Ptr+ResInfo)->CR[7]; + tempcx = (LVDSCRT1Ptr+ResInfo)->CR[13] << 8; + tempcx &= 0x0100; + tempcx <<= 2; + tempbx |= tempcx; + temp1 = (LVDSCRT1Ptr+ResInfo)->CR[7]; +#endif } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; CRT1Index = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC; - if(HwDeviceExtension->jChipType < SIS_315H) { - CRT1Index &= 0x3F; - } - temp1 = (USHORT)SiS_Pr->SiS_CRT1Table[CRT1Index].CR[0]; - temp2 = (USHORT)SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14]; - tempax = (temp1 & 0xFF) | ((temp2 & 0x03) << 8); - tempbx = (USHORT)SiS_Pr->SiS_CRT1Table[CRT1Index].CR[6]; - tempcx = (USHORT)SiS_Pr->SiS_CRT1Table[CRT1Index].CR[13]<<8; - tempcx = tempcx & 0x0100; - tempcx = tempcx << 2; - tempbx = tempbx | tempcx; - temp1 = (USHORT)SiS_Pr->SiS_CRT1Table[CRT1Index].CR[7]; +#if 0 /* Not any longer */ + if(HwDeviceExtension->jChipType < SIS_315H) CRT1Index &= 0x3F; +#endif + tempax = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[0]; + tempax |= SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14] << 8; + tempax &= 0x03FF; + tempbx = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[6]; + tempcx = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[13] << 8; + tempcx &= 0x0100; + tempcx <<= 2; + tempbx |= tempcx; + temp1 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[7]; } @@ -4239,15 +4848,18 @@ if(temp1 & 0x01) tempbx |= 0x0100; if(temp1 & 0x20) tempbx |= 0x0200; + tempax += 5; /* Charx8Dot is no more used (and assumed), so we set it */ - modeflag |= Charx8Dot; + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + modeflag |= Charx8Dot; + } if(modeflag & Charx8Dot) tempax *= 8; else tempax *= 9; - /* TW: From 650/301LVx 1.10.6s */ + /* TW: From 650/30xLV 1.10.6s */ if(modeflag & HalfDCLK) tempax <<= 1; SiS_Pr->SiS_VGAHT = SiS_Pr->SiS_HT = tempax; @@ -4283,7 +4895,10 @@ void SiS_DisableBridge(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr) { - USHORT tempah,pushax,temp=0; +#ifdef SIS315H + USHORT tempah,pushax=0,modenum; +#endif + USHORT temp=0; UCHAR *ROMAddr = HwDeviceExtension->pjVirtualRomBase; if (SiS_Pr->SiS_IF_DEF_LVDS == 0) { @@ -4292,168 +4907,315 @@ if(HwDeviceExtension->jChipType < SIS_315H) { - /* 300 series */ - - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { - SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x08); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - } - if(SiS_Is301B(SiS_Pr,BaseAddr)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0x3f); - SiS_ShortDelay(SiS_Pr,1); - } - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); - SiS_DisplayOff(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension,BaseAddr); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x01,0x80); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x02,0x40); - if( (!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) || - (!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) ) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xFB,0x04); - } - - } else { - - /* 310 series */ +#ifdef SIS300 /* 300 series */ -#ifdef SIS650301NEW /* From 650/301LVx 1.10.6s */ - if(!(SiS_IsM650or651(SiS_Pr,HwDeviceExtension, BaseAddr))) { - tempah = 0xef; - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - tempah = 0xf7; - } - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } + if(HwDeviceExtension->jChipType == SIS_300) { /* New for 300+301LV */ - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + } + } + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0x3f); + SiS_ShortDelay(SiS_Pr,1); + } + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); + SiS_DisplayOff(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + if( (!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) || + (!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) ) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xFB,0x04); + } - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); - } else if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); - } + } else { - SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x08); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + } + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0x3f); + SiS_ShortDelay(SiS_Pr,1); + } + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); + SiS_DisplayOff(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension,BaseAddr); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x01,0x80); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x02,0x40); + if( (!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) || + (!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) ) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xFB,0x04); + } + } - pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); +#endif /* SIS300 */ - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + } else { - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { +#ifdef SIS315H /* 310/325 series */ - SiS_DisplayOff(SiS_Pr); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); + if(IS_SIS650740) { /* 650, 740 */ - } else { +#if 0 + if(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x00) != 1) return; /* From 1.10.7w */ +#endif - SiS_DisplayOff(SiS_Pr); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + modenum = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x34); - } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + + if( (modenum <= 0x13) || + (!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) || + (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) ) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); + } + SiS_DDC2Delay(SiS_Pr,0xff00); + SiS_DDC2Delay(SiS_Pr,0x6000); + SiS_DDC2Delay(SiS_Pr,0x8000); + + SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); + + pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); + + if(IS_SIS740) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); + } + + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + + if(!(SiS_IsNotM650or651(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0xef; + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0xf7; + } + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,tempah); + } - tempah = 0x3f; - if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { - tempah = 0x7f; - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - tempah = 0xbf; } - } - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + tempah = 0x3f; + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x7f; + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0xbf; + } + } + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); + } - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); - } + if((SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || + ((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13))) { - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) { - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - } - } - } + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); + SiS_DisplayOff(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + } else { + SiS_DisplayOff(SiS_Pr); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); + if((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13)) { + SiS_DisplayOff(SiS_Pr); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + } + } - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); + } else { + + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); + SiS_DisplayOff(SiS_Pr); + } + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + } else { + SiS_DisplayOff(SiS_Pr); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + } -#else /* TW: From 650/301LV BIOS */ + } - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - } else if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - /* TW: 301B dependent code starts here in 650/301LV BIOS */ - if(SiS_Is301B(SiS_Pr,BaseAddr)) { - tempah = 0x3f; - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); - } + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,~0x10); /* 1.10.8r */ + + tempah = 0x3f; + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x7f; + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0xbf; + } + } + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); - SiS_DisplayOff(SiS_Pr); + if(SiS_IsNotM650or651(SiS_Pr,HwDeviceExtension, BaseAddr)) { /* 1.10.8r */ + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + } /* 1.10.8r */ + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); + } - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) { + if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); + } + } + } - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); - temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + } - /* TW: Inserted from 650/301LV BIOS */ - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 4); - } else if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 4); - } - } - } else if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) { - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 4); - } else if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 4); - } else if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 2); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 4); - } - } - } /* TW: 650/301LV end */ -#endif +#if 0 + } else if(IS_SIS740) { /* 740 */ + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { /* 30xLV */ + + if( (!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) || + (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) ) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); + } + + SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); + + pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); + + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); + + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_DisplayOff(SiS_Pr); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); + } else { + SiS_DisplayOff(SiS_Pr); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + } + + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,0x3F); + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,0xEF); /* (from 650) */ + + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); + } + + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) { + if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); + } + } + } + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); + + } else { /* (301,) 301B */ + + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,0x3F); + } + + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); + SiS_DisplayOff(SiS_Pr); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + + } +#endif + } else { /* 315, 330 - all bridge types */ + + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + tempah = 0x3f; + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x7f; + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0xbf; + } + } + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_DisplayOff(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + } + } + if( (!(SiS_Is301B(SiS_Pr,BaseAddr))) || + (!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) ) { + + if( (!(SiS_Is301B(SiS_Pr,BaseAddr))) || + (!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) ) { + + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); + SiS_DisplayOff(SiS_Pr); + + } + + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x00,temp); + + } + + } /* 315/330 */ + +#endif /* SIS315H */ } } else { /* ============ TW: For 301 ================ */ if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF0,0x0B); SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 1); } @@ -4483,24 +5245,26 @@ if(HwDeviceExtension->jChipType < SIS_315H) { - /* 300 series */ +#ifdef SIS300 /* 300 series */ if(SiS_Pr->SiS_IF_DEF_CH70xx == 1) { -#if 0 /* TW: Implemented this for power saving, but it's not worth - * the problems - */ - if(SiS_Pr->SiS_Backup70xx == 0xff) { - SiS_Pr->SiS_Backup70xx = SiS_GetCH700x(SiS_Pr,0x0e); - } -#endif SiS_SetCH700x(SiS_Pr,0x090E); } - if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x11) & 0x08)) { - - if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { + if(HwDeviceExtension->jChipType == SIS_730) { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x11) & 0x08)) { + SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); + } + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x08); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + } + } else { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x11) & 0x08)) { - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { + + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); @@ -4510,8 +5274,9 @@ SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x08); SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - } - } + } + } + } } SiS_DisplayOff(SiS_Pr); @@ -4523,17 +5288,31 @@ SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x01,0x80); SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x02,0x40); - if( (!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) || + if( (!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) || (!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) ) { SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xFB,0x04); } +#endif /* SIS300 */ + } else { - /* 310 series */ +#ifdef SIS315H /* 310/325 series */ - if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + temp = SiS_GetCH701x(SiS_Pr,0x61); + if(temp < 1) { + SiS_SetCH701x(SiS_Pr,0xac76); + SiS_SetCH701x(SiS_Pr,0x0066); + } + + if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetCH701x(SiS_Pr,0x3e49); + } else if(SiS_IsTVOrYPbPrOrScart(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_SetCH701x(SiS_Pr,0x3e49); + } + if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_Chrontel701xBLOff(SiS_Pr); SiS_Chrontel701xOff(SiS_Pr); @@ -4541,21 +5320,25 @@ SiS_Chrontel701xBLOff(SiS_Pr); SiS_Chrontel701xOff(SiS_Pr); } + + } - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetCH701x(SiS_Pr,0x0149); - } else if(SiS_IsTVOrYPbPr(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetCH701x(SiS_Pr,0x0149); - } + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x08); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); } - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + SiS_DisplayOff(SiS_Pr); + } else if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_DisplayOff(SiS_Pr); - } else if(!(SiS_IsTVOrYPbPr(SiS_Pr,HwDeviceExtension, BaseAddr))) { + } else if(!(SiS_IsTVOrYPbPrOrScart(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_DisplayOff(SiS_Pr); } - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + } else if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); } else if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); @@ -4563,25 +5346,35 @@ SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + } else if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); } else if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); } - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + if(SiS_CRT2IsLCD(SiS_Pr, BaseAddr,HwDeviceExtension)) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1e,0xdf); + } + } else if(SiS_IsLCDOrLCDA(SiS_Pr,HwDeviceExtension, BaseAddr)) { SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1e,0xdf); } - if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x13,0xff); - } else { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x13,0xfb); + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x13,0xff); + } else { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x13,0xfb); + } } SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); - if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0xf7); + } else if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0xf7); } else if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0xf7); @@ -4596,6 +5389,16 @@ } } #endif + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + if(SiS_CRT2IsLCD(SiS_Pr, BaseAddr,HwDeviceExtension)) { + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xFB,0x04); + } + } + } + +#endif /* SIS315H */ } /* 310 series */ @@ -4607,7 +5410,11 @@ void SiS_EnableBridge(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { - USHORT temp=0,tempah,pushax,temp1; + USHORT temp=0,tempah; +#ifdef SIS315H + USHORT temp1,pushax=0; + BOOLEAN delaylong = FALSE; +#endif UCHAR *ROMAddr = HwDeviceExtension->pjVirtualRomBase; if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { @@ -4616,216 +5423,388 @@ if(HwDeviceExtension->jChipType < SIS_315H) { - /* 300 series */ +#ifdef SIS300 /* 300 series */ + + if(HwDeviceExtension->jChipType == SIS_300) { - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x11,0xFB); - if(!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) { - SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 0); + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); + if(!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) { + SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 0); + } + } } - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* Enable CRT2 */ -/* DoSomeThingPCI_On(SiS_Pr) */ - SiS_DisplayOn(SiS_Pr); - SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0xBF); - if(SiS_BridgeInSlave(SiS_Pr)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x01,0x1F); - } else { - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0x1F,0x40); + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; } - if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { - if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); - } - SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); - SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x00); - } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); + SiS_DisplayOn(SiS_Pr); + } else { + SiS_VBLongWait(SiS_Pr); + SiS_DisplayOn(SiS_Pr); + SiS_VBLongWait(SiS_Pr); + } + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); + } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfe,0x01); + } + } + } } - } else { - temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; - } - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ - if(SiS_Is301B(SiS_Pr,BaseAddr)) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); - SiS_DisplayOn(SiS_Pr); - } else { - SiS_VBLongWait(SiS_Pr); - SiS_DisplayOn(SiS_Pr); - SiS_VBLongWait(SiS_Pr); - } - } - } else { + } else { - /* 310 series */ + if((SiS_Pr->SiS_VBType & VB_NoLCD) && + (SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) { + /* This is only for LCD output on 301B-DH via LVDS */ + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x11,0xFB); + if(!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) { + SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 0); + } + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* Enable CRT2 */ +/* DoSomeThingPCI_On(SiS_Pr) */ + SiS_DisplayOn(SiS_Pr); + SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0xBF); + if(SiS_BridgeInSlave(SiS_Pr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x01,0x1F); + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0x1F,0x40); + } + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { + if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); + } + SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF7,0x00); + } + } + } else { + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); + SiS_DisplayOn(SiS_Pr); + } else { + SiS_VBLongWait(SiS_Pr); + SiS_DisplayOn(SiS_Pr); + SiS_VBLongWait(SiS_Pr); + } + } -#ifdef SIS650301NEW /* TW: From 650/301LVx 1.10.6s */ + } +#endif /* SIS300 */ - if(!(SiS_IsM650or651(SiS_Pr,HwDeviceExtension, BaseAddr))) { - tempah = 0x10; - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - tempah = 0x08; - } - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } + } else { - SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); +#ifdef SIS315H /* 310/325 series */ - SiS_DisplayOff(SiS_Pr); + if(IS_SIS650740) { /* 650 */ - pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); +#if 0 + if(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x00) != 1) return; /* From 1.10.7w */ +#endif - if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || - (SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) ) { - if(!(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); - } - } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0xef); /* 1.10.7u */ + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); /* 1.10.7u */ + + if(!(IS_SIS740)) { + if(!(SiS_IsNotM650or651(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0x10; + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x08; + } + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x4c,tempah); + } + } - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; - } - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); + SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); + SiS_DisplayOff(SiS_Pr); + pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); + if(IS_SIS740) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); + } - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); - } + if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || + (SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) ) { + if(!(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 2); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 2); + } + } - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1e,0x20); - } + if(!(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x40)) { + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 10); + delaylong = TRUE; + } - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); + } - tempah = 0xc0; - if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { - tempah = 0x80; - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - tempah = 0x40; - } - } - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ + + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2e); + if(!(temp & 0x80)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + } + } else { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + } + } - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || - ((SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) ) { - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); - SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfe,0x01); + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1e,0x20); } - } - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); - SiS_DisplayOn(SiS_Pr); - SiS_SetReg3(SiS_Pr->SiS_P3c6,0xff); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); - } + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2e); + if(!(temp & 0x80)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + } + } - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x0c); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x31,0x05); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x32,0x60); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x33,0x00); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x40); - -#else /* TW: From 650/301LV BIOS (different PanelDelay!) */ - - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfd,0x02); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 0); - } else if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfd,0x02); - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 0); - } - /* TW: --- end --- */ + tempah = 0xc0; + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x80; + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0x40; + } + } + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); - if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; - } - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + } - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); /* All this from 1.10.7u */ + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x0c); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x31,0x05); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x32,0x60); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x33,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x40); + + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1f,0x10); /* 1.10.8r */ -/* SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2E,0x7F); */ /* TW: Not done in 650/301LV BIOS */ - temp=SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2E); - if (!(temp & 0x80)) - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); - } + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || + ((SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) ) { + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 10); + if(delaylong) { + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 10); + } + SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfe,0x01); + } + } - if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); + SiS_DisplayOn(SiS_Pr); + SiS_SetReg3(SiS_Pr->SiS_P3c6,0xff); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xc0); + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + } +#if 0 + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x0c); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x31,0x05); /* 1.10.8r: 0x0d */ + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x32,0x60); /* 1.10.8r: 0x70 */ + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x33,0x00); /* 1.10.8r: 0x40 */ + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x40); +#endif - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) /* TW: "if" new from 650/301LV BIOS */ - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7F); + } - } else { +#if 0 + } else if(IS_SIS740) { /* 740 */ + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { /* 30xLV */ + + SiS_SetReg3(SiS_Pr->SiS_P3c6,0x00); + SiS_DisplayOff(SiS_Pr); + pushax = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x06); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); + + if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || + (SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) ) { + if(!(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 3); + } + } + + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); - SiS_VBLongWait(SiS_Pr); - SiS_DisplayOn(SiS_Pr); - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { /* TW: "if" new from 650/301LV BIOS */ - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7F); - SiS_VBLongWait(SiS_Pr); - } + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 2); + } + + if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1E,0x20); + } + + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0x10); /* (taken from 650 1.10.8r) */ + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); + + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || + (SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) ) { + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 10); + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x40) { + SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); + } + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xfe,0x01); + SiS_SetPanelDelayLoop(SiS_Pr,ROMAddr, HwDeviceExtension, 3, 10); + SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); + } + } + + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x06,pushax); + SiS_DisplayOn(SiS_Pr); + SiS_SetReg3(SiS_Pr->SiS_P3c6,0xff); + + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + } + + } else { /* (301), 301B */ + + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); - } + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ - /* TW: Entire section from 650/301LV BIOS */ - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { -/* if (!(SiS_WeHaveBacklightCtrl(HwDeviceExtension, BaseAddr))) { */ /* TW: BIOS code makes no sense */ - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); - SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x01); -/* } */ - } else if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { -/* if (!(SiS_WeHaveBacklightCtrl(HwDeviceExtension, BaseAddr))) { */ /* TW: BIOS code makes no sense */ - SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); - SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x01); -/* } */ - } - } + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2E); + if(!(temp & 0x80)) + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); + } + + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + } else { + SiS_VBLongWait(SiS_Pr); + SiS_DisplayOn(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7F); + SiS_VBLongWait(SiS_Pr); + } + + } #endif + + } else { /* 315, 330 */ + + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + temp = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x32) & 0xDF; + if(SiS_BridgeInSlave(SiS_Pr)) { + tempah = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + } + SiS_SetReg1(SiS_Pr->SiS_P3c4,0x32,temp); + + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ + + temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2E); + if(!(temp & 0x80)) + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); + } + + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); + + if(SiS_Is301B(SiS_Pr,BaseAddr)) { + + temp=SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2E); + if (!(temp & 0x80)) + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); + + tempah = 0xc0; + if(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr)) { + tempah = 0x80; + if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { + tempah = 0x40; + } + } + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); + + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + + } else { + + SiS_VBLongWait(SiS_Pr); + SiS_DisplayOn(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7F); + SiS_VBLongWait(SiS_Pr); + + } + + } /* 315, 330 */ + +#endif /* SIS315H */ } } else { /* ============ TW: For 301 ================ */ if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF0,0x0B); SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 0); } @@ -4856,7 +5835,7 @@ SiS_VBLongWait(SiS_Pr); if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 1); SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,0xF0,0x03); } @@ -4868,9 +5847,14 @@ if(HwDeviceExtension->jChipType < SIS_315H) { - /* 300 series */ +#ifdef SIS300 /* 300 series */ - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + if(HwDeviceExtension->jChipType == SIS_730) { + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); + SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 1); + } SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x11,0xFB); if(!(SiS_CR36BIOSWord23d(SiS_Pr,HwDeviceExtension))) { SiS_SetPanelDelay(SiS_Pr,ROMAddr, HwDeviceExtension, 0); @@ -4888,20 +5872,12 @@ } if(SiS_Pr->SiS_IF_DEF_CH70xx == 1) { - if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) { -#if 0 /* TW: Implemented this for power saving, but it's not worth - * the problems - */ - if(SiS_Pr->SiS_Backup70xx != 0xff) { - SiS_SetCH700x(SiS_Pr,((SiS_Pr->SiS_Backup70xx<<8)|0x0E)); - SiS_Pr->SiS_Backup70xx = 0xff; - } else -#endif + if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) { SiS_SetCH700x(SiS_Pr,0x0B0E); } } - if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { if(!(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwDeviceExtension))) { @@ -4914,9 +5890,11 @@ } } +#endif /* SIS300 */ + } else { - /* 310 series */ +#ifdef SIS315H /* 310/325 series */ #if 0 /* BIOS code makes no sense */ if(SiS_IsVAMode()) { @@ -4925,6 +5903,13 @@ } #endif + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x11,0xFB); + SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 0); + } + } + SiS_EnableCRT2(SiS_Pr); SiS_UnLockCRT2(SiS_Pr,HwDeviceExtension, BaseAddr); @@ -4937,6 +5922,14 @@ } SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + +#ifdef NEWCH701x + if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { + if(SiS_IsLCDOrLCDA(SiS_Pr,HwDeviceExtension,BaseAddr)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1E,0x20); + } + } +#endif temp1 = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x2E); if (!(temp1 & 0x80)) @@ -4948,9 +5941,16 @@ } } - if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1E,0x20); + } + } +#ifndef NEWCH701x + else if(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) { SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1E,0x20); } +#endif if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); @@ -4958,7 +5958,7 @@ if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { - if(SiS_IsTVOrYPbPr(SiS_Pr,HwDeviceExtension, BaseAddr)) { + if(SiS_IsTVOrYPbPrOrScart(SiS_Pr,HwDeviceExtension, BaseAddr)) { SiS_Chrontel701xOn(SiS_Pr,HwDeviceExtension, BaseAddr); } @@ -4976,14 +5976,21 @@ SiS_Chrontel701xBLOn(SiS_Pr); SiS_ChrontelDoSomething4(SiS_Pr,HwDeviceExtension, BaseAddr); } else if(SiS_IsLCDOrLCDA(SiS_Pr,HwDeviceExtension, BaseAddr)) { -/* if(!SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr)) { */ /* TW: makes no sense */ - SiS_Chrontel701xBLOn(SiS_Pr); - SiS_ChrontelDoSomething4(SiS_Pr,HwDeviceExtension, BaseAddr); -/* } */ + SiS_Chrontel701xBLOn(SiS_Pr); + SiS_ChrontelDoSomething4(SiS_Pr,HwDeviceExtension, BaseAddr); } } + } else if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + if(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) { + SiS_SetPanelDelay(SiS_Pr, ROMAddr, HwDeviceExtension, 1); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x11,0xF7); + } + } } +#endif /* SIS315H */ + } /* 310 series */ } /* LVDS */ @@ -4995,9 +6002,9 @@ { USHORT BaseAddr = (USHORT)HwDeviceExtension->ulIOAddress; - /* TW: Switch on LCD backlight on SiS30x */ + /* TW: Switch on LCD backlight on SiS30xLV */ if( (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) || - (SiS_CRT2IsLCD(SiS_Pr,BaseAddr)) ) { + (SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension)) ) { if(!(SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); SiS_WaitVBRetrace(SiS_Pr,HwDeviceExtension); @@ -5013,14 +6020,14 @@ { USHORT BaseAddr = (USHORT)HwDeviceExtension->ulIOAddress; - /* TW: Switch off LCD backlight on SiS30x */ + /* TW: Switch off LCD backlight on SiS30xLV */ if( (!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) || (SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr)) ) { SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFE,0x00); } if(!(SiS_IsVAMode(SiS_Pr,HwDeviceExtension, BaseAddr))) { - if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr))) { + if(!(SiS_CRT2IsLCD(SiS_Pr,BaseAddr,HwDeviceExtension))) { if(!(SiS_IsDualEdge(SiS_Pr,HwDeviceExtension, BaseAddr))) { SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x26,0xFD,0x00); } @@ -5065,13 +6072,28 @@ } void +SiS_SetPanelDelayLoop(SiS_Private *SiS_Pr, UCHAR *ROMAddr, PSIS_HW_DEVICE_INFO HwDeviceExtension, + USHORT DelayTime, USHORT DelayLoop) +{ + int i; + for(i=0; ijChipType < SIS_315H) { +#ifdef SIS300 + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* 300 series, LVDS */ PanelID = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x36); @@ -5132,11 +6154,41 @@ } +#endif /* SIS300 */ + } else { + if(HwDeviceExtension->jChipType == SIS_330) return; + +#ifdef SIS315H + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* 310/325 series, LVDS */ - /* TW: Not currently used */ + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { + PanelID = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x36); + DelayIndex = PanelID >> 4; + if((DelayTime >= 2) && ((PanelID & 0x0f) == 1)) { + Delay = 3; + } else { + if(DelayTime >= 2) DelayTime -= 2; + + if(!(DelayTime & 0x01)) { + Delay = SiS_Pr->SiS_PanelDelayTblLVDS[DelayIndex].timer[0]; + } else { + Delay = SiS_Pr->SiS_PanelDelayTblLVDS[DelayIndex].timer[1]; + } + if((ROMAddr) && (SiS_Pr->SiS_UseROM)) { + if(ROMAddr[0x13c] & 0x40) { + if(!(DelayTime & 0x01)) { + Delay = (USHORT)ROMAddr[0x17e]; + } else { + Delay = (USHORT)ROMAddr[0x17f]; + } + } + } + } + SiS_ShortDelay(SiS_Pr,Delay); + } } else { /* 310/325 series, 301(B) */ @@ -5151,6 +6203,8 @@ } +#endif /* SIS315H */ + } } @@ -5197,10 +6251,14 @@ } BOOLEAN -SiS_CRT2IsLCD(SiS_Private *SiS_Pr, USHORT BaseAddr) +SiS_CRT2IsLCD(SiS_Private *SiS_Pr, USHORT BaseAddr, PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT flag; + if(HwDeviceExtension->jChipType == SIS_730) { + flag = SiS_GetReg1(SiS_Pr->SiS_P3c4,0x13); + if(flag & 0x20) return(1); + } flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); if(flag & 0x20) return(1); else return(0); @@ -5213,9 +6271,11 @@ USHORT flag; if(HwDeviceExtension->jChipType >= SIS_315H) { - flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(flag & EnableDualEdge) return(1); - else return(0); + if((HwDeviceExtension->jChipType != SIS_650) || (SiS_GetReg1(SiS_Pr->SiS_P3d4,0x5f) & 0xf0)) { + flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); + if(flag & EnableDualEdge) return(1); + else return(0); + } else return(0); } else #endif return(0); @@ -5230,7 +6290,7 @@ if(HwDeviceExtension->jChipType >= SIS_315H) { flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); if((flag & EnableDualEdge) && (flag & SetToLCDA)) return(1); -#if 0 /* Not done in 650/301LVx 1.10.6s, but in 650/301LV */ +#if 0 /* Not done in 650/30xLV 1.10.6s, but in 650/301LV */ else if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(flag) return(1); else return(0); @@ -5276,12 +6336,12 @@ #endif BOOLEAN -SiS_IsM650or651(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) +SiS_IsNotM650or651(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { #ifdef SIS315H USHORT flag; - if(HwDeviceExtension->jChipType >= SIS_315H) { + if(HwDeviceExtension->jChipType == SIS_650) { flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x5f); flag &= 0xF0; if((flag == 0xb0) || (flag == 0x90)) return 0; @@ -5299,25 +6359,41 @@ if(HwDeviceExtension->jChipType >= SIS_315H) { flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(flag & 0x08) return(1); - else return(0); + if(flag & EnableLVDSHiVision) return(1); /* = YPrPb = 0x08 */ + else return(0); + } else +#endif + return(0); +} + +BOOLEAN +SiS_IsChScart(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) +{ +#ifdef SIS315H + USHORT flag; + + if(HwDeviceExtension->jChipType >= SIS_315H) { + flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); + if(flag & EnableLVDSScart) return(1); /* = Scart = 0x04 */ + else return(0); } else #endif return(0); } BOOLEAN -SiS_IsTVOrYPbPr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) +SiS_IsTVOrYPbPrOrScart(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { USHORT flag; #ifdef SIS315H if(HwDeviceExtension->jChipType >= SIS_315H) { flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x30); - if(flag & SetCRT2ToTV) return(1); + if(flag & SetCRT2ToTV) return(1); flag = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(flag & 0x08) return(1); - else return(0); + if(flag & EnableLVDSHiVision) return(1); /* = YPrPb = 0x08 */ + if(flag & EnableLVDSScart) return(1); /* = Scart = 0x04- TW inserted */ + else return(0); } else #endif { @@ -5365,17 +6441,11 @@ USHORT flag; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - return(0); /* TW: Changed from 1 to 0! */ + return(0); } else { -#if 0 /* TW: Commented for test on bridge-less systems */ - if(HwDeviceExtension->jChipType >= SIS_315H) { /* TW: New (from 630/301B BIOS - not done there) */ -#endif - flag = SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x00); - if((flag == 1) || (flag == 2)) return(0); /* TW: Changed from 1 to 0! */ - else return(1); /* TW: Changed from 0 to 1! */ -#if 0 - } else return(0); /* TW: New (from 630/301B BIOS - always return 0) */ -#endif + flag = SiS_GetReg1(SiS_Pr->SiS_Part4Port,0x00); + if((flag == 1) || (flag == 2)) return(0); + else return(1); } } @@ -5392,7 +6462,7 @@ if((flag == 0x80) || (flag == 0x20)) return 0; else return 1; } else { - /* 310/325 series (650/301LVx 1.10.6s) */ + /* 310/325 series (650/30xLV 1.10.6s) */ flag &= 0x50; if((flag == 0x40) || (flag == 0x10)) return 0; else return 1; @@ -5411,37 +6481,31 @@ else return 0; } -/* TW: New from 650/301LV(x) 1.10.6s BIOS */ void SiS_SetHiVision(SiS_Private *SiS_Pr, USHORT BaseAddr,PSIS_HW_DEVICE_INFO HwDeviceExtension) { +#ifdef SIS315H USHORT temp; +#endif + + /* Note: This variable is only used on 30xLV systems. + CR38 has a different meaning on LVDS/CH7019 systems. + */ SiS_Pr->SiS_HiVision = 0; if(HwDeviceExtension->jChipType >= SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { -#if 0 /* TW: Old */ +#ifdef SIS315H + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { - SiS_Pr->SiS_HiVision = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - SiS_Pr->SiS_HiVision &= 0x38; - SiS_Pr->SiS_HiVision >>= 3; - } -#endif /* TW: New from 650/301LVx BIOS 1.10.6s */ - temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - if(temp & 0x40) { - temp &= 0x30; - switch(temp) { - case 0x00: SiS_Pr->SiS_HiVision = 4; break; - case 0x10: SiS_Pr->SiS_HiVision = 1; break; - case 0x20: SiS_Pr->SiS_HiVision = 2; break; - default: SiS_Pr->SiS_HiVision = 3; break; - } + temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); + temp &= 0x38; + SiS_Pr->SiS_HiVision = (temp >> 3); } } +#endif /* SIS315H */ } } -/* TW: Checked against 630/LVDS 2.08, 650/LVDS and 650/301LV BIOS */ BOOLEAN SiS_GetLCDResInfo(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -5455,11 +6519,15 @@ SiS_Pr->SiS_LCDTypeInfo = 0; SiS_Pr->SiS_LCDInfo = 0; - if (ModeNo<=0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + if(SiS_Pr->UseCustomMode) { + modeflag = SiS_Pr->CModeFlag; } else { + if(ModeNo <= 0x13) { + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; + } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; + } } if(!(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA))) return 0; @@ -5469,12 +6537,16 @@ temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x36); /* FSTN: Fake CR36 (TypeInfo 2, ResInfo SiS_Panel320x480) */ - if(SiS_Pr->SiS_IF_DEF_FSTN){ + if(SiS_Pr->SiS_IF_DEF_FSTN) { temp = 0x20 | SiS_Pr->SiS_Panel320x480; SiS_SetReg1(SiS_Pr->SiS_P3d4,0x36,temp); } - SiS_Pr->SiS_LCDTypeInfo = temp >> 4; + if(HwDeviceExtension->jChipType < SIS_315H) { + SiS_Pr->SiS_LCDTypeInfo = temp >> 4; + } else { + SiS_Pr->SiS_LCDTypeInfo = (temp & 0x0F) - 1; + } temp &= 0x0f; if(HwDeviceExtension->jChipType < SIS_315H) { /* TW: Translate 300 series LCDRes to 310/325 series for unified usage */ @@ -5504,23 +6576,54 @@ SiS_SetReg1(SiS_Pr->SiS_P3d4,0x37,temp); } SiS_Pr->SiS_LCDInfo = temp; + + if(!(SiS_Pr->UsePanelScaler)) SiS_Pr->SiS_LCDInfo &= ~DontExpandLCD; + else if(SiS_Pr->UsePanelScaler == 1) SiS_Pr->SiS_LCDInfo |= DontExpandLCD; - /* TW: Inserted entire 315-block from 650/LVDS/301+LVx BIOSes */ + /* TW: Inserted entire 315-block from 650/LVDS/30xLV BIOSes */ if(HwDeviceExtension->jChipType >= SIS_315H) { +#ifdef SIS315H if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { if(ModeNo == 0x3a || ModeNo == 0x4d || ModeNo == 0x65) { - SiS_Pr->SiS_LCDInfo |= LCDNonExpanding; + /* Bridge does not scale to 1280x1024 */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; } } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { + if(ModeNo == 0x7c || ModeNo == 0x7d || ModeNo == 0x7e) { + /* TW: Bridge does not scale to 1280x960 */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } + if(ModeNo == 0x2f || ModeNo == 0x5d || ModeNo == 0x5e) { + /* TW: Bridge does not scale to 640x400 */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } + } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { + if(ModeNo == 0x2f || ModeNo == 0x5d || ModeNo == 0x5e) { + /* TW: Most panels can't scale to 640x400 */ + SiS_Pr->SiS_LCDInfo &= ~DontExpandLCD; + } + } + /* TEMP - no idea about the timing and zoom factors */ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } + /* TEMP - no idea about the timing and zoom factors */ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } } } if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x01) { SiS_Pr->SiS_LCDInfo &= 0xFFEF; SiS_Pr->SiS_LCDInfo |= LCDPass11; } +#endif } else { +#ifdef SIS300 if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { if((ROMAddr) && SiS_Pr->SiS_UseROM) { if(!(ROMAddr[0x235] & 0x02)) { @@ -5529,47 +6632,41 @@ } } else { if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - if((SiS_Pr->SiS_SetFlag & CRT2IsVGA) && ((ModeNo == 0x03) || (ModeNo == 0x10))) { + if((SiS_Pr->SiS_SetFlag & SetDOSMode) && ((ModeNo == 0x03) || (ModeNo == 0x10))) { SiS_Pr->SiS_LCDInfo &= 0xEF; } } } - } - -#ifdef LINUX_KERNEL - printk(KERN_INFO "sisfb: (LCDInfo = 0x%x LCDResInfo = 0x%x LCDTypeInfo = 0x%x)\n", - SiS_Pr->SiS_LCDInfo, SiS_Pr->SiS_LCDResInfo, SiS_Pr->SiS_LCDTypeInfo); -#endif -#ifdef LINUX_XF86 - xf86DrvMsg(0, X_PROBED, "(init301: LCDInfo=0x%04x LCDResInfo=0x%02x LCDTypeInfo=0x%02x)\n", - SiS_Pr->SiS_LCDInfo, SiS_Pr->SiS_LCDResInfo, SiS_Pr->SiS_LCDTypeInfo); #endif - + } + /* TW: With Trumpion, always Expanding */ if(SiS_Pr->SiS_IF_DEF_TRUMPION != 0){ - SiS_Pr->SiS_LCDInfo &= (~LCDNonExpanding); + SiS_Pr->SiS_LCDInfo &= (~DontExpandLCD); } - if(!((HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_SetFlag & CRT2IsVGA))) { + if(!((HwDeviceExtension->jChipType < SIS_315H) && (SiS_Pr->SiS_SetFlag & SetDOSMode))) { if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { if(ModeNo > 0x13) { - if(!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { + if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { if((resinfo == 7) || (resinfo == 3)) { SiS_Pr->SiS_SetFlag |= EnableLVDSDDA; } } } } - if(SiS_Pr->SiS_LCDInfo & LCDPass11) { - SiS_Pr->SiS_SetFlag |= EnableLVDSDDA; + if(ModeNo == 0x12) { + if(SiS_Pr->SiS_LCDInfo & LCDPass11) { + SiS_Pr->SiS_SetFlag |= EnableLVDSDDA; + } } } if(modeflag & HalfDCLK) { if(SiS_Pr->SiS_IF_DEF_TRUMPION == 0) { - if(!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { + if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { if(!(((SiS_Pr->SiS_IF_DEF_LVDS == 1) || (HwDeviceExtension->jChipType < SIS_315H)) && (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480))) { if(ModeNo > 0x13) { @@ -5586,7 +6683,6 @@ } - /* TW: wdr: if (VBInfo & LCD) && (VBInfo & (SetSimuScanMode | SwitchToCRT2)) { */ if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { if(SiS_Pr->SiS_VBInfo & SetNotSimuMode) { SiS_Pr->SiS_SetFlag |= LCDVESATiming; @@ -5595,16 +6691,34 @@ SiS_Pr->SiS_SetFlag |= LCDVESATiming; } - /* TW: Inserted from 650/301LVx BIOS 1.10.6s */ - if(SiS_Pr->SiS_VBType & VB_SIS30xNEW) { +#ifdef SIS315H + /* TW: 650/30xLV 1.10.6s */ + if(HwDeviceExtension->jChipType >= SIS_315H) { + if(SiS_Pr->SiS_VBType & (VB_SIS302B | VB_SIS302LV)) { + /* Enable 302B/302LV dual link mode */ + /* (302B is a theory - not in any BIOS */ temp = 0x00; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) temp = 0x04; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) temp = 0x04; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) temp = 0x04; SiS_SetReg1(SiS_Pr->SiS_P3d4,0x39,temp); - } else if((HwDeviceExtension->jChipType > SIS_315H) && (SiS_Pr->SiS_IF_DEF_LVDS == 0)) { + } else if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { SiS_SetReg1(SiS_Pr->SiS_P3d4,0x39,0x00); + } } +#endif + +#ifdef LINUX_KERNEL +#ifdef TWDEBUG + printk(KERN_DEBUG "sisfb: (LCDInfo=0x%04x LCDResInfo=0x%02x LCDTypeInfo=0x%02x)\n", + SiS_Pr->SiS_LCDInfo, SiS_Pr->SiS_LCDResInfo, SiS_Pr->SiS_LCDTypeInfo); +#endif +#endif +#ifdef LINUX_XF86 + xf86DrvMsgVerb(0, X_PROBED, 3, + "(init301: LCDInfo=0x%04x LCDResInfo=0x%02x LCDTypeInfo=0x%02x SetFlag=0x%04x)\n", + SiS_Pr->SiS_LCDInfo, SiS_Pr->SiS_LCDResInfo, SiS_Pr->SiS_LCDTypeInfo, SiS_Pr->SiS_SetFlag); +#endif return 1; } @@ -5677,6 +6791,7 @@ SiS_WaitVBRetrace(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension) { if(HwDeviceExtension->jChipType < SIS_315H) { +#ifdef SIS300 if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(!(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x20)) return; } @@ -5685,27 +6800,36 @@ } else { SiS_WaitRetrace2(SiS_Pr,HwDeviceExtension); } +#endif } else { +#ifdef SIS315H if(!(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x40)) { SiS_WaitRetrace1(SiS_Pr,HwDeviceExtension); } else { SiS_WaitRetrace2(SiS_Pr,HwDeviceExtension); } +#endif } } void SiS_WaitRetrace1(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension) { - USHORT i,watchdog; + USHORT watchdog; +#ifdef SIS300 + USHORT i; +#endif if(HwDeviceExtension->jChipType >= SIS_315H) { +#ifdef SIS315H if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x1f) & 0xc0) return; watchdog = 65535; while( (SiS_GetReg2(SiS_Pr->SiS_P3da) & 0x08) && --watchdog); watchdog = 65535; while( (!(SiS_GetReg2(SiS_Pr->SiS_P3da) & 0x08)) && --watchdog); +#endif } else { +#ifdef SIS300 #if 0 /* TW: Not done in A901 BIOS */ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_GetReg1(SiS_Pr->SiS_P3c4,0x1f) & 0xc0) return; @@ -5721,6 +6845,7 @@ while( (!(SiS_GetReg2(SiS_Pr->SiS_P3da) & 0x08)) && --watchdog); if(watchdog) break; } +#endif } } @@ -5739,24 +6864,31 @@ void SiS_WaitRetrace2(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension) { - USHORT i,watchdog,temp; + USHORT watchdog; +#ifdef SIS300 + USHORT i; +#endif if(HwDeviceExtension->jChipType >= SIS_315H) { +#ifdef SIS315H watchdog = 65535; while( (SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x30) & 0x02) && --watchdog); watchdog = 65535; while( (!(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x30) & 0x02)) && --watchdog); +#endif } else { +#ifdef SIS300 for(i=0; i<10; i++) { watchdog = 65535; - while( (temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x25) & 0x02) && --watchdog); + while( (SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x25) & 0x02) && --watchdog); if(watchdog) break; } for(i=0; i<10; i++) { watchdog = 65535; - while( (!(temp = SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x25) & 0x02)) && --watchdog); + while( (!(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x25) & 0x02)) && --watchdog); if(watchdog) break; } +#endif } } @@ -5767,7 +6899,7 @@ { USHORT temp; - temp = SiS_GetReg1(Port,Index); /* SiS_Pr->SiS_Part1Port index 02 */ + temp = SiS_GetReg1(Port,Index); temp = (temp & (DataAND)) | DataOR; SiS_SetReg1(Port,Index,temp); } @@ -5777,7 +6909,7 @@ { USHORT temp; - temp = SiS_GetReg1(Port,Index); /* SiS_Pr->SiS_Part1Port index 02 */ + temp = SiS_GetReg1(Port,Index); temp &= DataAND; SiS_SetReg1(Port,Index,temp); } @@ -5786,7 +6918,7 @@ { USHORT temp; - temp = SiS_GetReg1(Port,Index); /* SiS_Pr->SiS_Part1Port index 02 */ + temp = SiS_GetReg1(Port,Index); temp |= DataOR; SiS_SetReg1(Port,Index,temp); } @@ -5794,7 +6926,6 @@ /* ========================================================= */ /* TW: Set 301 TV Encoder (and some LCD relevant) registers */ -/* TW: Checked against 650/301LV, 650/301LVx and 630/301B (I+II) */ void SiS_SetGroup2(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr, USHORT ModeNo, USHORT ModeIdIndex,USHORT RefreshRateTableIndex, @@ -5804,26 +6935,29 @@ USHORT push1, push2; const UCHAR *PhasePoint; const UCHAR *TimingPoint; +#ifdef SIS315H const SiS_Part2PortTblStruct *CRT2Part2Ptr = NULL; - USHORT modeflag, resinfo, crt2crtc, resindex, CRT2Index; + USHORT resindex, CRT2Index; +#endif + USHORT modeflag, resinfo, crt2crtc; ULONG longtemp, tempeax, tempebx, temp2, tempecx; const UCHAR atable[] = { 0xc3,0x9e,0xc3,0x9e,0x02,0x02,0x02, 0xab,0x87,0xab,0x9e,0xe7,0x02,0x02 }; - /* TW: Inserted from 650/301LV BIOS */ +#ifdef SIS315H if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - /* TW: Inserted from 650/301LVx 1.10.6s: (Is at end of SetGroup2!) */ + /* TW: 650/30xLV 1.10.6s: (Is at end of SetGroup2!) */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1a,0xfc,0x03); temp = 1; - if(ModeNo<=0x13) temp = 3; + if(ModeNo <= 0x13) temp = 3; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x0b,temp); } } - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 650/301LV: (VB_SIS301LV | VB_SIS302LV)) */ + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { if((ModeNo == 0x4a) || (ModeNo == 0x38)) { @@ -5855,6 +6989,7 @@ } return; } +#endif if(ModeNo<=0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; /* si+St_ResInfo */ @@ -5876,10 +7011,16 @@ temp |= ((tempbx & 0x00FF) >> 3); temp ^= 0x0C; + /* TW: From 1.10.7w (no vb check there; don't care - this only disables SVIDEO and CVBS signal) */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + temp |= 0x0c; + } + PhasePoint = SiS_Pr->SiS_PALPhase; TimingPoint = SiS_Pr->SiS_PALTiming; -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { /* PALPhase */ + + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + temp ^= 0x01; if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { TimingPoint = SiS_Pr->SiS_HiTVSt2Timing; @@ -5888,14 +7029,17 @@ else TimingPoint = SiS_Pr->SiS_HiTVTextTiming; } } else TimingPoint = SiS_Pr->SiS_HiTVExtTiming; + + if(SiS_Pr->SiS_HiVision & 0x03) temp &= 0xfe; + } else { -#endif + if(SiS_Pr->SiS_VBInfo & SetPALTV){ TimingPoint = SiS_Pr->SiS_PALTiming; PhasePoint = SiS_Pr->SiS_PALPhase; - if( (SiS_Pr->SiS_VBType & (VB_SIS301B | VB_SIS302B)) && + if( (SiS_Pr->SiS_VBType & VB_SIS301B302B) && ( (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode) ) ) { PhasePoint = SiS_Pr->SiS_PALPhase2; @@ -5907,16 +7051,15 @@ TimingPoint = SiS_Pr->SiS_NTSCTiming; PhasePoint = SiS_Pr->SiS_NTSCPhase; - if( (SiS_Pr->SiS_VBType & (VB_SIS301B | VB_SIS302B)) && + if( (SiS_Pr->SiS_VBType & VB_SIS301B302B) && ( (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode) ) ) { PhasePoint = SiS_Pr->SiS_NTSCPhase2; } } -#ifdef oldHV + } -#endif SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x00,temp); temp = 0; @@ -5931,17 +7074,17 @@ if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x01) { temp1 = SiS_GetReg1(SiS_Pr->SiS_P3d4,temp); - if(temp1 & 0x40) { + if(temp1 & EnablePALM) { /* 0x40 */ PhasePoint = SiS_Pr->SiS_PALMPhase; - if( (SiS_Pr->SiS_VBType & (VB_SIS301B | VB_SIS302B)) && + if( (SiS_Pr->SiS_VBType & VB_SIS301B302B) && ( (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode) ) ) { PhasePoint = SiS_Pr->SiS_PALMPhase2; } } - if(temp1 & 0x80) { + if(temp1 & EnablePALN) { /* 0x80 */ PhasePoint = SiS_Pr->SiS_PALNPhase; - if( (SiS_Pr->SiS_VBType & (VB_SIS301B | VB_SIS302B)) && + if( (SiS_Pr->SiS_VBType & VB_SIS301B302B) && ( (!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode) ) ) { PhasePoint = SiS_Pr->SiS_PALNPhase2; @@ -5952,9 +7095,9 @@ } #ifdef SIS315H - /* TW: Inserted from 650/301LV BIOS */ + /* TW: 650/301LV BIOS */ if(HwDeviceExtension->jChipType >= SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 650/301LV : 301LV | 302LV */ + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { if((ModeNo == 0x4a) || (ModeNo == 0x38)) { @@ -5966,14 +7109,14 @@ } #endif - for(i=0x31, j=0; i<=0x34; i++, j++){ + for(i=0x31, j=0; i<=0x34; i++, j++) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,PhasePoint[j]); } - for(i=0x01, j=0; i<=0x2D; i++, j++){ + for(i=0x01, j=0; i<=0x2D; i++, j++) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,TimingPoint[j]); } - for(i=0x39; i<=0x45; i++, j++){ + for(i=0x39; i<=0x45; i++, j++) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,TimingPoint[j]); } @@ -5993,15 +7136,13 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x37,SiS_Pr->SiS_RY3COE); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x38,SiS_Pr->SiS_RY4COE); -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) tempax = 950; - else { -#endif + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if(SiS_Pr->SiS_HiVision == 3) tempax = 950; + else tempax = 440; + } else { if(SiS_Pr->SiS_VBInfo & SetPALTV) tempax = 520; else tempax = 440; -#ifdef oldHV } -#endif if( ( ( (!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) || (SiS_Pr->SiS_HiVision == 3) ) && (SiS_Pr->SiS_VDE <= tempax) ) || ( (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (SiS_Pr->SiS_HiVision != 3) && @@ -6040,7 +7181,7 @@ tempcx = SiS_Pr->SiS_HT; - /* TW: Inserted from 650/301LVx 1.10.6s */ + /* TW: 650/30xLV 1.10.6s */ if(HwDeviceExtension->jChipType >= SIS_315H) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) { tempcx >>= 1; @@ -6065,9 +7206,10 @@ push1 = tempcx; tempcx += 7; -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) tempcx -= 4; -#endif + if((SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3)) { + tempcx -= 4; + } temp = (tempcx & 0x00FF) << 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x22,0x0F,temp); @@ -6084,12 +7226,11 @@ tempbx = push2; tempbx += 8; -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if((SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3)) { tempbx -= 4; tempcx = tempbx; } -#endif temp = (tempbx & 0x00FF) << 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x29,0x0F,temp); @@ -6101,10 +7242,11 @@ SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x28,0x0F,temp); tempcx += 8; -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) tempcx -= 4; -#endif - temp = (tempcx & 0xFF) << 4; + if((SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3)) { + tempcx -= 4; + } + temp = (tempcx & 0x00FF) << 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x2A,0x0F,temp); tempcx = push1; @@ -6147,45 +7289,67 @@ } tempbx -= 2; temp = tempbx & 0x00FF; -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if((SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (SiS_Pr->SiS_HiVision == 3)) { if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { if(ModeNo == 0x2f) temp++; } } -#endif + /* TW: From 1.10.7w - doesn't make sense */ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { + if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { + if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) { /* SetFlag?? */ + if(ModeNo == 0x03) temp++; + } + } + } + } SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x2F,temp); tempax = (tempcx & 0xFF00) | (tempax & 0x00FF); tempbx = ((tempbx & 0xFF00) << 6) | (tempbx & 0x00FF); tempax |= (tempbx & 0xFF00); -#ifdef oldHV - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { -#endif - if(HwDeviceExtension->jChipType < SIS_315H) { - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSCART)) { /* TW: New from 630/301B (II) BIOS */ - tempax |= 0x1000; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSVIDEO)) tempax |= 0x2000; - } - } else { - tempax |= 0x1000; - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSVIDEO)) tempax |= 0x2000; - } -#ifdef oldHV + if(HwDeviceExtension->jChipType < SIS_315H) { + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSCART)) { /* TW: New from 630/301B (II) BIOS */ + tempax |= 0x1000; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSVIDEO)) tempax |= 0x2000; + } + } + } else { + /* TODO Check this with other BIOSes */ + if((!(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) && + (SiS_Pr->SiS_HiVision == 3)) { + tempax |= 0x1000; + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToSVIDEO)) tempax |= 0x2000; + } } -#endif temp = (tempax & 0xFF00) >> 8; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x30,temp); - /* TW: Inserted from 650/301LVx 1.10.6s */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) || - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) ) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x10,0x60); + /* TW: 650/30xLV 1.10.6s */ + if(HwDeviceExtension->jChipType > SIS_315H) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if( (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) ) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x10,0x60); + } + } + } + + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if(SiS_Pr->SiS_HiVision != 3) { + for(i=0, j=0; i<=0x2d; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS_HiVisionTable[SiS_Pr->SiS_HiVision][j]); + } + for(i=0x39; i<=0x45; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS_HiVisionTable[SiS_Pr->SiS_HiVision][j]); + } } } - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* tv gatingno */ + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { tempbx = SiS_Pr->SiS_VDE; if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && (!(SiS_Pr->SiS_HiVision & 0x03))) { tempbx >>= 1; @@ -6196,33 +7360,35 @@ temp |= 0x18; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x46,temp); temp = tempbx & 0x00FF; - SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x47,temp); - if(HwDeviceExtension->jChipType >= SIS_315H) { /* TW: Inserted from 650/301LVx 1.10.6s */ - tempax = 0; - if(SiS_Pr->SiS_HiVision & 0x07) { - if(SiS_Pr->SiS_HiVision & 0x04) tempax = 0x1000; - else if(SiS_Pr->SiS_HiVision & 0x01) tempax = 0x3000; - else tempax = 0x5000; + SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x47,temp); /* tv gatingno */ + if(HwDeviceExtension->jChipType >= SIS_315H) { /* TW: 650/30xLV 1.10.6s */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + tempax = 0; + if(SiS_Pr->SiS_HiVision & 0x03) { + tempax = 0x3000; + if(SiS_Pr->SiS_HiVision & 0x01) tempax = 0x5000; + } + temp = (tempax & 0xFF00) >> 8; + SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x4d,temp); } - temp = (tempax & 0xFF00) >> 8; - SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x4d,temp); } } tempbx &= 0x00FF; if(!(modeflag & HalfDCLK)) { tempcx = SiS_Pr->SiS_VGAHDE; - if(tempcx >= SiS_Pr->SiS_HDE){ + if(tempcx >= SiS_Pr->SiS_HDE) { tempbx |= 0x2000; tempax &= 0x00FF; } } tempcx = 0x0101; - if(SiS_Pr->SiS_VBInfo & (SetPALTV | SetCRT2ToTV)) { /*301b- TW: BIOS BUG? */ +/*if(SiS_Pr->SiS_VBInfo & (SetPALTV | SetCRT2ToTV)) { */ /*301b- TW: BIOS BUG? */ + if(SiS_Pr->SiS_VBInfo & (SetCRT2ToTV - SetCRT2ToHiVisionTV)) { if(!(SiS_Pr->SiS_HiVision & 0x03)) { if(SiS_Pr->SiS_VGAHDE >= 1024) { - if(!(modeflag & HalfDCLK)) { /* TW: This check not in 630/301B */ + if((!(modeflag & HalfDCLK)) || (HwDeviceExtension->jChipType < SIS_315H)) { /* TW: This check not in 630/301B */ tempcx = 0x1920; if(SiS_Pr->SiS_VGAHDE >= 1280) { tempcx = 0x1420; @@ -6233,8 +7399,7 @@ } } - if(!(tempbx & 0x2000)){ - + if(!(tempbx & 0x2000)) { if(modeflag & HalfDCLK) { tempcx = (tempcx & 0xFF00) | (((tempcx & 0x00FF) << 1) & 0xff); } @@ -6270,17 +7435,17 @@ temp |= 0x18; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x46,0xE0,temp); if(SiS_Pr->SiS_VBInfo & SetPALTV) { - tempbx = 0x0382; /* TW: BIOS; Was 0x0364; */ - tempcx = 0x007e; /* TW: BIOS; Was 0x009c; */ + tempbx = 0x0382; + tempcx = 0x007e; } else { - tempbx = 0x0369; /* TW: BIOS; Was 0x0346; */ - tempcx = 0x0061; /* TW: BIOS; Was 0x0078; */ + tempbx = 0x0369; + tempcx = 0x0061; } temp = (tempbx & 0x00FF) ; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x4B,temp); temp = (tempcx & 0x00FF) ; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x4C,temp); - tempbx &= 0x0300; + tempbx &= 0x03FF; temp = (tempcx & 0xFF00) >> 8; temp = (temp & 0x0003) << 2; temp |= (tempbx >> 8); @@ -6295,7 +7460,7 @@ } temp = 0; - if((HwDeviceExtension->jChipType == SIS_630)|| + if((HwDeviceExtension->jChipType == SIS_630) || (HwDeviceExtension->jChipType == SIS_730)) { temp = 0x35; } else if(HwDeviceExtension->jChipType >= SIS_315H) { @@ -6304,7 +7469,7 @@ if(temp) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x01) { - if(SiS_GetReg1(SiS_Pr->SiS_P3d4,temp) & 0x40) { + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,temp) & EnablePALM) { /* 0x40 */ SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xEF); temp = SiS_GetReg1(SiS_Pr->SiS_Part2Port,0x01); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x01,temp - 1); @@ -6313,21 +7478,23 @@ } } - -#ifdef oldHV - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (!(SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) ) { if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x0B,0x00); } } -#endif if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) return; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + SiS_Set300Part2Regs(SiS_Pr, HwDeviceExtension, ModeIdIndex, + RefreshRateTableIndex, BaseAddr, ModeNo); + return; + } } else { /* TW: !!! The following is a duplicate, done for LCDA as well (see above) */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 650/301LV: (VB_SIS301LV | VB_SIS302LV)) */ + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { if((ModeNo == 0x4a) || (ModeNo == 0x38)) { @@ -6364,11 +7531,11 @@ /* TW: From here: Part2 LCD setup */ tempbx = SiS_Pr->SiS_HDE; - /* TW: Inserted from 650/301LVx 1.10.6s */ if(HwDeviceExtension->jChipType >= SIS_315H) { + /* TW: 650/30xLV 1.10.6s */ if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) tempbx >>= 1; } - tempbx--; /* RHACTE=HDE-1 */ + tempbx--; /* RHACTE=HDE-1 */ temp = tempbx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x2C,temp); temp = (tempbx & 0xFF00) >> 8; @@ -6408,34 +7575,44 @@ temp = (tempcx & 0xFF00) >> 8; temp <<= 5; - if(HwDeviceExtension->jChipType < SIS_315H) { + + /* Enable dithering; newer versions only do this for 32bpp mode */ + if((HwDeviceExtension->jChipType == SIS_300) && (SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) { + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) temp |= 0x10; + } else if(HwDeviceExtension->jChipType < SIS_315H) { if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) temp |= 0x10; else { if(SiS_Pr->SiS_LCDInfo & LCDSync) /* TW: 630/301 BIOS checks this */ temp |= 0x10; } } else { - /* TW: Inserted from 630/301LVx 1.10.6s */ - if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { - if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { - temp |= 0x10; - } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + /* TW: 650/30xLV 1.10.6s */ + if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { + if(SiS_GetReg1(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { /* 32bpp mode? */ + temp |= 0x10; + } + } + } else { + temp |= 0x10; } } /* 630/301 does not do all this */ if((SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) && (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { - if(HwDeviceExtension->jChipType >= SIS_315H) { - /* TW: Inserted from 650/301LVx 1.10.6s */ + if((HwDeviceExtension->jChipType >= SIS_315H) && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { + /* TW: 650/30xLV 1.10.6s */ temp |= (SiS_GetReg1(SiS_Pr->SiS_P3d4,0x37) >> 6); - } else { + temp |= 0x08; /* From 1.10.7w */ + if(!(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit)) temp |= 0x04; /* From 1.10.7w */ + } else { tempbx = (tempbx & 0xFF00) | (SiS_Pr->SiS_LCDInfo & 0x0FF); if(tempbx & LCDSync) { - tempbx &= (0xFF00 | LCDSyncBit); - tempbx = (tempbx & 0xFF00) | ((tempbx & 0x00FF) >> LCDSyncShift); - temp |= (tempbx & 0x00FF); + tempbx &= 0xFFE0; + tempbx = (tempbx & 0xFF00) | ((tempbx & 0x00FF) >> 6); + temp |= (tempbx & 0x00FF); } - } + } } SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x1A,temp); @@ -6445,9 +7622,22 @@ SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x17,0xFB); SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x18,0xDF); - if(HwDeviceExtension->jChipType >= SIS_315H) { /* ------------- 310 series ------------ */ + /* 1280x960, 1280x1024 and 1600x1200 data invalid/missing in tables, use old calculation */ + if((HwDeviceExtension->jChipType >= SIS_315H) && + (SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024) && + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x768) && + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1600x1200) && + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x960)) { + +#ifdef SIS315H /* ------------- 310/325/330 series ------------ */ /* TW: Inserted this entire section from 650/301LV(x) BIOS */ + + /* Using this on the 301B with an auto-expanding 1024 panel (CR37=1) results + * in a black bar in modes < 1024; if the panel is non-expanding, the bridge + * scales all modes to 1024. All modes in both variants (exp/non-exp) work. + */ SiS_GetCRT2Part2Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &CRT2Index,&resindex); @@ -6457,11 +7647,11 @@ case Panel_1280x1024 : CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1280x1024_1; break; case Panel_1400x1050 : CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1400x1050_1; break; case Panel_1600x1200 : CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1600x1200_1; break; - case Panel_1024x768 + 16 : CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_2; break; /* Non-Expanding */ + case Panel_1024x768 + 16: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_2; break; /* Non-Expanding */ case Panel_1280x1024 + 16: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1280x1024_2; break; case Panel_1400x1050 + 16: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1400x1050_2; break; case Panel_1600x1200 + 16: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1600x1200_2; break; - case Panel_1024x768 + 32 : CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_3; break; /* VESA Timing */ + case Panel_1024x768 + 32: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_3; break; /* VESA Timing */ case Panel_1280x1024 + 32: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1280x1024_3; break; case Panel_1400x1050 + 32: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1400x1050_3; break; case Panel_1600x1200 + 32: CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1600x1200_3; break; @@ -6483,7 +7673,7 @@ SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0x0f,(CRT2Part2Ptr+resindex)->CR[11]); if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) { - if(SiS_Pr->SiS_VGAVDE == 0x20d) { + if(SiS_Pr->SiS_VGAVDE == 525) { temp = 0xc3; if(SiS_Pr->SiS_ModeType <= ModeVGA) { temp++; @@ -6492,7 +7682,7 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x2f,temp); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x30,0xb3); } - if(SiS_Pr->SiS_VGAVDE == 0x1a4) { + if(SiS_Pr->SiS_VGAVDE == 420) { temp = 0x4d; if(SiS_Pr->SiS_ModeType <= ModeVGA) { temp++; @@ -6502,20 +7692,36 @@ } } - /* TW: Inserted from 650/301LVx 1.10.6s: */ + /* TW: 650/30xLV 1.10.6s: */ /* !!! This is a duplicate, done for LCDA as well - see above */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1a,0xfc,0x03); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1a,0xfc,0x03); /* Not done in 1.10.7w */ temp = 1; - if(ModeNo<=0x13) temp = 3; + if(ModeNo <= 0x13) temp = 3; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x0b,temp); } } +#endif - } else { /* ------------- 300 series ----------- */ + } else { /* ------ 300 series and other bridges, other LCD resolutions ------ */ + /* Using this on the 301B with an auto-expanding 1024 panel (CR37=1) makes + * the panel scale at modes < 1024 (no black bars); if the panel is non-expanding, + * the bridge scales all modes to 1024. + * !!! Malfunction at 640x480 and 640x400 when panel is auto-expanding - black screen !!! + */ + tempcx++; + + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) tempbx = 768; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) tempbx = 1024; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) tempbx = 1200; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) tempbx = 768; + else if(SiS_Pr->SiS_VDE != 1024) tempbx = 960; + else tempbx = 1024; + +#if 0 /* old */ tempbx = 768; if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1024x768) { tempbx = 1024; @@ -6528,14 +7734,18 @@ } } } - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { +#endif + + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { tempbx = SiS_Pr->SiS_VDE - 1; tempcx--; } + tempax = 1; - if(!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) { - if(tempbx != SiS_Pr->SiS_VDE){ + if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { + if(tempbx != SiS_Pr->SiS_VDE) { tempax = tempbx; +/* if(SiS_Pr->SiS_VGAVDE == 525) tempax += 60; in 650/301B BIOS */ if(tempax < SiS_Pr->SiS_VDE) { tempax = 0; tempcx = 0; @@ -6551,13 +7761,17 @@ tempcx -= tempax; /* lcdvdes */ tempbx -= tempax; /* lcdvdee */ } + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "lcdvds 0x%x lcdvde 0x%x\n", tempcx, tempbx); +#endif temp = tempcx & 0x00FF; /* RVEQ1EQ=lcdvdes */ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x05,temp); temp = tempbx & 0x00FF; /* RVEQ2EQ=lcdvdee */ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x06,temp); - temp = ((tempbx & 0xFF00) >> 8 ) << 3; + temp = ((tempbx & 0xFF00) >> 8) << 3; temp |= ((tempcx & 0xFF00) >> 8); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x02,temp); @@ -6568,7 +7782,11 @@ tempcx >>= 4; tempbx += tempax; tempbx >>= 1; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx -= 10; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx -= 10; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "lcdvrs 0x%x\n", tempbx); +#endif temp = tempbx & 0x00FF; /* RTVACTEE=lcdvrs */ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x04,temp); @@ -6585,7 +7803,7 @@ (HwDeviceExtension->jChipRevision > 2) ) && (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) && (!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) && - (!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) ) { + (!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) ) { if(ModeNo == 0x13) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x04,0xB9); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x05,0xCC); @@ -6601,7 +7819,11 @@ } } - /* TW: Inserted missing code from 630/301B BIOS: (II: 3258) */ + /* TW: Inserted missing code from 630/301B BIOS; + * Strangely, this is done in all 650 BIOSes as + * well (although LCDTypeInfo is not used there + * in the same way as on 300 series) + */ if(SiS_Pr->SiS_LCDTypeInfo == 0x0c) { crt2crtc &= 0x1f; @@ -6633,6 +7855,9 @@ tempbx += 2; } push1 = tempbx; +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "lcdhde 0x%x\n", tempbx); +#endif temp = tempbx & 0x00FF; /* RHEQPLE=lcdhdee */ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x23,temp); temp = (tempbx & 0xFF00) >> 8; @@ -6642,17 +7867,19 @@ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { temp += 2; } - SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x1F,temp); /* RHBLKE=lcdhdes */ - - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x20,0x0F); + SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x1F,temp); /* RHBLKE=lcdhdes[7:0] */ + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x20,0x0F); /* lcdhdes [11:8] */ tempbx += tempcx; push2 = tempbx; - temp = tempbx & 0xFF; /* RHBURSTS=lcdhrs */ +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "lcdhrs 0x%x\n", tempbx); +#endif + temp = tempbx & 0x00FF; /* RHBURSTS=lcdhrs */ if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) { - if(SiS_Pr->SiS_HDE == 1280) temp = 0x47; - } + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { + if(SiS_Pr->SiS_HDE == 1280) temp = 0x47; + } } SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x1C,temp); temp = ((tempbx & 0xFF00) >> 8) << 4; @@ -6661,6 +7888,9 @@ tempbx = push2; tempcx <<= 1; tempbx += tempcx; +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "lcdhre 0x%x\n", tempbx); +#endif temp = tempbx & 0x00FF; /* RHSYEXP2S=lcdhre */ SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x21,temp); @@ -6676,10 +7906,12 @@ if(SiS_Pr->SiS_ModeType <= ModeVGA) temp=0x4F; else - temp=0x4D; /* 650: 4e */ + temp=0x4D; SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x2f,temp); } } + SiS_Set300Part2Regs(SiS_Pr, HwDeviceExtension, ModeIdIndex, + RefreshRateTableIndex, BaseAddr, ModeNo); } /* HwDeviceExtension */ } @@ -6695,20 +7927,60 @@ return((USHORT) tempax); } +/* TW: New from 300/301LV BIOS 1.16.51 for ECS A907. Seems highly preliminary. */ +void +SiS_Set300Part2Regs(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, + USHORT ModeIdIndex, USHORT RefreshRateTableIndex, + USHORT BaseAddr, USHORT ModeNo) +{ + USHORT crt2crtc, resindex; + int i,j; + const SiS_Part2PortTblStruct *CRT2Part2Ptr = NULL; + + if(HwDeviceExtension->jChipType != SIS_300) return; + if(!(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) return; + + if(ModeNo<=0x13) { + crt2crtc = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; + } else { + crt2crtc = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; + } + + resindex = crt2crtc & 0x3F; + if(SiS_Pr->SiS_SetFlag & LCDVESATiming) CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_1; + else CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_2; + + /* TW: The BIOS code (1.16.51) is obviously a fragment! */ + if(ModeNo > 0x13) { + CRT2Part2Ptr = SiS_Pr->SiS_CRT2Part2_1024x768_1; + resindex = 4; + } + + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x01,0x80,(CRT2Part2Ptr+resindex)->CR[0]); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x02,0x80,(CRT2Part2Ptr+resindex)->CR[1]); + for(i = 2, j = 0x04; j <= 0x06; i++, j++ ) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,j,(CRT2Part2Ptr+resindex)->CR[i]); + } + for(j = 0x1c; j <= 0x1d; i++, j++ ) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,j,(CRT2Part2Ptr+resindex)->CR[i]); + } + for(j = 0x1f; j <= 0x21; i++, j++ ) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,j,(CRT2Part2Ptr+resindex)->CR[i]); + } + SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x23,(CRT2Part2Ptr+resindex)->CR[10]); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0x0f,(CRT2Part2Ptr+resindex)->CR[11]); +} + /* TW: Set 301 Macrovision(tm) registers */ -/* TW: Double-Checked against 650/301LV, 650/301LVx and 630/301B BIOS */ void SiS_SetGroup3(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT temp; -#ifdef oldHV USHORT i; const UCHAR *tempdi; -#endif USHORT modeflag; - /* TW: Inserted from 650/301LVx 1.10.6s */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) return; if(ModeNo<=0x13) @@ -6741,7 +8013,7 @@ if(temp) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x01) { - if(SiS_GetReg1(SiS_Pr->SiS_P3d4,temp) & 0x40){ + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,temp) & EnablePALM){ /* 0x40 */ SiS_SetReg1(SiS_Pr->SiS_Part3Port,0x13,0xFA); SiS_SetReg1(SiS_Pr->SiS_Part3Port,0x14,0xC8); SiS_SetReg1(SiS_Pr->SiS_Part3Port,0x3D,0xA8); @@ -6750,7 +8022,6 @@ } } -#ifdef oldHV if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { tempdi = SiS_Pr->SiS_HiTVGroup3Data; if(SiS_Pr->SiS_SetFlag & TVSimuMode) { @@ -6759,53 +8030,65 @@ tempdi = SiS_Pr->SiS_HiTVGroup3Text; } } + if(SiS_Pr->SiS_HiVision & 0x03) { + tempdi = SiS_HiTVGroup3_1; + if(SiS_Pr->SiS_HiVision & 0x02) tempdi = SiS_HiTVGroup3_2; + } for(i=0; i<=0x3E; i++){ SiS_SetReg1(SiS_Pr->SiS_Part3Port,i,tempdi[i]); } } -#endif return; } /* TW: Set 301 VGA2 registers */ -/* TW: Double-Checked against 650/301LV(x) and 630/301B BIOS */ void SiS_SetGroup4(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex,USHORT RefreshRateTableIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) { - USHORT tempax,tempcx,tempbx,modeflag,temp,temp2; + USHORT tempax,tempcx,tempbx,modeflag,temp,temp2,resinfo; ULONG tempebx,tempeax,templong; - if(ModeNo<=0x13) + + if(ModeNo<=0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - else + resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; + } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; + } - /* TW: From 650/301LVx 1.10.6s BIOS */ - if(SiS_Pr->SiS_VBType & (VB_SIS30xLV | VB_SIS30xNEW)) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x24,0x0e); - } + if(HwDeviceExtension->jChipType >= SIS_315H) { + /* TW: From 650/302LV 1.10.6s (not for 300/301LV - no LCDA on this combination) */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x24,0x0e); + } + } } - if(SiS_Pr->SiS_VBType & VB_SIS30xNEW) { + if(SiS_Pr->SiS_VBType & VB_SIS302LV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x10,0x9f); } } - /* TW: From 650/301LV BIOS */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { - /* TW: This is a duplicate; done at the end, too */ - if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) { + if(HwDeviceExtension->jChipType >= SIS_315H) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + /* TW: From 650/301LV (any, incl. 1.10.6s, 1.10.7w) */ + /* TW: This is a duplicate; done at the end, too */ + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) { SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x2c); + } + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x2a,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); } - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x2a,0x00); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); return; + } } temp = SiS_Pr->SiS_RVBHCFACT; @@ -6833,13 +8116,11 @@ temp = temp2 | ((tempcx & 0xFF00) >> 8); SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x15,temp); - tempcx = SiS_Pr->SiS_VBInfo; tempbx = SiS_Pr->SiS_VGAHDE; if(modeflag & HalfDCLK) tempbx >>= 1; /* TW: New for 650/301LV and 630/301B */ temp = 0xA0; -#ifdef oldHV if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { temp = 0; if(tempbx > 800) { @@ -6850,18 +8131,17 @@ } } } else -#endif if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(tempbx <= 800) { temp = 0x80; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD){ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { temp = 0; if(tempbx > 800) temp = 0x60; } } } else { temp = 0x80; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD){ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { temp = 0; if(tempbx > 800) temp = 0x60; } @@ -6883,11 +8163,9 @@ tempebx = SiS_Pr->SiS_VDE; -#ifdef oldHV if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) { if(!(temp & 0xE0)) tempebx >>=1; } -#endif tempcx = SiS_Pr->SiS_RVBHRS; temp = tempcx & 0x00FF; @@ -6932,7 +8210,8 @@ } } - if((SiS_Pr->SiS_VBInfo & (SetCRT2ToTV | SetPALTV)) && (!(SiS_Pr->SiS_HiVision & 0x03))) { +/* if((SiS_Pr->SiS_VBInfo & (SetCRT2ToTV | SetPALTV)) && (!(SiS_Pr->SiS_HiVision & 0x03))) { */ + if((SiS_Pr->SiS_VBInfo & (SetCRT2ToTV - SetCRT2ToHiVisionTV)) && (!(SiS_Pr->SiS_HiVision & 0x03))) { if(tempax > 800) { tempbx = 8; if(tempax == 1024) @@ -6949,12 +8228,25 @@ tempax--; temp = (tempax & 0xFF00) >> 8; temp &= 0x03; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { /* From 1.10.7w */ + if(ModeNo > 0x13) { /* From 1.10.7w */ + if(resinfo == 8) tempax = 0x1f; /* From 1.10.7w */ + } /* From 1.10.7w */ + } /* From 1.10.7w */ SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x1D,tempax & 0x00FF); temp <<= 4; temp |= tempbx; SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x1E,temp); - temp = 0x0036; + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(IS_SIS650740) { + temp = 0x0026; /* 1.10.7w; 1.10.8r; needs corresponding code in Dis/EnableBridge! */ + } else { + temp = 0x0036; + } + } else { + temp = 0x0036; + } if((SiS_Pr->SiS_VBInfo & (SetCRT2ToTV - SetCRT2ToHiVisionTV)) && (!(SiS_Pr->SiS_HiVision & 0x03))) { temp |= 0x01; @@ -6975,21 +8267,33 @@ SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,0xC0,temp); temp = tempbx & 0x00FF; SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x22,temp); - if( (SiS_Pr->SiS_VBType & (VB_SIS30xLV | VB_SIS30xNEW)) && - (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) ) { - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x24,0x0e); + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x24,0x0e); + } } - /* TW: 650 BIOS does this for all bridge types - assumingly wrong */ if(HwDeviceExtension->jChipType >= SIS_315H) { + /* TW: 650/LV BIOS does this for all bridge types - assumingly wrong */ + /* 315, 330, 650+301B BIOS don't do this at all */ /* TW: This is a duplicate; done for LCDA as well (see above) */ - if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x2c); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x39) & 0x04) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x2c); + } + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x2a,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); } - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x2a,0x00); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); - } + } else if(HwDeviceExtension->jChipType == SIS_300) { + /* TW: 300/301LV BIOS does this for all bridge types - assumingly wrong */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x2a,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x30,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x34,0x10); + } + } } /* 301B */ @@ -6997,7 +8301,7 @@ RefreshRateTableIndex,HwDeviceExtension); } -/* TW: Double-Checked against 650/301LV(x) and 630/301B BIOS */ + void SiS_SetCRT2VCLK(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -7009,12 +8313,12 @@ HwDeviceExtension); if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_A; - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,tempah); - tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_B; - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0B,tempah); - /* TW: New from 650/301LV, LVx BIOS */ - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* 650/301LV: (VB_SIS301LV | VB_SIS302LV)) */ + tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_A; + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,tempah); + tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_B; + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0B,tempah); + if(HwDeviceExtension->jChipType >= SIS_315H) { + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { if((ModeNo == 0x4a) || (ModeNo == 0x38)) { @@ -7025,27 +8329,27 @@ } } } - } else { /* 650/301LVx does not do this anymore, jumps to SetRegs above - BUG? */ - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,0x01); - tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_B; - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0B,tempah); - tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_A; - SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,tempah); + } + } else { + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,0x01); + tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_B; + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0B,tempah); + tempah = SiS_Pr->SiS_VBVCLKData[vclkindex].Part4_A; + SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x0A,tempah); } SiS_SetReg1(SiS_Pr->SiS_Part4Port,0x12,0x00); tempah = 0x08; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { - tempah |= 0x20; - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) tempah |= 0x20; SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x12,tempah); } -/* TW: Double-checked against 650/LVDS (1.10.07), 630/301B/LVDS/LVDS+CH, 650/301LVx (1.10.6s) BIOS */ USHORT SiS_GetVCLK2Ptr(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT tempbx; + const USHORT LCDXlat0VCLK[4] = {VCLK40, VCLK40, VCLK40, VCLK40}; + const USHORT LVDSXlat1VCLK[4] = {VCLK40, VCLK40, VCLK40, VCLK40}; #ifdef SIS300 const USHORT LCDXlat1VCLK300[4] = {VCLK65, VCLK65, VCLK65, VCLK65}; const USHORT LCDXlat2VCLK300[4] = {VCLK108_2,VCLK108_2,VCLK108_2,VCLK108_2}; @@ -7057,10 +8361,7 @@ const USHORT LCDXlat2VCLK310[4] = {VCLK108_2+5,VCLK108_2+5,VCLK108_2+5,VCLK108_2+5}; const USHORT LVDSXlat2VCLK310[4]= {VCLK65+2, VCLK65+2, VCLK65+2, VCLK65+2}; const USHORT LVDSXlat3VCLK310[4]= {VCLK108_2+5,VCLK108_2+5,VCLK108_2+5,VCLK108_2+5}; - /* {VCLK65+2, VCLK65+2, VCLK65+2, VCLK65+2}; - 650/LVDS 1.10.07 */ #endif - const USHORT LCDXlat0VCLK[4] = {VCLK40, VCLK40, VCLK40, VCLK40}; - const USHORT LVDSXlat1VCLK[4] = {VCLK40, VCLK40, VCLK40, VCLK40}; USHORT CRT2Index,VCLKIndex=0; USHORT modeflag,resinfo; const UCHAR *CHTVVCLKPtr=NULL; @@ -7069,25 +8370,23 @@ const USHORT *LVDSXlatVCLK2 = NULL; const USHORT *LVDSXlatVCLK3 = NULL; -#ifdef SIS315H if(HwDeviceExtension->jChipType >= SIS_315H) { +#ifdef SIS315H LCDXlatVCLK1 = LCDXlat1VCLK310; LCDXlatVCLK2 = LCDXlat2VCLK310; LVDSXlatVCLK2 = LVDSXlat2VCLK310; LVDSXlatVCLK3 = LVDSXlat3VCLK310; - } else { #endif + } else { #ifdef SIS300 LCDXlatVCLK1 = LCDXlat1VCLK300; LCDXlatVCLK2 = LCDXlat2VCLK300; LVDSXlatVCLK2 = LVDSXlat2VCLK300; LVDSXlatVCLK3 = LVDSXlat3VCLK300; #endif -#ifdef SIS315H } -#endif - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; CRT2Index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC; @@ -7097,35 +8396,39 @@ CRT2Index = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; } - if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { /* 301 */ + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { /* 30x/B/LV */ - if (SiS_Pr->SiS_SetFlag & ProgrammingCRT2) { + if(SiS_Pr->SiS_SetFlag & ProgrammingCRT2) { CRT2Index >>= 6; if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)){ /* LCD */ if(HwDeviceExtension->jChipType < SIS_315H) { - /* TW: Inserted from 630/301B BIOS */ if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) VCLKIndex = LCDXlat0VCLK[CRT2Index]; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) VCLKIndex = LCDXlatVCLK1[CRT2Index]; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) + VCLKIndex = LCDXlatVCLK1[CRT2Index]; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) + VCLKIndex = LCDXlatVCLK1[CRT2Index]; else VCLKIndex = LCDXlatVCLK2[CRT2Index]; } else { - /* TW: 650/301LV BIOS does not check expanding, 315 does */ - if( (HwDeviceExtension->jChipType > SIS_315PRO) || - (!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding)) ) { + if( (SiS_Pr->SiS_VBType & VB_SIS301LV302LV) || + (!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) ) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { VCLKIndex = 0x19; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + VCLKIndex = 0x12; /* TEMP - guessed */ } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { VCLKIndex = 0x19; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { VCLKIndex = 0x21; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) { + VCLKIndex = 0x45; /* TW: in VBVCLK table */ + if(resinfo == 0x09) VCLKIndex++; } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { VCLKIndex = LCDXlatVCLK1[CRT2Index]; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x960) { - VCLKIndex = 0x45; - if(resinfo == 0x09) VCLKIndex++; } else { VCLKIndex = LCDXlatVCLK2[CRT2Index]; } @@ -7135,16 +8438,23 @@ if(ModeNo > 0x13) { VCLKIndex = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK; } - if(ModeNo <= 0x13) { /* TW: Inserted from 315 BIOS */ - if(SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC == 1) VCLKIndex = 0x42; + if(ModeNo <= 0x13) { + if(HwDeviceExtension->jChipType <= SIS_315PRO) { + if(SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC == 1) VCLKIndex = 0x42; + } else { + if(SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC == 1) VCLKIndex = 0x00; + } + } + if(HwDeviceExtension->jChipType <= SIS_315PRO) { + if(VCLKIndex == 0) VCLKIndex = 0x41; + if(VCLKIndex == 1) VCLKIndex = 0x43; + if(VCLKIndex == 4) VCLKIndex = 0x44; } - if(VCLKIndex == 0) VCLKIndex = 0x41; - if(VCLKIndex == 1) VCLKIndex = 0x43; - if(VCLKIndex == 4) VCLKIndex = 0x44; } } } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { /* TV */ - if((SiS_Pr->SiS_IF_DEF_HiVision == 1) && (SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV)) { + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) && + (!(SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) ) { if(SiS_Pr->SiS_SetFlag & RPLLDIV2XO) VCLKIndex = HiTVVCLKDIV2; else VCLKIndex = HiTVVCLK; if(SiS_Pr->SiS_SetFlag & TVSimuMode) { @@ -7199,13 +8509,29 @@ VCLKIndex &= 0x1f; tempbx = 0; - if(SiS_Pr->SiS_VBInfo & SetPALTV) tempbx += 2; - if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + if(SiS_Pr->SiS_VBInfo & SetPALTV) { + tempbx += 2; + if(SiS_Pr->SiS_CHSOverScan) tempbx = 8; + if(SiS_Pr->SiS_CHPALM) { + tempbx = 4; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + } else if(SiS_Pr->SiS_CHPALN) { + tempbx = 6; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx += 1; + } + } switch(tempbx) { - case 0: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUNTSC; break; - case 1: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKONTSC; break; - case 2: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUPAL; break; - case 3: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKOPAL; break; + case 0: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUNTSC; break; + case 1: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKONTSC; break; + case 2: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUPAL; break; + case 3: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKOPAL; break; + case 4: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUPALM; break; + case 5: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKOPALM; break; + case 6: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKUPALN; break; + case 7: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKOPALN; break; + case 8: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKSOPAL; break; + default: CHTVVCLKPtr = SiS_Pr->SiS_CHTVVCLKOPAL; break; } VCLKIndex = CHTVVCLKPtr[VCLKIndex]; @@ -7213,12 +8539,14 @@ VCLKIndex >>= 6; if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel800x600) || - (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel320x480)) + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel320x480)) VCLKIndex = LVDSXlat1VCLK[VCLKIndex]; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) VCLKIndex = LVDSXlatVCLK2[VCLKIndex]; else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) VCLKIndex = LVDSXlatVCLK2[VCLKIndex]; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) + VCLKIndex = LVDSXlatVCLK2[VCLKIndex]; else VCLKIndex = LVDSXlatVCLK3[VCLKIndex]; } else { @@ -7227,12 +8555,14 @@ VCLKIndex = ((VCLKIndex >> 2) & 0x03); if(ModeNo > 0x13) { VCLKIndex = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK; + if(HwDeviceExtension->jChipType < SIS_315H) { + VCLKIndex &= 0x3F; + } if( (HwDeviceExtension->jChipType == SIS_630) && (HwDeviceExtension->jChipRevision >= 0x30) ) { if(VCLKIndex == 0x14) VCLKIndex = 0x2e; } } - } } else { /* if not programming CRT2 */ @@ -7242,20 +8572,26 @@ if(ModeNo > 0x13) { VCLKIndex = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK; if(HwDeviceExtension->jChipType < SIS_315H) { + VCLKIndex &= 0x3F; if( (HwDeviceExtension->jChipType != SIS_630) && (HwDeviceExtension->jChipType != SIS_300) ) { if(VCLKIndex == 0x1b) VCLKIndex = 0x35; } +#if 0 + if(HwDeviceExtension->jChipType == SIS_730) { + if(VCLKIndex == 0x0b) VCLKIndex = 0x40; /* 1024x768-70 */ + if(VCLKIndex == 0x0d) VCLKIndex = 0x41; /* 1024x768-75 */ + } +#endif } } } } - - if(HwDeviceExtension->jChipType < SIS_315H) { - VCLKIndex &= 0x3F; - } +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "VCLKIndex %d (0x%x)\n", VCLKIndex, VCLKIndex); +#endif return (VCLKIndex); } @@ -7269,15 +8605,13 @@ if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) return; if(SiS_Pr->SiS_ModeType == ModeVGA){ - if(!(SiS_Pr->SiS_VBInfo & (SetInSlaveMode | LoadDACFlag))){ - SiS_EnableCRT2(SiS_Pr); - SiS_LoadDAC(SiS_Pr,HwDeviceExtension,ROMAddr,ModeNo,ModeIdIndex); - } + if(!(SiS_Pr->SiS_VBInfo & (SetInSlaveMode | LoadDACFlag))){ + SiS_EnableCRT2(SiS_Pr); + SiS_LoadDAC(SiS_Pr,HwDeviceExtension,ROMAddr,ModeNo,ModeIdIndex); + } } - return; } -/* TW: Checked against 650/LVDS and 630/301B BIOS */ void SiS_ModCRT1CRTC(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) @@ -7297,9 +8631,8 @@ if(temp == 0) return; - /* TW: Inserted from 630/LVDS BIOS */ if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_SetFlag & CRT2IsVGA) return; + if(SiS_Pr->SiS_SetFlag & SetDOSMode) return; } switch(DisplayType) { @@ -7338,6 +8671,12 @@ case 37: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_1_H; break; case 38: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_2; break; case 39: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11600x1200_2_H; break; + case 40: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x768_1; break; + case 41: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x768_1_H; break; + case 42: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x768_2; break; + case 43: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x768_2_H; break; + case 99: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1SOPAL; break; + default: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_1; break; } SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x11,0x7f); /*unlock cr0-7 */ @@ -7368,116 +8707,21 @@ tempah = (LVDSCRT1Ptr+ResInfo)->CR[14]; tempah &= 0xE0; - SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x0E,0x1f,tempah); /* TW: Modfied (650/LVDS); Was SetReg(tempah) */ + SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x0E,0x1f,tempah); tempah = (LVDSCRT1Ptr+ResInfo)->CR[14]; tempah &= 0x01; tempah <<= 5; - if(modeflag & DoubleScanMode){ - tempah |= 0x080; - } + if(modeflag & DoubleScanMode) tempah |= 0x080; SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x09,~0x020,tempah); - /* TW: Inserted from 650/LVDS BIOS */ + /* TW: 650/LVDS BIOS - doesn't make sense */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(modeflag & HalfDCLK) SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x11,0x7f); } - - return; -} - -#if 0 /* TW: Unused */ -/*301b*/ -void -SiS_CHACRT1CRTC(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, - USHORT RefreshRateTableIndex) -{ - USHORT temp,tempah,i,modeflag,j; - USHORT ResInfo,DisplayType; - SiS_LVDSCRT1DataStruct *LVDSCRT1Ptr=NULL; - - if(ModeNo<=0x13) { - modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; /* si+St_ResInfo */ - } else { - modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; /* si+Ext_ResInfo */ - } - - temp=SiS_GetLVDSCRT1Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex,RefreshRateTableIndex, - &ResInfo,&DisplayType); - if(temp==0){ - return; - } - - switch(DisplayType) { - case 0 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1800x600_1; break; - case 1 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_1; break; - case 2 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x1024_1; break; - case 3 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1800x600_1_H; break; - case 4 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_1_H; break; - case 5 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x1024_1_H; break; - case 6 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1800x600_2; break; - case 7 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_2; break; - case 8 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x1024_2; break; - case 9 : LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1800x600_2_H; break; - case 10: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11024x768_2_H; break; - case 11: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11280x1024_2_H; break; - case 12: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1XXXxXXX_1; break; - case 13: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1XXXxXXX_1_H; break; - case 14: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11400x1050_1; break; - case 15: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11400x1050_1_H; break; - case 16: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11400x1050_2; break; - case 17: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT11400x1050_2_H; break; - case 18: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1UNTSC; break; - case 19: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1ONTSC; break; - case 20: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1UPAL; break; - case 21: LVDSCRT1Ptr = SiS_Pr->SiS_CHTVCRT1OPAL; break; - case 22: LVDSCRT1Ptr = SiS_Pr->SiS_LVDSCRT1320x480_1; break; /* FSTN */ - } - - tempah=(UCHAR)SiS_GetReg1(SiS_Pr->SiS_P3d4,0x11); /*unlock cr0-7 */ - tempah=tempah&0x7F; - SiS_SetReg1(SiS_Pr->SiS_P3d4,0x11,tempah); - tempah = (LVDSCRT1Ptr+ResInfo)->CR[0]; - SiS_SetReg1(SiS_Pr->SiS_P3d4,0x0,tempah); - for(i=0x02,j=1;i<=0x05;i++,j++){ - tempah = (LVDSCRT1Ptr+ResInfo)->CR[j]; - SiS_SetReg1(SiS_Pr->SiS_P3d4,i,tempah); - } - for(i=0x06,j=5;i<=0x07;i++,j++){ - tempah = (LVDSCRT1Ptr+ResInfo)->CR[j]; - SiS_SetReg1(SiS_Pr->SiS_P3d4,i,tempah); - } - for(i=0x10,j=7;i<=0x11;i++,j++){ - tempah = (LVDSCRT1Ptr+ResInfo)->CR[j]; - SiS_SetReg1(SiS_Pr->SiS_P3d4,i,tempah); - } - for(i=0x15,j=9;i<=0x16;i++,j++){ - tempah = (LVDSCRT1Ptr+ResInfo)->CR[j]; - SiS_SetReg1(SiS_Pr->SiS_P3d4,i,tempah); - } - - for(i=0x0A,j=11;i<=0x0C;i++,j++){ - tempah = (LVDSCRT1Ptr+ResInfo)->CR[j]; - SiS_SetReg1(SiS_Pr->SiS_P3c4,i,tempah); - } - - tempah = (LVDSCRT1Ptr+ResInfo)->CR[14]; - tempah=tempah&0x0E0; - SiS_SetReg1(SiS_Pr->SiS_P3c4,0x0E,tempah); - - tempah = (LVDSCRT1Ptr+ResInfo)->CR[14]; - tempah=tempah&0x01; - tempah=tempah<<5; - if(modeflag&DoubleScanMode){ - tempah=tempah|0x080; - } - SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x09,~0x020,tempah); - return; } -#endif -/* TW: Checked against 650/LVDS BIOS: modified for new panel resolutions */ BOOLEAN SiS_GetLVDSCRT1Ptr(SiS_Private *SiS_Pr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,USHORT *ResInfo, @@ -7487,10 +8731,11 @@ USHORT Flag,CRT2CRTC; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - /* TW: Inserted from 650/LVDS BIOS */ - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { - if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) return 0; - } + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { + if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) return 0; + } + } else { + if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) return 0; } if(ModeNo <= 0x13) { @@ -7504,45 +8749,60 @@ Flag = 1; tempbx = 0; if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { - Flag = 0; - tempbx = 18; - if(SiS_Pr->SiS_VBInfo & SetPALTV) tempbx += 2; - if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx++; - } + if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) { + Flag = 0; + tempbx = 18; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx++; + if(SiS_Pr->SiS_VBInfo & SetPALTV) { + tempbx += 2; + if(SiS_Pr->SiS_CHSOverScan) tempbx = 99; + if(SiS_Pr->SiS_CHPALM) { + tempbx = 18; /* PALM uses NTSC data */ + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx++; + } else if(SiS_Pr->SiS_CHPALN) { + tempbx = 20; /* PALN uses PAL data */ + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) tempbx++; + } + } + } } if(Flag) { - tempbx = SiS_Pr->SiS_LCDResInfo; - tempbx -= SiS_Pr->SiS_PanelMinLVDS; - if(SiS_Pr->SiS_LCDResInfo <= SiS_Pr->SiS_Panel1280x1024) { - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 6; - if(modeflag & HalfDCLK) tempbx += 3; - } else { - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { + tempbx = SiS_Pr->SiS_LCDResInfo; + tempbx -= SiS_Pr->SiS_PanelMinLVDS; + if(SiS_Pr->SiS_LCDResInfo <= SiS_Pr->SiS_Panel1280x1024) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 6; + if(modeflag & HalfDCLK) tempbx += 3; + } else { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { tempbx = 14; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; if(modeflag & HalfDCLK) tempbx++; - } else if(SiS_Pr->SiS_LCDInfo & LCDPass11) { - tempbx = 12; - if(modeflag & HalfDCLK) tempbx++; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x600) { tempbx = 23; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; if(modeflag & HalfDCLK) tempbx++; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1152x768) { tempbx = 27; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; if(modeflag & HalfDCLK) tempbx++; - } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { tempbx = 36; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; if(modeflag & HalfDCLK) tempbx++; - } - } + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x768) { + tempbx = 40; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 2; + if(modeflag & HalfDCLK) tempbx++; + } + } + if(SiS_Pr->SiS_LCDInfo & LCDPass11) { + tempbx = 12; + if(modeflag & HalfDCLK) tempbx++; + } } if(SiS_Pr->SiS_IF_DEF_FSTN){ - if(SiS_Pr->SiS_LCDResInfo==SiS_Pr->SiS_Panel320x480){ - tempbx=22; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel320x480){ + tempbx = 22; } } *ResInfo = CRT2CRTC & 0x3F; @@ -7550,20 +8810,13 @@ return 1; } -/* TW: Checked against 650/LVDS (1.10a, 1.10.07), 630/301B (I/II) and 630/LVDS BIOS */ void SiS_SetCRT2ECLK(SiS_Private *SiS_Pr, UCHAR *ROMAddr, USHORT ModeNo,USHORT ModeIdIndex, USHORT RefreshRateTableIndex,PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT tempah,tempal,pushax; USHORT vclkindex=0; - - if(HwDeviceExtension->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD)) return; - } - } - + if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel640x480) || (SiS_Pr->SiS_IF_DEF_TRUMPION == 1)) { SiS_Pr->SiS_SetFlag &= (~ProgrammingCRT2); tempal = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK; @@ -7576,7 +8829,7 @@ vclkindex = SiS_GetVCLK2Ptr(SiS_Pr,ROMAddr,ModeNo,ModeIdIndex, RefreshRateTableIndex,HwDeviceExtension); } - + tempal = 0x02B; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { if(!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) { @@ -7644,19 +8897,37 @@ tempcl = SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC; TVType = 0; - if(SiS_Pr->SiS_VBInfo & SetPALTV) TVType += 2; if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) TVType += 1; + if(SiS_Pr->SiS_VBInfo & SetPALTV) { + TVType += 2; + if(SiS_Pr->SiS_CHSOverScan) TVType = 8; + if(SiS_Pr->SiS_CHPALM) { + TVType = 4; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) TVType += 1; + } else if(SiS_Pr->SiS_CHPALN) { + TVType = 6; + if(SiS_Pr->SiS_VBInfo & SetCHTVOverScan) TVType += 1; + } + } switch(TVType) { - case 0: CHTVRegData = SiS_Pr->SiS_CHTVReg_UNTSC; break; - case 1: CHTVRegData = SiS_Pr->SiS_CHTVReg_ONTSC; break; - case 2: CHTVRegData = SiS_Pr->SiS_CHTVReg_UPAL; break; - case 3: CHTVRegData = SiS_Pr->SiS_CHTVReg_OPAL; break; + case 0: CHTVRegData = SiS_Pr->SiS_CHTVReg_UNTSC; break; + case 1: CHTVRegData = SiS_Pr->SiS_CHTVReg_ONTSC; break; + case 2: CHTVRegData = SiS_Pr->SiS_CHTVReg_UPAL; break; + case 3: CHTVRegData = SiS_Pr->SiS_CHTVReg_OPAL; break; + case 4: CHTVRegData = SiS_Pr->SiS_CHTVReg_UPALM; break; + case 5: CHTVRegData = SiS_Pr->SiS_CHTVReg_OPALM; break; + case 6: CHTVRegData = SiS_Pr->SiS_CHTVReg_UPALN; break; + case 7: CHTVRegData = SiS_Pr->SiS_CHTVReg_OPALN; break; + case 8: CHTVRegData = SiS_Pr->SiS_CHTVReg_SOPAL; break; + default: CHTVRegData = SiS_Pr->SiS_CHTVReg_OPAL; break; } resindex = tempcl & 0x3F; if(SiS_Pr->SiS_IF_DEF_CH70xx == 1) { - /* Chrontel 7005 */ +#ifdef SIS300 + + /* Chrontel 7005 - I assume that it does not come with a 310/325 series chip */ /* TW: We don't support modes >800x600 */ if (resindex > 5) return; @@ -7712,7 +8983,7 @@ SiS_SetCH70xxANDOR(SiS_Pr,0x0010,0x1F); /* TW: Register 0x11 only contains 3 writable bits (S0-S2) for - contrast enhancement (set to 010 -> gain 2 Yout = 9/8*(Yin-57) ) + contrast enhancement (set to 010 -> gain 1 Yout = 17/16*(Yin-30) ) */ SiS_SetCH70xxANDOR(SiS_Pr,0x0211,0xF8); @@ -7768,10 +9039,14 @@ SiS_SetCH70xxANDOR(SiS_Pr,0x0121,0xFE); /* ACIV on */ } } + +#endif /* 300 */ } else { - /* Chrontel 7019 */ + /* Chrontel 7019 - assumed that it does not come with a 300 series chip */ + +#ifdef SIS315H /* TW: We don't support modes >1024x768 */ if (resindex > 6) return; @@ -7839,93 +9114,181 @@ temp = CHTVRegData[resindex].Reg[15]; tempbx=((temp & 0x00FF) <<8 ) | 0x10; SiS_SetCH701x(SiS_Pr,tempbx); + +#endif /* 315 */ + + } +} + +/* TW: Chrontel 701x functions ================================= */ + +void +SiS_Chrontel701xBLOn(SiS_Private *SiS_Pr) +{ +#ifndef NEWCH701x + USHORT temp; +#endif + + /* TW: Enable Chrontel 7019 LCD panel backlight */ + if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { +#ifdef NEWCH701x + SiS_SetCH701x(SiS_Pr,0x6566); +#else + temp = SiS_GetCH701x(SiS_Pr,0x66); + temp |= 0x20; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x66); +#endif + } +} + +void +SiS_Chrontel701xBLOff(SiS_Private *SiS_Pr) +{ + USHORT temp; + /* TW: Disable Chrontel 7019 LCD panel backlight */ + if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { + temp = SiS_GetCH701x(SiS_Pr,0x66); + temp &= 0xDF; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x66); } } +#ifdef SIS315H /* -------- 310/325 series only --------- */ + void SiS_SetCH701xForLCD(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr) { +#ifdef NEWCH701x + UCHAR regtable[] = { 0x1c, 0x5f, 0x64, 0x6f, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x76, 0x78, 0x7d, 0x66 }; + UCHAR table1024[] = { 0x60, 0x02, 0x00, 0x07, 0x40, 0xed, + 0xa3, 0xc8, 0xc7, 0xac, 0xe0, 0x02, 0x44 }; + UCHAR table1280[] = { 0x60, 0x03, 0x11, 0x00, 0x40, 0xe3, + 0xad, 0xdb, 0xf6, 0xac, 0xe0, 0x02, 0x44 }; + UCHAR table1400[] = { 0x60, 0x03, 0x11, 0x00, 0x40, 0xe3, + 0xad, 0xdb, 0xf6, 0xac, 0xe0, 0x02, 0x44 }; + UCHAR table1600[] = { 0x60, 0x04, 0x11, 0x00, 0x40, 0xe3, + 0xad, 0xde, 0xf6, 0xac, 0x60, 0x1a, 0x44 }; +#else UCHAR regtable[] = { 0x1c, 0x5f, 0x64, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x76, 0x78, 0x7d }; - UCHAR table28b4[] = { 0x60, 0x02, 0x00, 0x07, 0x40, 0xed, - 0xa3, 0xc8, 0xc7, 0xac, 0x60, 0x02 }; - UCHAR table28c0[] = { 0x60, 0x03, 0x11, 0x00, 0x40, 0xef, - 0xad, 0xdb, 0xf6, 0xac, 0x60, 0x02 }; + UCHAR table1024[] = { 0x60, 0x02, 0x00, 0x07, 0x40, 0xed, + 0xa3, 0xc8, 0xc7, 0xac, 0x60, 0x02 }; + UCHAR table1280[] = { 0x60, 0x03, 0x11, 0x00, 0x40, 0xe3, + 0xad, 0xdb, 0xf6, 0xac, 0xe0, 0x02 }; + UCHAR table1400[] = { 0x60, 0x03, 0x11, 0x00, 0x40, 0xef, + 0xad, 0xdb, 0xf6, 0xac, 0x60, 0x02 }; + UCHAR table1600[] = { 0x60, 0x04, 0x11, 0x00, 0x40, 0xe3, + 0xad, 0xde, 0xf6, 0xac, 0x60, 0x1a }; +#endif UCHAR *tableptr = NULL; USHORT tempbh; int i; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - tableptr = table28c0; - } else { - tableptr = table28b4; - } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { + tableptr = table1024; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) { + tableptr = table1280; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { + tableptr = table1400; + } else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) { + tableptr = table1600; + } else return; + tempbh = SiS_GetCH701x(SiS_Pr,0x74); if((tempbh == 0xf6) || (tempbh == 0xc7)) { tempbh = SiS_GetCH701x(SiS_Pr,0x73); if(tempbh == 0xc8) { - if(SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1400x1050) return; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) return; } else if(tempbh == 0xdb) { + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) return; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) return; + } else if(tempbh == 0xde) { if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) return; } } +#ifdef NEWCH701x /* New from 740/LVDS: */ + for(i=0; i<0x0d; i++) { +#else for(i=0; i<0x0c; i++) { +#endif SiS_SetCH701x(SiS_Pr,(tableptr[i] << 8) | regtable[i]); } - SiS_Chrontel19f2(SiS_Pr); + SiS_ChrontelPowerSequencing(SiS_Pr); tempbh = SiS_GetCH701x(SiS_Pr,0x1e); tempbh |= 0xc0; - SiS_SetCH701x(SiS_Pr,(tempbh << 8) | 0x1e); + SiS_SetCH701x(SiS_Pr,(tempbh << 8) | 0x1e); + +#ifdef NEWCH701x /* 740/LVDS: */ + tempbh = SiS_GetCH701x(SiS_Pr,0x1c); + tempbh &= 0xfb; + SiS_SetCH701x(SiS_Pr,(tempbh << 8) | 0x1c); + SiS_SetReg1(SiS_Pr->SiS_Part1Port, 0x2d, 0x03); + tempbh = SiS_GetCH701x(SiS_Pr,0x64); + tempbh |= 0x40; + SiS_SetCH701x(SiS_Pr,(tempbh << 8) | 0x64); + tempbh = SiS_GetCH701x(SiS_Pr,0x03); + tempbh &= 0x3f; + SiS_SetCH701x(SiS_Pr,(tempbh << 8) | 0x03); +#endif /* End 740/LVDS */ } -/* TW: Chrontel 701x functions ================================= */ - void -SiS_Chrontel19f2(SiS_Private *SiS_Pr) +SiS_ChrontelPowerSequencing(SiS_Private *SiS_Pr) { UCHAR regtable[] = { 0x67, 0x68, 0x69, 0x6a, 0x6b }; - UCHAR table19e8[] = { 0x01, 0x02, 0x01, 0x01, 0x02 }; - UCHAR table19ed[] = { 0x01, 0x02, 0x01, 0x01, 0x02 }; +#ifdef NEWCH701x + UCHAR table1024[] = { 0x01, 0x02, 0x01, 0x01, 0x01 }; + UCHAR table1400[] = { 0x01, 0x6e, 0x01, 0x01, 0x01 }; +#else + UCHAR table1024[] = { 0x01, 0x02, 0x01, 0x01, 0x02 }; + UCHAR table1400[] = { 0x01, 0x02, 0x01, 0x01, 0x02 }; +#endif UCHAR *tableptr = NULL; int i; - if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { - tableptr = table19ed; - } else { - tableptr = table19e8; - } - + /* Set up Power up/down timing */ + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { + tableptr = table1024; + } else if((SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1280x1024) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) || + (SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200)) { + tableptr = table1400; + } else return; + for(i=0; i<5; i++) { SiS_SetCH701x(SiS_Pr,(tableptr[i] << 8) | regtable[i]); } } void -SiS_Chrontel701xBLOn(SiS_Private *SiS_Pr) -{ - USHORT temp; - - /* TW: Enable Chrontel 7019 LCD panel backlight */ - if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { - temp = SiS_GetCH701x(SiS_Pr,0x66); - temp |= 0x20; - SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x66); - } -} - -void SiS_Chrontel701xOn(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr) { USHORT temp; if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { +#ifdef NEWCH701x + temp = SiS_GetCH701x(SiS_Pr,0x1c); + temp |= 0x04; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x1c); +#endif if(SiS_IsYPbPr(SiS_Pr,HwDeviceExtension, BaseAddr)) { temp = SiS_GetCH701x(SiS_Pr,0x01); temp &= 0x3f; - temp |= 0x80; + temp |= 0x80; /* TW: Enable YPrPb (HDTV) */ + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x01); + } + if(SiS_IsChScart(SiS_Pr,HwDeviceExtension, BaseAddr)) { + temp = SiS_GetCH701x(SiS_Pr,0x01); + temp &= 0x3f; + temp |= 0xc0; /* TW: Enable SCART + CVBS */ SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x01); } +#ifdef NEWCH701x + SiS_ChrontelDoSomething5(SiS_Pr); + SiS_SetCH701x(SiS_Pr,0x2049); /* TW: Enable TV path */ +#else SiS_SetCH701x(SiS_Pr,0x2049); /* TW: Enable TV path */ temp = SiS_GetCH701x(SiS_Pr,0x49); if(SiS_IsYPbPr(SiS_Pr,HwDeviceExtension, BaseAddr)) { @@ -7940,19 +9303,7 @@ temp = SiS_GetCH701x(SiS_Pr,0x47); temp |= 0x80; SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); - } -} - -void -SiS_Chrontel701xBLOff(SiS_Private *SiS_Pr) -{ - USHORT temp; - - /* TW: Disable Chrontel 7019 LCD panel backlight */ - if(SiS_Pr->SiS_IF_DEF_CH70xx == 2) { - temp = SiS_GetCH701x(SiS_Pr,0x66); - temp &= 0xDF; - SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x66); +#endif } } @@ -7971,40 +9322,118 @@ } } +#ifdef NEWCH701x +void +SiS_ChrontelDoSomething5(SiS_Private *SiS_Pr) +{ + unsigned char temp, temp1; + + temp1 = SiS_GetCH701x(SiS_Pr,0x49); + SiS_SetCH701x(SiS_Pr,0x3e49); + temp = SiS_GetCH701x(SiS_Pr,0x47); + temp &= 0x7f; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); + SiS_LongDelay(SiS_Pr,3); + temp = SiS_GetCH701x(SiS_Pr,0x47); + temp |= 0x80; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); + SiS_SetCH701x(SiS_Pr,(temp1 << 8) | 0x49); +} +#endif + void -SiS_ChrontelResetDB(SiS_Private *SiS_Pr) +SiS_ChrontelResetDB(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { +#ifdef NEWCH701x + USHORT temp; + + /* 740/LVDS: */ + temp = SiS_GetCH701x(SiS_Pr,0x4a); + temp &= 0x01; + if(!(temp)) { + + if(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr)) { + temp = SiS_GetCH701x(SiS_Pr,0x49); + SiS_SetCH701x(SiS_Pr,0x3e49); + } + /* TW: Reset Chrontel 7019 datapath */ + SiS_SetCH701x(SiS_Pr,0x1048); + SiS_LongDelay(SiS_Pr,1); + SiS_SetCH701x(SiS_Pr,0x1848); + + if(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr)) { + SiS_ChrontelDoSomething5(SiS_Pr); + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x49); + } + } else { + + temp = SiS_GetCH701x(SiS_Pr,0x5c); + temp &= 0xef; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x5c); + temp = SiS_GetCH701x(SiS_Pr,0x5c); + temp |= 0x10; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x5c); + temp = SiS_GetCH701x(SiS_Pr,0x5c); + temp &= 0xef; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x5c); + temp = SiS_GetCH701x(SiS_Pr,0x61); + if(!temp) { + SiS_SetCH701xForLCD(SiS_Pr,HwDeviceExtension,BaseAddr); + } + } +#else /* pre 740/LVDS code */ /* TW: Reset Chrontel 7019 datapath */ SiS_SetCH701x(SiS_Pr,0x1048); SiS_LongDelay(SiS_Pr,1); SiS_SetCH701x(SiS_Pr,0x1848); +#endif } void SiS_ChrontelDoSomething4(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { +#ifdef NEWCH701x + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwDeviceExtension, BaseAddr))) { + SiS_ChrontelDoSomething5(SiS_Pr); + } +#else USHORT temp; - SiS_SetCH701x(SiS_Pr,0xaf76); + SiS_SetCH701x(SiS_Pr,0xaf76); /* Power up LVDS block */ temp = SiS_GetCH701x(SiS_Pr,0x49); temp &= 1; - if(temp != 1) { + if(temp != 1) { /* TV block powered? (0 = yes, 1 = no) */ temp = SiS_GetCH701x(SiS_Pr,0x47); temp &= 0x70; - SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); /* enable VSYNC */ SiS_LongDelay(SiS_Pr,3); temp = SiS_GetCH701x(SiS_Pr,0x47); temp |= 0x80; - SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x47); /* disable VSYNC */ } +#endif } void -SiS_ChrontelDoSomething3(SiS_Private *SiS_Pr, USHORT ModeNo,PSIS_HW_DEVICE_INFO HwDeviceExtension, +SiS_ChrontelDoSomething3(SiS_Private *SiS_Pr, USHORT ModeNo, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr) { - USHORT temp,temp1; +#ifdef NEWCH701x + USHORT temp; + + temp = SiS_GetCH701x(SiS_Pr,0x61); + if(temp < 1) { + temp++; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x61); + } + SiS_SetCH701x(SiS_Pr,0x4566); + SiS_SetCH701x(SiS_Pr,0xaf76); + SiS_LongDelay(SiS_Pr,1); + SiS_GenericDelay(SiS_Pr,0x16ff); +#else + USHORT temp,temp1; + temp1 = 0; temp = SiS_GetCH701x(SiS_Pr,0x61); if(temp < 2) { @@ -8033,6 +9462,7 @@ temp &= 0x7f; SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x66); SiS_LongDelay(SiS_Pr,1); +#endif } void @@ -8048,12 +9478,16 @@ temp = SiS_GetCH701x(SiS_Pr,0x66); temp &= 0x04; if(temp == 0x04) break; + +#ifdef NEWCH701x + SiS_SetCH701x(SiS_Pr,0xac76); /* 740/LVDS */ +#endif - SiS_SetCH701xForLCD(SiS_Pr,HwDeviceExtension, BaseAddr); + SiS_SetCH701xForLCD(SiS_Pr,HwDeviceExtension,BaseAddr); if(tempcl == 0) { if(tempch == 3) break; - SiS_ChrontelResetDB(SiS_Pr); + SiS_ChrontelResetDB(SiS_Pr,HwDeviceExtension,BaseAddr); tempcl = 3; tempch++; } @@ -8065,7 +9499,11 @@ temp = SiS_GetCH701x(SiS_Pr,0x76); temp |= 0x04; SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x76); +#ifdef NEWCH701x + SiS_SetCH701x(SiS_Pr,0xe078); +#else SiS_SetCH701x(SiS_Pr,0x6078); +#endif SiS_LongDelay(SiS_Pr,2); } while(0); @@ -8079,20 +9517,50 @@ USHORT temp; temp = SiS_GetCH701x(SiS_Pr,0x03); - temp |= 0x80; - temp &= 0xbf; + temp |= 0x80; /* Set datapath 1 to TV */ + temp &= 0xbf; /* Set datapath 2 to LVDS */ + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x03); + +#ifdef NEWCH701x /* 740/LVDS: */ + + temp = SiS_GetCH701x(SiS_Pr,0x1c); + temp &= 0xfb; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x1c); + + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x2d,0x03); + + temp = SiS_GetCH701x(SiS_Pr,0x64); + temp |= 0x40; + SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x64); + + temp = SiS_GetCH701x(SiS_Pr,0x03); + temp &= 0x3f; SiS_SetCH701x(SiS_Pr,(temp << 8) | 0x03); + + temp = SiS_GetCH701x(SiS_Pr,0x66); + if(temp != 0x45) { + SiS_ChrontelResetDB(SiS_Pr,HwDeviceExtension,BaseAddr); + SiS_ChrontelDoSomething2(SiS_Pr,HwDeviceExtension,BaseAddr); + temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x34); + SiS_ChrontelDoSomething3(SiS_Pr,temp,HwDeviceExtension,BaseAddr); + } + +#else /* pre-740/LVDS: */ SiS_ChrontelResetDB(SiS_Pr); - SiS_ChrontelDoSomething2(SiS_Pr,HwDeviceExtension, BaseAddr); + SiS_ChrontelDoSomething2(SiS_Pr,HwDeviceExtension,BaseAddr); temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x34); - SiS_ChrontelDoSomething3(SiS_Pr,temp, HwDeviceExtension, BaseAddr); + SiS_ChrontelDoSomething3(SiS_Pr,temp,HwDeviceExtension,BaseAddr); SiS_SetCH701x(SiS_Pr,0xaf76); + +#endif /* End of pre-740/LVDS */ } +#endif /* 310/325 series --------------------------------- */ + /* TW: End of Chrontel 701x functions ==================================== */ /* TW: Generic Read/write routines for Chrontel ========================== */ @@ -8316,12 +9784,20 @@ #ifdef LINUX_XF86 /* TW: Our own DDC functions */ USHORT -SiS_InitDDCRegs(SiS_Private *SiS_Pr, SISPtr pSiS, USHORT adaptnum, USHORT DDCdatatype) +SiS_InitDDCRegs(SiS_Private *SiS_Pr, SISPtr pSiS, USHORT adaptnum, USHORT DDCdatatype, + BOOLEAN checkcr32) { - unsigned char ddcdtype[] = { 0xa0, 0xa0, 0xa0, 0xa2, 0xa6}; + unsigned char ddcdtype[] = { 0xa0, 0xa0, 0xa0, 0xa2, 0xa6 }; unsigned char flag, cr32; USHORT temp = 0, myadaptnum = adaptnum; + if(adaptnum != 0) { + if(!(pSiS->VBFlags & (VB_301|VB_301B|VB_302B))) return 0xFFFF; + if((pSiS->VBFlags & VB_30xBDH) && (adaptnum == 1)) return 0xFFFF; + } + + /* adapternum for SiS bridges: 0 = CRT1, 1 = LCD, 2 = VGA2 */ + SiS_Pr->SiS_ChrontelInit = 0; /* force re-detection! */ SiS_Pr->SiS_DDC_SecAddr = 0; @@ -8331,23 +9807,23 @@ flag = 0xff; cr32 = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x32); - - if(pSiS->VGAEngine == SIS_300_VGA) { /* 300 series */ - - if(pSiS->VBFlags & VB_SISBRIDGE) { - if(myadaptnum == 0) { - if(!(cr32 & 0x20)) { - myadaptnum = 2; - if(!(cr32 & 0x10)) { - myadaptnum = 1; - if(!(cr32 & 0x08)) { - myadaptnum = 0; - } + + if(pSiS->VBFlags & VB_SISBRIDGE) { + if(myadaptnum == 0) { + if(!(cr32 & 0x20)) { + myadaptnum = 2; + if(!(cr32 & 0x10)) { + myadaptnum = 1; + if(!(cr32 & 0x08)) { + myadaptnum = 0; } - } + } } - } + } + } + if(pSiS->VGAEngine == SIS_300_VGA) { /* 300 series */ + if(myadaptnum != 0) { flag = 0; if(pSiS->VBFlags & VB_SISBRIDGE) { @@ -8356,37 +9832,25 @@ } } - if(cr32 & 0x80) { - if(myadaptnum >= 1) { - if(!(cr32 & 0x08)) { - myadaptnum = 1; - if(!(cr32 & 0x10)) return 0xFFFF; - } + if(!(pSiS->VBFlags & VB_301)) { + if((cr32 & 0x80) && (checkcr32)) { + if(myadaptnum >= 1) { + if(!(cr32 & 0x08)) { + myadaptnum = 1; + if(!(cr32 & 0x10)) return 0xFFFF; + } + } } } temp = 4 - (myadaptnum * 2); if(flag) temp = 0; - SiS_Pr->SiS_DDC_Data = 0x02 << temp; - SiS_Pr->SiS_DDC_Clk = 0x01 << temp; - - } else { /* 310/325 series */ - - if(pSiS->VBFlags & (VB_30xLV|VB_30xLVX)) myadaptnum = 0; + } else { /* 310/325/330 series */ + /* here we simplify: 0 = CRT1, 1 = CRT2 (VGA, LCD) */ + if(pSiS->VBFlags & VB_SISBRIDGE) { - if(myadaptnum == 0) { - if(!(cr32 & 0x20)) { - myadaptnum = 2; - if(!(cr32 & 0x10)) { - myadaptnum = 1; - if(!(cr32 & 0x08)) { - myadaptnum = 0; - } - } - } - } if(myadaptnum == 2) { myadaptnum = 1; } @@ -8400,7 +9864,7 @@ } } - if(cr32 & 0x80) { + if((cr32 & 0x80) && (checkcr32)) { if(myadaptnum >= 1) { if(!(cr32 & 0x08)) { myadaptnum = 1; @@ -8416,28 +9880,48 @@ } if(flag) temp = 0; - - SiS_Pr->SiS_DDC_Data = 0x02 << temp; - SiS_Pr->SiS_DDC_Clk = 0x01 << temp; - } + + SiS_Pr->SiS_DDC_Data = 0x02 << temp; + SiS_Pr->SiS_DDC_Clk = 0x01 << temp; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "DDC Port %x Index %x Shift %d\n", + SiS_Pr->SiS_DDC_Port, SiS_Pr->SiS_DDC_Index, temp); +#endif + return 0; } USHORT SiS_WriteDABDDC(SiS_Private *SiS_Pr) { - SiS_SetStart(SiS_Pr); - if(SiS_WriteDDC2Data(SiS_Pr, SiS_Pr->SiS_DDC_DeviceAddr)) return 0xFFFF; - if(SiS_WriteDDC2Data(SiS_Pr, SiS_Pr->SiS_DDC_SecAddr)) return 0xFFFF; + if(SiS_SetStart(SiS_Pr)) return 0xFFFF; + if(SiS_WriteDDC2Data(SiS_Pr, SiS_Pr->SiS_DDC_DeviceAddr)) { +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "WriteDAB 1 failed\n"); +#endif + return 0xFFFF; + } + if(SiS_WriteDDC2Data(SiS_Pr, SiS_Pr->SiS_DDC_SecAddr)) { +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "WriteDAB 2 failed\n"); +#endif + return 0xFFFF; + } return(0); } USHORT SiS_PrepareReadDDC(SiS_Private *SiS_Pr) { - SiS_SetStart(SiS_Pr); - if(SiS_WriteDDC2Data(SiS_Pr, (SiS_Pr->SiS_DDC_DeviceAddr | 0x01))) return 0xFFFF; + if(SiS_SetStart(SiS_Pr)) return 0xFFFF; + if(SiS_WriteDDC2Data(SiS_Pr, (SiS_Pr->SiS_DDC_DeviceAddr | 0x01))) { +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "PrepareReadDDC 1 failed\n"); +#endif + return 0xFFFF; + } return(0); } @@ -8467,11 +9951,15 @@ SiS_DoProbeDDC(SiS_Private *SiS_Pr) { unsigned char mask, value; - USHORT temp, ret; + USHORT temp, ret=0; + BOOLEAN failed = FALSE; SiS_SetSwitchDDC2(SiS_Pr); if(SiS_PrepareDDC(SiS_Pr)) { SiS_SetStop(SiS_Pr); +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "DoProbeDDC 1 failed at PrepareDDC\n"); +#endif return(0xFFFF); } mask = 0xf0; @@ -8482,16 +9970,21 @@ if(temp == 0) { mask = 0xff; value = 0xff; + } else { + failed = TRUE; + ret = 0xFFFF; } } - temp = (unsigned char)SiS_ReadDDC2Data(SiS_Pr, 0); - SiS_SendACK(SiS_Pr, 1); - temp &= mask; - if(temp == value) ret = 0; - else { - ret = 0xFFFF; - if(SiS_Pr->SiS_DDC_DeviceAddr == 0xa0) { - if(value == 0x30) ret = 0; + if(failed == FALSE) { + temp = (unsigned char)SiS_ReadDDC2Data(SiS_Pr, 0); + SiS_SendACK(SiS_Pr, 1); + temp &= mask; + if(temp == value) ret = 0; + else { + ret = 0xFFFF; + if(SiS_Pr->SiS_DDC_DeviceAddr == 0xa0) { + if(temp == 0x30) ret = 0; + } } } SiS_SetStop(SiS_Pr); @@ -8520,7 +10013,7 @@ USHORT flag, length, i; unsigned char chksum,gotcha; - if(DDCdatatype > 3) return 0xFFFF; /* incomplete! */ + if(DDCdatatype > 4) return 0xFFFF; flag = 0; SiS_SetSwitchDDC2(SiS_Pr); @@ -8547,6 +10040,27 @@ return(flag); } +USHORT +SiS_ReadLCDDDC(SiS_Private *SiS_Pr, USHORT length, unsigned char *buffer) +{ + USHORT i=0, flag=0; + + length--; + + SiS_SetSwitchDDC2(SiS_Pr); + if(!(SiS_PrepareDDC(SiS_Pr))) { + for(i=0; i 2) return 0xFFFF; - if(pSiS->VGAEngine == SIS_300_VGA) { - if((adaptnum != 0) && (DDCdatatype != 0)) return 0xFFFF; - } + if(DDCdatatype > 4) return 0xFFFF; if((!(pSiS->VBFlags & VB_VIDEOBRIDGE)) && (adaptnum > 0)) return 0xFFFF; - if(SiS_InitDDCRegs(SiS_Pr, pSiS, adaptnum, DDCdatatype) == 0xFFFF) return 0xFFFF; + if(SiS_InitDDCRegs(SiS_Pr, pSiS, adaptnum, DDCdatatype, TRUE) == 0xFFFF) return 0xFFFF; if(DDCdatatype == 0) { return(SiS_ProbeDDC(SiS_Pr)); } else { - if(DDCdatatype > 4) return 0xFFFF; return(SiS_ReadDDC(SiS_Pr, pSiS, DDCdatatype, buffer)); } } +/* Sense the LCD parameters (CR36, CR37) via DDC */ +/* SiS30x(B) only */ +USHORT +SiS_SenseLCDDDC(SiS_Private *SiS_Pr, SISPtr pSiS) +{ + USHORT DDCdatatype, paneltype, flag, xres, yres; + USHORT index, myindex, lumsize, numcodes; + unsigned char cr37=0, seekcode; + BOOLEAN checkexpand = FALSE; + int retry, i; + unsigned char buffer[256]; + + if(!(pSiS->VBFlags & (VB_301|VB_301B|VB_302B))) return 0; + if(pSiS->VBFlags & VB_30xBDH) return 0; + + if(SiS_InitDDCRegs(SiS_Pr, pSiS, 1, 0, FALSE) == 0xFFFF) return 0; + + SiS_Pr->SiS_DDC_SecAddr = 0x00; + + /* Probe supported DA's */ + flag = SiS_ProbeDDC(SiS_Pr); +#ifdef TWDEBUG + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, + "CRT2 DDC capabilities 0x%x\n", flag); +#endif + if(flag & 0x10) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa6; /* EDID V2 (FP) */ + DDCdatatype = 4; + } else if(flag & 0x08) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa2; /* EDID V2 (P&D-D Monitor) */ + DDCdatatype = 3; + } else if(flag & 0x02) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; /* EDID V1 */ + DDCdatatype = 1; + } else return 0; /* no DDC support (or no device attached) */ + + /* Read the entire EDID */ + retry = 2; + do { + if(SiS_ReadDDC(SiS_Pr, pSiS, DDCdatatype, buffer)) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, + "CRT2: DDC read failed (attempt %d), %s\n", + (3-retry), (retry == 1) ? "giving up" : "retrying"); + retry--; + if(retry == 0) return 0xFFFF; + } else break; + } while(1); + +#ifdef TWDEBUG + for(i=0; i<256; i+=16) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, + "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], + buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], + buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11], + buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]); + } +#endif + + /* Analyze EDID and retrieve LCD panel information */ + paneltype = 0; + switch(DDCdatatype) { + case 1: /* Analyze EDID V1 */ + /* Catch a few clear cases: */ + if(!(buffer[0x14] & 0x80)) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Attached display expects analog input (0x%02x)\n", + buffer[0x14]); + return 0; + } + + if((buffer[0x18] & 0x18) != 0x08) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Attached display is not of RGB but of %s type (0x%02x)\n", + ((buffer[0x18] & 0x18) == 0x00) ? "monochrome/greyscale" : + ( ((buffer[0x18] & 0x18) == 0x10) ? "non-RGB multicolor" : + "undefined"), + buffer[0x18]); + return 0; + } + + /* Now analyze the first Detailed Timing Block and hope + * that the preferred timing mode is stored there. + */ + xres = buffer[0x38] | ((buffer[0x3a] & 0xf0) << 4); + yres = buffer[0x3b] | ((buffer[0x3d] & 0xf0) << 4); + checkexpand = FALSE; + switch(xres) { + case 800: + if(yres == 600) { + paneltype = Panel310_800x600; + checkexpand = TRUE; + } + break; + case 1024: + if(yres == 768) { + paneltype = Panel310_1024x768; + checkexpand = FALSE; /* expand causes error at 640x480, should otherwise be TRUE; */ + } + break; + case 1280: + if(yres == 960) { + if(pSiS->VGAEngine == SIS_300_VGA) { + paneltype = Panel300_1280x960; + } else { + paneltype = Panel310_1280x960; + } + } else if(yres == 1024) { + paneltype = Panel310_1280x1024; + checkexpand = TRUE; + } else if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 768) { + paneltype = Panel310_1280x768; + checkexpand = FALSE; + cr37 |= 0x10; + } + } + break; + case 1400: + if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 1050) { + paneltype = Panel310_1400x1050; + checkexpand = TRUE; + } + } + break; + case 1600: + if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 1200) { + paneltype = Panel310_1600x1200; + checkexpand = TRUE; + } + } + break; + } + + if(buffer[0x18] & 0x02) { + /* If the preferred timing mode is stored in the first + * detailed timing block, we now can extract the sync + * polarisation information as well. This only works + * if the Flags indicate a digital separate output. + */ + if((buffer[0x47] & 0x18) == 0x18) { + cr37 |= ((((buffer[0x47] & 0x06) ^ 0x06) << 5) | 0x20); + } else { + /* What now? There is no digital separate output timing... */ + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_WARNING, + "CRT2: Unable to retrieve Sync polarity information\n"); + } + + } else { + /* If the preferred timing mode is *not* stored in the first + * detailed timing block, we need to guess the resolution + * from the supported Established Timings and assume the + * default sync polarity + */ + paneltype = 0; + if(buffer[0x24] & 0x01) { + paneltype = Panel310_1280x1024; + checkexpand = TRUE; + cr37 |= 0x20; + } else if(buffer[0x24] & 0x0e) { + paneltype = Panel310_1024x768; + cr37 |= 0xe0; + checkexpand = FALSE; /* Bug at 640x480 */ + } else if(buffer[0x23] & 0x01) { + paneltype = Panel310_800x600; + cr37 |= 0xe0; + checkexpand = TRUE; + } + } + + if(checkexpand) { + /* If any of the Established low-res modes is supported, the + * panel can scale automatically. For 800x600 panels, we only + * check the even lower ones. + */ + if(paneltype == Panel310_800x600) { + if(buffer[0x23] & 0xfc) cr37 |= 0x10; + } else { + if(buffer[0x23]) cr37 |= 0x10; + } + } + + break; + + case 3: /* Analyze EDID V2 */ + case 4: + index = 0; + if((buffer[0x41] & 0x0f) == 0x03) { + index = 0x42 + 3; + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Display supports TMDS input on primary interface\n"); + } else if((buffer[0x41] & 0xf0) == 0x30) { + index = 0x46 + 3; + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Display supports TMDS input on secondary interface\n"); + } else { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Display does not support TMDS video interface (0x%02x)\n", + buffer[0x41]); + return 0; + } + + xres = buffer[0x76] | (buffer[0x77] << 8); + yres = buffer[0x78] | (buffer[0x79] << 8); + switch(xres) { + case 800: + if(yres == 600) { + paneltype = Panel310_800x600; + checkexpand = TRUE; + } + break; + case 1024: + if(yres == 768) { + paneltype = Panel310_1024x768; + checkexpand = FALSE; /* Bug at 640x480; we do the scaling ourselves */ + } + break; + case 1280: + if(yres == 960) { + if(pSiS->VGAEngine == SIS_315_VGA) { + paneltype = Panel310_1280x960; + } else { + paneltype = Panel300_1280x960; + } + } else if(yres == 1024) { + paneltype = Panel310_1280x1024; + checkexpand = TRUE; + } else if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 768) { + paneltype = Panel310_1280x768; + checkexpand = FALSE; + cr37 |= 0x10; + } + } + break; + case 1400: + if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 1050) { + paneltype = Panel310_1400x1050; + checkexpand = TRUE; + } + } + break; + case 1600: + if(pSiS->VGAEngine == SIS_315_VGA) { + if(yres == 1200) { + paneltype = Panel310_1600x1200; + checkexpand = TRUE; + } + } + break; + } + + /* Determine if RGB18 or RGB24 */ + if(index) { + if((buffer[index] == 0x20) || (buffer[index] == 0x34)) { + cr37 |= 0x01; + } + } + + if(checkexpand) { + /* TODO - for now, we let the panel scale */ + cr37 |= 0x10; + } + + /* Now seek 4-Byte Timing codes and extract sync pol info */ + index = 0x80; + if(buffer[0x7e] & 0x20) { /* skip Luminance Table (if provided) */ + lumsize = buffer[0x80] & 0x1f; + if(buffer[0x80] & 0x80) lumsize *= 3; + lumsize++; + index += lumsize; + } + index += (((buffer[0x7e] & 0x1c) >> 2) * 8); /* skip Frequency Ranges */ + index += ((buffer[0x7e] & 0x03) * 27); /* skip Detailed Range Limits */ + numcodes = (buffer[0x7f] & 0xf8) >> 3; + if(numcodes) { + myindex = index; + seekcode = (xres - 256) / 16; + for(i=0; ipScrn->scrnIndex, X_WARNING, + "CRT2: Unable to retrieve Sync polarity information\n"); + } + } else { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_WARNING, + "CRT2: Unable to retrieve Sync polarity information\n"); + } + + break; + + } + + /* 1280x960 panels are always RGB24, unable to scale and use + * high active sync polarity + */ + if(pSiS->VGAEngine == SIS_315_VGA) { + if(paneltype == Panel310_1280x960) cr37 &= 0x0e; + } else { + if(paneltype == Panel300_1280x960) cr37 &= 0x0e; + } + + if(paneltype) { + cr37 &= 0xf1; + cr37 |= 0x02; /* SiS301 */ + SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x36,0xf0,paneltype); + SiS_SetReg1(SiS_Pr->SiS_P3d4,0x37,cr37); + SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x32,0x08); +#ifdef TWDEBUG + xf86DrvMsgVerb(pSiS->pScrn->scrnIndex, X_PROBED, 3, + "CRT2: [DDC LCD results: 0x%02x, 0x%02x]\n", paneltype, cr37); +#endif + } + return 0; +} + +USHORT +SiS_SenseVGA2DDC(SiS_Private *SiS_Pr, SISPtr pSiS) +{ + USHORT DDCdatatype,flag; + BOOLEAN foundcrt = FALSE; + int retry; + unsigned char buffer[256]; + + if(!(pSiS->VBFlags & (VB_301|VB_301B|VB_302B))) return 0; +/* if(pSiS->VBFlags & VB_30xBDH) return 0; */ + + if(SiS_InitDDCRegs(SiS_Pr, pSiS, 2, 0, FALSE) == 0xFFFF) return 0; + + SiS_Pr->SiS_DDC_SecAddr = 0x00; + + /* Probe supported DA's */ + flag = SiS_ProbeDDC(SiS_Pr); + if(flag & 0x10) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa6; /* EDID V2 (FP) */ + DDCdatatype = 4; + } else if(flag & 0x08) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa2; /* EDID V2 (P&D-D Monitor) */ + DDCdatatype = 3; + } else if(flag & 0x02) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; /* EDID V1 */ + DDCdatatype = 1; + } else { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, + "Do DDC answer\n"); + return 0; /* no DDC support (or no device attached) */ + } + + /* Read the entire EDID */ + retry = 2; + do { + if(SiS_ReadDDC(SiS_Pr, pSiS, DDCdatatype, buffer)) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_INFO, + "CRT2: DDC read failed (attempt %d), %s\n", + (3-retry), (retry == 1) ? "giving up" : "retrying"); + retry--; + if(retry == 0) return 0xFFFF; + } else break; + } while(1); + + /* Analyze EDID. We don't have many chances to + * distinguish a flat panel from a CRT... + */ + switch(DDCdatatype) { + case 1: + if(buffer[0x14] & 0x80) { /* Display uses digital input */ + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Attached display expects digital input\n"); + return 0; + } + foundcrt = TRUE; + break; + case 3: + case 4: + if( ((buffer[0x41] & 0x0f) != 0x01) && /* Display does not support analog input */ + ((buffer[0x41] & 0x0f) != 0x02) && + ((buffer[0x41] & 0xf0) != 0x10) && + ((buffer[0x41] & 0xf0) != 0x20) ) { + xf86DrvMsg(pSiS->pScrn->scrnIndex, X_PROBED, + "CRT2: Attached display does not support analog input (0x%02x)\n", + buffer[0x41]); + return 0; + } + foundcrt = TRUE; + break; + } + + if(foundcrt) { + SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x32,0x10); + } + return(0); +} + +#if 0 + /* ----- */ +USHORT +SiS_SenseLCDDDC(SiS_Private *SiS_Pr, SISPtr pSiS) +{ + USHORT DDCdatatype, paneltype, flag; + unsigned char cr36=0, cr37=0; + unsigned char tempal, tempah, tempbl, tempbh; + USHORT tempax, tempbx, tempcx, push1, push2, push3; + unsigned char addresstable[] = { 0x00, 0x22, 0x30, 0x40 }; + int i; + unsigned char buffer[256]; + + if(pSiS->VGAEngine != SIS_315_VGA) return 0xFFFF; + if(!(pSiS->VBFlags & (VB_301B|VB_302B))) return 0xFFFF; + + if(SiS_InitDDCRegs(SiS_Pr, pSiS, 1, 0, FALSE) == 0xFFFF) return 0xFFFF; + + flag = SiS_ProbeDDC(SiS_Pr); + if(flag & 0x02) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; /* EDID V1 */ + DDCdatatype = 1; + SiS_Pr->SiS_DDC_SecAddr = 0x3a; + } else if(flag & 0x08) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa2; /* EDID V2 (P&D-D Monitor) */ + DDCdatatype = 3; + SiS_Pr->SiS_DDC_SecAddr = 0x76; + } else if(flag & 0x10) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa6; /* EDID V2 (FP) */ + DDCdatatype = 4; + SiS_Pr->SiS_DDC_SecAddr = 0x76; + } else return 0xFFFF; + + + SiS_ReadLCDDDC(SiS_Pr, 4, buffer); + tempbl = buffer[0]; /* 3a - 76 */ + tempbh = buffer[1]; /* 3b - 77 */ + + if(SiS_Pr->SiS_DDC_DeviceAddr == 0xa0) { + + /* Read and analyze EDID V1 (res) */ + + tempah = 0x02; /* 1024x768 by default */ + tempbl &= 0xf0; + if(tempbl != 0x40) { + tempah = 0x03; /* 1280x1024 by default */ + if(tempbl == 0x50) { + if(!tempbh) { + tempbh = buffer[3] & 0xf0; + if(tempbh == 0x30) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; + SiS_Pr->SiS_DDC_SecAddr = 0x23; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempbl = buffer[0]; /* 0x23 */ + tempbh = buffer[1]; /* 0x24 */ + if(tempbl) cr37 |= 0x10; + tempah = 0x0a; /* 1280x768 */ + } + if(tempbh == 0x40) { + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; + SiS_Pr->SiS_DDC_SecAddr = 0x23; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempbl = buffer[0]; /* 0x23 */ + tempbh = buffer[1]; /* 0x24 */ + if(tempbl) cr37 |= 0x10; + tempah = 0x03; /* 1280x1024 */ + } + tempbh = 0x00; + } + } + if(tempbh == 0x00) goto cr36ready; + tempah = 0x07; /* 1280x960 */ + if(tempbh == 0xc0) goto cr36ready; + } + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; + SiS_Pr->SiS_DDC_SecAddr = 0x18; /* feature support */ + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempbl = buffer[0]; + tempbh = buffer[1]; + if(tempbl & 0x02) goto cr36ready; + SiS_Pr->SiS_DDC_DeviceAddr = 0xa0; + SiS_Pr->SiS_DDC_SecAddr = 0x23; /* Established Timings */ + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempbl = buffer[0]; + tempbh = buffer[1]; + tempah = 0x03; + if(!(tempbh & 0x01)) tempah = 0x02; + if(!tempbl) cr37 |= 0x10; + + } else { + + /* Read and analyze EDID V2 (res) */ + + tempah = 0x02; + tempbx = tempbl | (tempbh << 8); + if(tempbx != 1024) tempah = 0x03; + + } + +cr36ready: + cr36 = tempah; + + if(SiS_Pr->SiS_DDC_DeviceAddr == 0xa0) { + + /* Read and analyze EDID V1 (pol) */ + + SiS_Pr->SiS_DDC_SecAddr = 0x47; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempah = buffer[0]; + tempah &= 0x06; + tempah ^= 0x06; + tempah <<= 5; + tempah |= 0x20; + cr37 &= 0x1f; + cr37 |= tempah; + if((cr36 & 0x07) == 0x07) cr37 &= 0x0e; + + } else { + + /* Read and analyze EDID V2 (depth, pol) */ + + push1 = tempah; + SiS_Pr->SiS_DDC_SecAddr = 0x45; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempah = 0x01; + if((buffer[0] != 0x20) && (buffer[0] != 0x34)) { /* RGB18 or 24? */ + tempah = 0x00; + } + cr37 &= 0xfe; + cr37 |= tempah; + + SiS_Pr->SiS_DDC_SecAddr = 0x7e; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempax = (USHORT)(buffer[0] | (buffer[1] << 8)); + push2 = tempax; + tempax &= 0x0003; + tempax *= 0x1b; + push3 = tempax; + tempax = (USHORT)buffer[0]; + tempax &= 0x001c; + tempax >>= 2; + tempax *= 8; + tempbx = push3; + tempbx += tempax; + if(buffer[0] & 0x20) { /* Luminance table provided? */ + SiS_Pr->SiS_DDC_SecAddr = 0x80; + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempax = buffer[0] | (buffer[1] << 8); + tempax &= 0x1f; + if(buffer[0] & 0x70) tempax <<= 1; + tempax++; + tempbx += tempax; /* yes -> skip it */ + } + tempcx = push2; + tempax = push1 << 8; + tempbx = (tempbx & 0xff00) | (((tempbx & 0x00ff) + 0x80) & 0x00ff); + if(tempcx & 0xf800) { + tempal = addresstable[((tempax & 0xff00) >> 8)]; + tempcx &= 0xf8ff; + tempcx >>= 11; + for(i=0; iSiS_DDC_SecAddr = (tempbx & 0x00ff); + SiS_ReadLCDDDC(SiS_Pr, 2, buffer); + tempbx += 0x04; + if(buffer[0] == tempal) break; + } + tempah = buffer[1]; + tempah &= 0x0c; + tempah ^= 0x0c; + tempah <<= 4; + tempah |= 0x20; + cr37 &= 0x1f; + cr37 |= tempah; + if((cr36 & 0x07) == 0x07) cr37 &= 0x0e; + } + } + xf86DrvMsg(0, X_INFO, "DDC: cr36 = 0x%02x, cr37 = 0x%02x\n", cr36, cr37); + return 0; +} +#endif + /* TW: Generic I2C functions (compliant to i2c library) */ #if 0 @@ -8719,14 +10810,8 @@ USHORT SiS_SetSCLKLow(SiS_Private *SiS_Pr) { - USHORT temp, watchdog=50000; - SiS_SetRegANDOR(SiS_Pr->SiS_DDC_Port,SiS_Pr->SiS_DDC_Index, ~SiS_Pr->SiS_DDC_Clk,0x00); /* SetSCLKLow() */ - do { - temp = SiS_GetReg1(SiS_Pr->SiS_DDC_Port,SiS_Pr->SiS_DDC_Index); - } while((temp & SiS_Pr->SiS_DDC_Clk) && --watchdog); - if (!watchdog) return 0xFFFF; SiS_DDC2Delay(SiS_Pr,SiS_I2CDELAYSHORT); return 0; } @@ -8734,14 +10819,19 @@ USHORT SiS_SetSCLKHigh(SiS_Private *SiS_Pr) { - USHORT temp,watchdog=50000; + USHORT temp,watchdog=1000; SiS_SetRegANDOR(SiS_Pr->SiS_DDC_Port,SiS_Pr->SiS_DDC_Index, ~SiS_Pr->SiS_DDC_Clk,SiS_Pr->SiS_DDC_Clk); /* SetSCLKHigh() */ do { temp = SiS_GetReg1(SiS_Pr->SiS_DDC_Port,SiS_Pr->SiS_DDC_Index); } while((!(temp & SiS_Pr->SiS_DDC_Clk)) && --watchdog); - if (!watchdog) return 0xFFFF; + if (!watchdog) { +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "SetClkHigh failed\n"); +#endif + return 0xFFFF; + } SiS_DDC2Delay(SiS_Pr,SiS_I2CDELAYSHORT); return 0; } @@ -8776,11 +10866,78 @@ /* TW: End of I2C functions ----------------------- */ -/* =============== SiS 310/325 O.E.M. ================= */ +/* =============== SiS 310/325/330 O.E.M. ================= */ #ifdef SIS315H -USHORT +static USHORT +GetRAMDACromptr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, UCHAR *ROMAddr) +{ + USHORT romptr; + + if(HwDeviceExtension->jChipType < SIS_330) { + romptr = ROMAddr[0x128] | (ROMAddr[0x129] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) + romptr = ROMAddr[0x12a] | (ROMAddr[0x12b] << 8); + } else { + romptr = ROMAddr[0x1a8] | (ROMAddr[0x1a9] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) + romptr = ROMAddr[0x1aa] | (ROMAddr[0x1ab] << 8); + } + return(romptr); +} + +static USHORT +GetLCDromptr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, UCHAR *ROMAddr) +{ + USHORT romptr; + + if(HwDeviceExtension->jChipType < SIS_330) { + romptr = ROMAddr[0x120] | (ROMAddr[0x121] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) + romptr = ROMAddr[0x122] | (ROMAddr[0x123] << 8); + } else { + romptr = ROMAddr[0x1a0] | (ROMAddr[0x1a1] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) + romptr = ROMAddr[0x1a2] | (ROMAddr[0x1a3] << 8); + } + return(romptr); +} + +static USHORT +GetTVromptr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, UCHAR *ROMAddr) +{ + USHORT romptr; + + if(HwDeviceExtension->jChipType < SIS_330) { + romptr = ROMAddr[0x114] | (ROMAddr[0x115] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) + romptr = ROMAddr[0x11a] | (ROMAddr[0x11b] << 8); + } else { + romptr = ROMAddr[0x194] | (ROMAddr[0x195] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) + romptr = ROMAddr[0x19a] | (ROMAddr[0x19b] << 8); + } + return(romptr); +} + +static USHORT +GetLCDPtrIndexBIOS(SiS_Private *SiS_Pr) +{ + USHORT index; + + index = SiS_Pr->SiS_LCDResInfo & 0x0F; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) index -= 5; + else if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1600x1200) index -= 6; + index--; + index *= 3; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) index += 2; + else if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) index++; + + return index; +} + +static USHORT GetLCDPtrIndex(SiS_Private *SiS_Pr) { USHORT index; @@ -8788,7 +10945,7 @@ index = SiS_Pr->SiS_LCDResInfo & 0x0F; index--; index *= 3; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) index += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) index += 2; else if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) index++; return index; @@ -8805,7 +10962,7 @@ 5 : HiVision Standard TVSimuMode --------------------------------------------------------- */ -USHORT +static USHORT GetTVPtrIndex(SiS_Private *SiS_Pr) { USHORT index; @@ -8822,90 +10979,231 @@ return index; } -/* TW: Checked against 650/LVDS (1.10.07) and 650/301LVx (1.10.6s) BIOS (including data) */ -void +static void SetDelayComp(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, - UCHAR *ROMAddr,USHORT ModeNo) + UCHAR *ROMAddr, USHORT ModeNo) { - USHORT delay,index; - - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { - delay = SiS310_CRT2DelayCompensation1; - if(SiS_Pr->SiS_VBType & (VB_SIS301B | VB_SIS302B)) - delay = SiS310_CRT2DelayCompensation2; - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) - delay = SiS310_CRT2DelayCompensation3; - } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - index = GetLCDPtrIndex(SiS_Pr); - delay = SiS310_LCDDelayCompensation1[index]; - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) - delay = SiS310_LCDDelayCompensation2[index]; - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) - delay = SiS310_LCDDelayCompensation3[index]; - } else { + USHORT delay,index,myindex,temp,romptr=0; + + if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { /* VGA */ + + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + romptr = GetRAMDACromptr(SiS_Pr, HwDeviceExtension, ROMAddr); + if(!romptr) return; + delay = ROMAddr[romptr]; + } else { + delay = 0x04; + if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + if(IS_SIS650) { + delay = 0x0a; + } else if(IS_SIS740) { + delay = 0x00; + } else if(HwDeviceExtension->jChipType < SIS_330) { + delay = 0x0c; + } else { + delay = 0x0c; + } + } + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) + delay = 0x00; + } + + } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { /* LCD */ + + index = GetLCDPtrIndexBIOS(SiS_Pr); + myindex = GetLCDPtrIndex(SiS_Pr); + + if(IS_SIS650 && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { /* 650+30xLV */ + if(SiS_IsNotM650or651(SiS_Pr,HwDeviceExtension, BaseAddr)) { + if((ROMAddr) && SiS_Pr->SiS_UseROM) { +#if 0 /* Always use the second pointer on 650; some BIOSes */ + /* still carry old 301 data at the first location */ + romptr = ROMAddr[0x120] | (ROMAddr[0x121] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS302LV) +#endif + romptr = ROMAddr[0x122] | (ROMAddr[0x123] << 8); + if(!romptr) return; + delay = ROMAddr[(romptr + index)]; + } else { + delay = SiS310_LCDDelayCompensation_650301B[myindex]; +#if 0 + if(SiS_Pr->SiS_VBType & VB_SIS302LV) + delay = SiS310_LCDDelayCompensation_650301B[myindex]; +#endif + } + } else { + delay = SiS310_LCDDelayCompensation_651301LV[myindex]; + if(SiS_Pr->SiS_VBType & VB_SIS302LV) + delay = SiS310_LCDDelayCompensation_651302LV[myindex]; + } + } else { + if((ROMAddr) && SiS_Pr->SiS_UseROM && /* 315, 330, 740, 650+301B */ + (SiS_Pr->SiS_LCDResInfo != SiS_Pr->SiS_Panel1280x1024)) { + romptr = GetLCDromptr(SiS_Pr, HwDeviceExtension, ROMAddr); + if(!romptr) return; + delay = ROMAddr[(romptr + index)]; + } else { + delay = SiS310_LCDDelayCompensation_301[myindex]; + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { +#if 0 /* This data is (like the one in the BIOS) wrong. */ + if(IS_SIS650740) { /* V */ + delay = SiS310_LCDDelayCompensation_650301B[myindex]; + } else { +#endif + delay = SiS310_LCDDelayCompensation_3xx301B[myindex]; +#if 0 + } +#endif + } + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + if(IS_SIS650) { + delay = SiS310_LCDDelayCompensation_LVDS650[myindex]; + } else { + delay = SiS310_LCDDelayCompensation_LVDS740[myindex]; + } + } + } + } + + } else { /* TV */ + index = GetTVPtrIndex(SiS_Pr); - delay = SiS310_TVDelayCompensation1[index]; - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) - delay = SiS310_TVDelayCompensation2[index]; - if(SiS_Pr->SiS_IF_DEF_LVDS == 1) - delay = SiS310_TVDelayCompensation3[index]; + + if(IS_SIS650 && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { + if(SiS_IsNotM650or651(SiS_Pr,HwDeviceExtension, BaseAddr)) { + if((ROMAddr) && SiS_Pr->SiS_UseROM) { +#if 0 /* Always use the second pointer on 650; some BIOSes */ + /* still carry old 301 data at the first location */ + romptr = ROMAddr[0x114] | (ROMAddr[0x115] << 8); + if(SiS_Pr->SiS_VBType & VB_SIS302LV) +#endif + romptr = ROMAddr[0x11a] | (ROMAddr[0x11b] << 8); + if(!romptr) return; + delay = ROMAddr[romptr + index]; + } else { + delay = SiS310_TVDelayCompensation_301B[index]; +#if 0 + if(SiS_Pr->SiS_VBType & VB_SIS302LV) + delay = SiS310_TVDelayCompensation_301B[index]; +#endif + } + } else { + delay = SiS310_TVDelayCompensation_651301LV[index]; + if(SiS_Pr->SiS_VBType & VB_SIS302LV) + delay = SiS310_TVDelayCompensation_651302LV[index]; + } + } else { + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + romptr = GetTVromptr(SiS_Pr, HwDeviceExtension, ROMAddr); + if(!romptr) return; + delay = ROMAddr[romptr + index]; + } else { + delay = SiS310_TVDelayCompensation_301[index]; + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + if(IS_SIS740) + delay = SiS310_TVDelayCompensation_740301B[index]; + else + delay = SiS310_TVDelayCompensation_301B[index]; + } + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) + delay = SiS310_TVDelayCompensation_LVDS[index]; + } + } + } if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0xF0,delay); - } else { - delay <<= 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0x0F,delay); - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0xF0,delay); + } else { + if(IS_SIS650 && (SiS_Pr->SiS_IF_DEF_CH70xx != 0)) { + delay <<= 4; + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0x0F,delay); + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0xF0,delay); + } + } } else { - SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x2D,delay); /* index 2D D[3:0] */ + if(IS_SIS650 && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { + temp = (SiS_GetReg1(SiS_Pr->SiS_P3d4,0x36) & 0xf0) >> 4; + if(temp == 8) { + delay &= 0x0f; + delay |= 0xb0; + } else if(temp == 6) { + delay &= 0x0f; + delay |= 0xc0; + } + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x2D,delay); /* index 2D D[3:0] */ + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2D,0xF0,delay); + } } } -/* TW: Checked against 650/301LVx 1.10.6s BIOS (including data) */ -void +static void SetAntiFlicker(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp; + USHORT index,temp,romptr=0; temp = GetTVPtrIndex(SiS_Pr); temp >>= 1; /* 0: NTSC, 1: PAL, 2: HiTV */ - if (ModeNo<=0x13) - index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].VB_StTVFlickerIndex; + if(ModeNo<=0x13) + index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].VB_StTVFlickerIndex; else - index = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].VB_ExtTVFlickerIndex; + index = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].VB_ExtTVFlickerIndex; + + if(ROMAddr && SiS_Pr->SiS_UseROM) { + romptr = ROMAddr[0x112] | (ROMAddr[0x113] << 8); + if(HwDeviceExtension->jChipType == SIS_330) { + romptr = ROMAddr[0x192] | (ROMAddr[0x193] << 8); + } + } - temp = SiS310_TVAntiFlick1[temp][index]; + if(romptr) { + temp <<= 1; + temp = ROMAddr[romptr + temp + index]; + } else { + temp = SiS310_TVAntiFlick1[temp][index]; + } temp <<= 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x0A,0x8f,temp); /* index 0A D[6:4] */ } -/* TW: Checked against 650/301LVx 1.10.6s BIOS (including data) */ -void +static void SetEdgeEnhance(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp; + USHORT index,temp,romptr=0; temp = GetTVPtrIndex(SiS_Pr); temp >>= 1; /* 0: NTSC, 1: PAL, 2: HiTV */ - if (ModeNo<=0x13) - index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].VB_StTVEdgeIndex; + if(ModeNo<=0x13) + index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].VB_StTVEdgeIndex; else - index = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].VB_ExtTVEdgeIndex; + index = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].VB_ExtTVEdgeIndex; + + if(ROMAddr && SiS_Pr->SiS_UseROM) { + romptr = ROMAddr[0x124] | (ROMAddr[0x125] << 8); + if(HwDeviceExtension->jChipType == SIS_330) { + romptr = ROMAddr[0x1a4] | (ROMAddr[0x1a5] << 8); + } + } - temp = SiS310_TVEdge1[temp][index]; + if(romptr) { + temp <<= 1; + temp = ROMAddr[romptr + temp + index]; + } else { + temp = SiS310_TVEdge1[temp][index]; + } temp <<= 5; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x3A,0x1F,temp); /* index 0A D[7:5] */ } -/* TW: Checked against 650/301LVx 1.10.6s BIOS (incl data) */ -void +static void SetYFilter(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { @@ -8921,7 +11219,7 @@ index = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].VB_ExtTVYFilterIndex; } - if(SiS_Pr->SiS_VBInfo&SetCRT2ToHiVisionTV) temp = 1; /* Hivision TV uses PAL */ + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) temp = 1; /* Hivision TV uses PAL */ if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { for(i=0x35, j=0; i<=0x38; i++, j++) { @@ -8935,25 +11233,28 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVYFilter1[temp][index][j]); } } - + if(ROMAddr && SiS_Pr->SiS_UseROM) { OutputSelect = ROMAddr[0xf3]; + if(HwDeviceExtension->jChipType == SIS_330) { + OutputSelect = ROMAddr[0x11b]; + } } if(OutputSelect & EnablePALMN) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x01) { temp = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); - temp &= (EnablePALMN | EnablePALN); - if(temp == EnablePALMN) { + temp &= (EnablePALM | EnablePALN); + if(temp == EnablePALM) { if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - for(i=0x35, j=0; i<=0x38; i++, j++){ + for(i=0x35, j=0; i<=0x38; i++, j++) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALMFilter2[index][j]); } for(i=0x48; i<=0x4A; i++, j++) { - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALMFilter2[index][j]); + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALMFilter2[index][j]); } } else { for(i=0x35, j=0; i<=0x38; i++, j++) { - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALMFilter[index][j]); + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALMFilter[index][j]); } } } @@ -8963,31 +11264,30 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALNFilter2[index][j]); } for(i=0x48, j=0; i<=0x4A; i++, j++) { - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALNFilter2[index][j]); + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALNFilter2[index][j]); } } else { - for(i=0x35, j=0; i<=0x38; i++, j++) - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALNFilter[index][j]); + for(i=0x35, j=0; i<=0x38; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_PALNFilter[index][j]); + } } } } } } -/* TW: Checked against 650/301LVx 1.10.6s BIOS (including data) */ -void +static void SetPhaseIncr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp,temp1,i,j,resinfo; + USHORT index,temp,temp1,i,j,resinfo,romptr=0; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) return; temp1 = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x38); /* if PALM/N not set */ - temp1 &= (EnablePALMN | EnablePALN); + temp1 &= (EnablePALM | EnablePALN); if(temp1) return; - if (ModeNo<=0x13) { resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; } else { @@ -8995,22 +11295,47 @@ } temp = GetTVPtrIndex(SiS_Pr); - /* 0: NTSC Graphics, 1: NTSC Text, 2:PAL Graphics, - * 3: PAL Text, 4: HiTV Graphics 5:HiTV Text + /* 0: NTSC Graphics, 1: NTSC Text, 2: PAL Graphics, + * 3: PAL Text, 4: HiTV Graphics 5: HiTV Text */ - index = temp % 2; - temp >>= 1; /* 0:NTSC, 1:PAL, 2:HiTV */ - - for(j=0, i=0x31; i<=0x34; i++, j++) { - if(!(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr1[temp][index][j]); - else if((!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode)) - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr2[temp][index][j]); - else - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr1[temp][index][j]); + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + romptr = ROMAddr[0x116] | (ROMAddr[0x117] << 8); + if(HwDeviceExtension->jChipType == SIS_330) { + romptr = ROMAddr[0x196] | (ROMAddr[0x197] << 8); + } + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { + romptr = ROMAddr[0x11c] | (ROMAddr[0x11d] << 8); + if(HwDeviceExtension->jChipType == SIS_330) { + romptr = ROMAddr[0x19c] | (ROMAddr[0x19d] << 8); + } + if((SiS_Pr->SiS_VBInfo & SetInSlaveMode) && (!(SiS_Pr->SiS_SetFlag & TVSimuMode))) { + romptr = ROMAddr[0x116] | (ROMAddr[0x117] << 8); + if(HwDeviceExtension->jChipType == SIS_330) { + romptr = ROMAddr[0x196] | (ROMAddr[0x197] << 8); + } + } + } + } + if(romptr) { + romptr += (temp << 2); + for(j=0, i=0x31; i<=0x34; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,ROMAddr[romptr + j]); + } + } else { + index = temp % 2; + temp >>= 1; /* 0:NTSC, 1:PAL, 2:HiTV */ + for(j=0, i=0x31; i<=0x34; i++, j++) { + if(!(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr1[temp][index][j]); + else if((!(SiS_Pr->SiS_VBInfo & SetInSlaveMode)) || (SiS_Pr->SiS_SetFlag & TVSimuMode)) + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr2[temp][index][j]); + else + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS310_TVPhaseIncr1[temp][index][j]); + } } + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* TW: 650/301LV: (VB_SIS301LV | VB_SIS302LV)) */ - if(!(SiS_Pr->SiS_VBInfo & SetPALTV)) { + if((!(SiS_Pr->SiS_VBInfo & SetPALTV)) && (ModeNo > 0x13)) { if(resinfo == 6) { SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x31,0x21); SiS_SetReg1(SiS_Pr->SiS_Part2Port,0x32,0xf0); @@ -9036,7 +11361,7 @@ UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { SetDelayComp(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo); - /* TW: The TV funtions are not for LVDS */ + /* TW: The TV functions are not for LVDS */ if( (SiS_Pr->SiS_IF_DEF_LVDS == 0) && (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) ) { SetAntiFlicker(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); SetPhaseIncr(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); @@ -9047,20 +11372,28 @@ } } -/* TW: New from 650/301LVx 1.10.6s - clashes with OEMLCD() */ +/* FinalizeLCD + * This finalizes some Part1 registers for the very panel used. + * If we have a backup if these registers, we use it; otherwise + * we set the register according to most BIOSes. However, this + * function looks quite different in every BIOS, so you better + * pray that we have a backup... + */ void SiS_FinalizeLCD(SiS_Private *SiS_Pr, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex, PSIS_HW_DEVICE_INFO HwDeviceExtension) { USHORT tempcl,tempch,tempbl,tempbh,tempbx,tempax,temp; - USHORT resinfo; + USHORT resinfo,modeflag; - if(!(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV)) return; + if(!(SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) return; - if(ModeNo<=0x13) { + if(ModeNo <= 0x13) { resinfo = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ResInfo; + modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; } else { resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; + modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; } if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) { @@ -9076,6 +11409,76 @@ if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1400x1050) { SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1f,0x76); } + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { + if((SiS_Pr->Backup == TRUE) && (SiS_Pr->Backup_Mode == ModeNo)) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x14,SiS_Pr->Backup_14); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x15,SiS_Pr->Backup_15); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,SiS_Pr->Backup_16); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,SiS_Pr->Backup_17); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,SiS_Pr->Backup_18); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x19,SiS_Pr->Backup_19); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1a,SiS_Pr->Backup_1a); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,SiS_Pr->Backup_1b); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1c,SiS_Pr->Backup_1c); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1d,SiS_Pr->Backup_1d); + } else if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { /* From 1.10.8w */ + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x14,0x90); + if(ModeNo <= 0x13) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x11); + if((resinfo == 0) && (resinfo == 2)) return; + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x18); + if((resinfo == 1) && (resinfo == 3)) return; + } + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x02); + if((ModeNo > 0x13) && (resinfo == 8)) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x02); /* 1.10.7u */ +#if 0 + tempbx = 806; /* 0x326 */ /* other older BIOSes */ + tempbx--; + temp = tempbx & 0xff; + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,temp); + temp = (tempbx >> 8) & 0x03; + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x1d,0xf8,temp); +#endif + } + } else { + if(ModeNo <= 0x13) { + if(ModeNo <= 1) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x70); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x19,0xff); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,0x48); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1d,0x12); + } + if(!(modeflag & HalfDCLK)) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x14,0x20); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x15,0x1a); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x16,0x28); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x17,0x00); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x4c); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x19,0xdc); + if(ModeNo == 0x12) { + switch(tempch) { + case 0: + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x95); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x19,0xdc); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1a,0x10); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,0x95); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1c,0x48); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1d,0x12); + break; + case 2: + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x18,0x95); + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,0x48); + break; + case 3: + SiS_SetReg1(SiS_Pr->SiS_Part1Port,0x1b,0x95); + break; + } + } + } + } + } + } } else { tempcl = tempbh = SiS_GetReg1(SiS_Pr->SiS_Part2Port,0x01); tempcl &= 0x0f; @@ -9084,15 +11487,15 @@ tempbl = SiS_GetReg1(SiS_Pr->SiS_Part2Port,0x04); tempbx = (tempbh << 8) | tempbl; if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { - if((resinfo == 8) || (!(SiS_Pr->SiS_LCDInfo & LCDNonExpanding))) { + if((resinfo == 8) || (!(SiS_Pr->SiS_LCDInfo & DontExpandLCD))) { if(SiS_Pr->SiS_SetFlag & LCDVESATiming) { tempbx = 770; } else { if(tempbx > 770) tempbx = 770; - if(SiS_Pr->SiS_VGAVDE < 600) { /* Shouldn't that be <=? */ + if(SiS_Pr->SiS_VGAVDE < 600) { tempax = 768 - SiS_Pr->SiS_VGAVDE; - tempax >>= 3; - if(SiS_Pr->SiS_VGAVDE < 480) tempax >>= 1; /* Shouldn't that be <=? */ + tempax >>= 4; /* From 1.10.7w; 1.10.6s: 3; */ + if(SiS_Pr->SiS_VGAVDE <= 480) tempax >>= 4; /* From 1.10.7w; 1.10.6s: < 480; >>=1; */ tempbx -= tempax; } } @@ -9112,6 +11515,7 @@ } } +#if 0 /* TW: New and checked from 650/301LV BIOS */ /* This might clash with newer "FinalizeLCD()" function */ void @@ -9166,13 +11570,15 @@ } #endif +#endif + /* ================= SiS 300 O.E.M. ================== */ #ifdef SIS300 #if 0 /* Not used */ -USHORT +static USHORT GetRevisionID(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension) { ULONG temp1; @@ -9199,74 +11605,170 @@ } #endif -/* TW: Checked against 630/301B BIOS (incl data) */ -USHORT -GetOEMLCDPtr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, int Flag) +static USHORT +GetOEMLCDPtr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, UCHAR *ROMAddr, int Flag) { - USHORT tempbx=0; - UCHAR customtable[] = { + USHORT tempbx=0,romptr=0; + UCHAR customtable300[] = { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff + }; + UCHAR customtable630[] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; - if(Flag) { - if(customtable[SiS_Pr->SiS_LCDTypeInfo] == 0xFF) return 0xFFFF; - } - if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - tempbx = SiS_Pr->SiS_LCDTypeInfo << 2; - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) tempbx += 2; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx++; + if(HwDeviceExtension->jChipType == SIS_300) { + + tempbx = SiS_Pr->SiS_LCDResInfo - 2; + if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx += 4; + if(SiS_Pr->SiS_LCDResInfo == SiS_Pr->SiS_Panel1024x768) { + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx += 3; + } + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + if(ROMAddr[0x235] & 0x80) { + tempbx = SiS_Pr->SiS_LCDTypeInfo; + if(Flag) { + romptr = ROMAddr[0x255] | (ROMAddr[0x256] << 8); + if(romptr) { + tempbx = ROMAddr[romptr + SiS_Pr->SiS_LCDTypeInfo]; + } else { + tempbx = customtable300[SiS_Pr->SiS_LCDTypeInfo]; + } + if(tempbx == 0xFF) return 0xFFFF; + } + tempbx <<= 1; + if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx++; + } + } + } else { - tempbx = SiS_Pr->SiS_LCDTypeInfo; - if(SiS_Pr->SiS_LCDInfo & LCDNonExpanding) tempbx += 16; + + if(Flag) { + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + romptr = ROMAddr[0x255] | (ROMAddr[0x256] << 8); + if(romptr) { + tempbx = ROMAddr[romptr + SiS_Pr->SiS_LCDTypeInfo]; + } else { + tempbx = 0xff; + } + } else { + tempbx = customtable630[SiS_Pr->SiS_LCDTypeInfo]; + } + if(tempbx == 0xFF) return 0xFFFF; + tempbx <<= 2; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx++; + return tempbx; + } + tempbx = SiS_Pr->SiS_LCDTypeInfo << 2; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) tempbx += 2; + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) tempbx++; } return tempbx; } -/* TW: Checked against 630/301B and 630/LVDS BIOS (incl data) */ -void +static void SetOEMLCDDelay(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp; + USHORT index,temp,romptr=0; - /* TW: The Panel Compensation Delay should be set according to tables - * here. Unfortunately, the different BIOS versions don't case about - * a uniform way using eg. ROM byte 0x220, but use different - * hard coded delays (0x04, 0x20, 0x18) in SetGroup1(). So we can't - * rely on the other OEM bits in 0x237, 0x238 here either. - * ROMAddr > 0x233 is even used for code (!) in newer BIOSes! - */ -#if 0 if((ROMAddr) && SiS_Pr->SiS_UseROM) { if(!(ROMAddr[0x237] & 0x01)) return; if(!(ROMAddr[0x237] & 0x02)) return; + romptr = ROMAddr[0x24b] | (ROMAddr[0x24c] << 8); } -#endif - /* TW: We just check if a non-standard delay has been set; if not, - * we use our tables. Otherwise don't do anything here. + + /* TW: The Panel Compensation Delay should be set according to tables + * here. Unfortunately, various BIOS versions don't case about + * a uniform way using eg. ROM byte 0x220, but use different + * hard coded delays (0x04, 0x20, 0x18) in SetGroup1(). + * Thus we don't set this if the user select a custom pdc or if + * we otherwise detected a valid pdc. */ - if((ROMAddr) && SiS_Pr->SiS_UseROM) { - if(ROMAddr[0x220] & 0x80) return; - } - /* TW: We don't need to set this if the user select a custom pdc */ if(HwDeviceExtension->pdc) return; - temp = GetOEMLCDPtr(SiS_Pr,HwDeviceExtension, 0); + temp = GetOEMLCDPtr(SiS_Pr,HwDeviceExtension, ROMAddr, 0); index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex].VB_LCDDelayIndex; - if (SiS_Pr->SiS_IF_DEF_LVDS == 0) { - temp = SiS300_OEMLCDDelay2[temp][index]; + if(HwDeviceExtension->jChipType != SIS_300) { + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += index; + temp = ROMAddr[romptr]; + } else { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + temp = SiS300_OEMLCDDelay2[temp][index]; + } else { + temp = SiS300_OEMLCDDelay3[temp][index]; + } + } } else { - temp = SiS300_OEMLCDDelay3[temp][index]; + if((ROMAddr) && SiS_Pr->SiS_UseROM && (ROMAddr[0x235] & 0x80)) { + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += index; + temp = ROMAddr[romptr]; + } else { + temp = SiS300_OEMLCDDelay5[temp][index]; + } + } else { + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + romptr = ROMAddr[0x249] | (ROMAddr[0x24a] << 8); + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += index; + temp = ROMAddr[romptr]; + } else { + temp = SiS300_OEMLCDDelay4[temp][index]; + } + } else { + temp = SiS300_OEMLCDDelay4[temp][index]; + } + } } temp &= 0x3c; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,~0x3C,temp); /* index 0A D[6:4] */ } -/* TW: Checked against 630/301B 2.04.50 and 630/LVDS BIOS */ -USHORT +static void +SetOEMLCDData(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, + UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) +{ +#if 0 /* TW: Unfinished; VData table missing */ + USHORT index,temp; + + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + if(!(ROMAddr[0x237] & 0x01)) return; + if(!(ROMAddr[0x237] & 0x04)) return; + /* No rom pointer in BIOS header! */ + } + + temp = GetOEMLCDPtr(SiS_Pr,HwDeviceExtension, ROMAddr, 1); + if(temp = 0xFFFF) return; + + index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex]._VB_LCDHIndex; + for(i=0x14, j=0; i<=0x17; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,i,SiS300_LCDHData[temp][index][j]); + } + SiS_SetRegANDOR(SiS_SiS_Part1Port,0x1a, 0xf8, (SiS300_LCDHData[temp][index][j] & 0x07)); + + index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex]._VB_LCDVIndex; + SiS_SetReg1(SiS_SiS_Part1Port,0x18, SiS300_LCDVData[temp][index][0]); + SiS_SetRegANDOR(SiS_SiS_Part1Port,0x19, 0xF0, SiS300_LCDVData[temp][index][1]); + SiS_SetRegANDOR(SiS_SiS_Part1Port,0x1A, 0xC7, (SiS300_LCDVData[temp][index][2] & 0x38)); + for(i=0x1b, j=3; i<=0x1d; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part1Port,i,SiS300_LCDVData[temp][index][j]); + } +#endif +} + +static USHORT GetOEMTVPtr(SiS_Private *SiS_Pr) { USHORT index; @@ -9284,58 +11786,81 @@ return index; } -/* TW: Checked against 630/301B 2.04.50 and 630/LVDS BIOS (incl data) */ -void +static void SetOEMTVDelay(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp; + USHORT index,temp,romptr=0; -#if 0 /* TW: Not used in newer BIOSes (2.04.5a) */ if((ROMAddr) && SiS_Pr->SiS_UseROM) { if(!(ROMAddr[0x238] & 0x01)) return; if(!(ROMAddr[0x238] & 0x02)) return; + romptr = ROMAddr[0x241] | (ROMAddr[0x242] << 8); } -#endif + temp = GetOEMTVPtr(SiS_Pr); index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex].VB_TVDelayIndex; - if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { - temp = SiS300_OEMTVDelay301[temp][index]; + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += index; + temp = ROMAddr[romptr]; } else { - temp = SiS300_OEMTVDelayLVDS[temp][index]; + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { + temp = SiS300_OEMTVDelay301[temp][index]; + } else { + temp = SiS300_OEMTVDelayLVDS[temp][index]; + } } temp &= 0x3c; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,~0x3C,temp); /* index 0A D[6:4] */ } -/* TW: Checked against 630/301B 2.04.50 BIOS (incl data) */ -void +static void SetOEMAntiFlicker(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension, USHORT BaseAddr,UCHAR *ROMAddr,USHORT ModeNo, USHORT ModeIdIndex) { - USHORT index,temp; + USHORT index,temp,romptr=0; + + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + if(!(ROMAddr[0x238] & 0x01)) return; + if(!(ROMAddr[0x238] & 0x04)) return; + romptr = ROMAddr[0x243] | (ROMAddr[0x244] << 8); + } temp = GetOEMTVPtr(SiS_Pr); index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex].VB_TVFlickerIndex; - temp = SiS300_OEMTVFlicker[temp][index]; + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += index; + temp = ROMAddr[romptr]; + } else { + temp = SiS300_OEMTVFlicker[temp][index]; + } temp &= 0x70; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x0A,0x8F,temp); /* index 0A D[6:4] */ } -/* TW: Checked against 630/301B 2.04.50 BIOS (incl data) */ -void +static void SetOEMPhaseIncr(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,i,j,temp; + USHORT index,i,j,temp,romptr=0; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVisionTV) return; + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + if(!(ROMAddr[0x238] & 0x01)) return; + if(!(ROMAddr[0x238] & 0x08)) return; + romptr = ROMAddr[0x245] | (ROMAddr[0x246] << 8); + } + temp = GetOEMTVPtr(SiS_Pr); index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex].VB_TVPhaseIndex; @@ -9345,21 +11870,35 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Phase2[temp][index][j]); } } else { - for(i=0x31, j=0; i<=0x34; i++, j++) { - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Phase1[temp][index][j]); + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += (index * 4); + for(i=0x31, j=0; i<=0x34; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,ROMAddr[romptr + j]); + } + } else { + for(i=0x31, j=0; i<=0x34; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Phase1[temp][index][j]); + } } } } -/* TW: Checked against 630/301B 2.04.50 BIOS (incl data) */ -void +static void SetOEMYFilter(SiS_Private *SiS_Pr, PSIS_HW_DEVICE_INFO HwDeviceExtension,USHORT BaseAddr, UCHAR *ROMAddr,USHORT ModeNo,USHORT ModeIdIndex) { - USHORT index,temp,temp1,i,j; + USHORT index,temp,temp1,i,j,romptr=0; if(SiS_Pr->SiS_VBInfo & (SetCRT2ToSCART | SetCRT2ToHiVisionTV)) return; + if((ROMAddr) && SiS_Pr->SiS_UseROM) { + if(!(ROMAddr[0x238] & 0x01)) return; + if(!(ROMAddr[0x238] & 0x10)) return; + romptr = ROMAddr[0x247] | (ROMAddr[0x248] << 8); + } + temp = GetOEMTVPtr(SiS_Pr); index = SiS_Pr->SiS_VBModeIDTable[ModeIdIndex].VB_TVYFilterIndex; @@ -9367,9 +11906,9 @@ if(HwDeviceExtension->jChipType > SIS_300) { if(SiS_GetReg1(SiS_Pr->SiS_P3d4,0x31) & 0x01) { temp1 = SiS_GetReg1(SiS_Pr->SiS_P3d4,0x35); - if(temp1 & (EnablePALMN | EnablePALN)) { + if(temp1 & (EnablePALM | EnablePALN)) { temp = 8; - if(temp1 & EnablePALN) temp = 9; + if(!(temp1 & EnablePALM)) temp = 9; } } } @@ -9381,8 +11920,17 @@ SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Filter2[temp][index][j]); } } else { - for(i=0x35, j=0; i<=0x38; i++, j++) { - SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Filter1[temp][index][j]); + if(romptr) { + romptr += (temp * 2); + romptr = ROMAddr[romptr] | (ROMAddr[romptr + 1] << 8); + romptr += (index * 4); + for(i=0x35, j=0; i<=0x38; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,ROMAddr[romptr + j]); + } + } else { + for(i=0x35, j=0; i<=0x38; i++, j++) { + SiS_SetReg1(SiS_Pr->SiS_Part2Port,i,SiS300_Filter1[temp][index][j]); + } } } } @@ -9396,12 +11944,15 @@ ModeIdIndex = SiS_SearchVBModeID(SiS_Pr,ROMAddr,&ModeNo); if(!(ModeIdIndex)) return; - if (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { SetOEMLCDDelay(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); + if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { + SetOEMLCDData(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); + } } - if (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { SetOEMTVDelay(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); - if(SiS_Pr->SiS_IF_DEF_LVDS==0) { + if(SiS_Pr->SiS_IF_DEF_LVDS == 0) { SetOEMAntiFlicker(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); SetOEMPhaseIncr(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); SetOEMYFilter(SiS_Pr,HwDeviceExtension,BaseAddr,ROMAddr,ModeNo,ModeIdIndex); diff --exclude-from /usr/src/exclude -u --new-file --recursive linux.21rc1/drivers/video/sis/init301.h linux.21rc1-ac2/drivers/video/sis/init301.h --- linux.21rc1/drivers/video/sis/init301.h 2003-04-22 16:38:52.000000000 +0100 +++ linux.21rc1-ac2/drivers/video/sis/init301.h 2003-04-25 16:05:08.000000000 +0100 @@ -24,9 +24,15 @@ #endif #ifdef LINUX_KERNEL +#include +#include #include #include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #include +#else +#include