From ca7e8b66f6d48ccc905219727980b6b8c6cb9ee8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:23:53 -0500 Subject: [PATCH] Linux 2.2.20pre9 o Document ip_always_defrag in proc.txt (Brett Eldrige) o Update S/390 asm for newer gcc (Ulrich Weigand o Update S/390 documentation Carsten Otte o Update s390 dump too and co) o Update s/390 dasd to match 2.4 o Backport s/390 tape driver from 2.4 o FDDI bits for s/390 o Updates for newer pmac laptops (Tom Rini) o AMD760MP support (Johannes Erdfelt) o Fix PPC oops on media change (Tom Rini) o Fix some weird but valid input combinations (Tom Rini) on PPC o Add additional checks to irc dcc masquerade (Juanjo Ciarlante, Michal Zalewski) o Update 2.2 ISDN maintainer (Kai Germaschewski) o Fix 3c505 with > 16Mb of RAM (Paul) o Bring USB into sync with 2.4.7 (Greg Kroah-Hartmann) --- Documentation/Debugging390.txt | 254 ++- Documentation/networking/ip-sysctl.txt | 5 + Documentation/proc.txt | 6 +- Documentation/s390/Debugging390.txt | 2499 ++++++++++++++++++++++++ Documentation/s390/TAPE | 122 ++ Documentation/s390/s390dump.txt | 177 ++ Documentation/usb/usb-serial.txt | 81 +- MAINTAINERS | 14 +- Makefile | 6 +- arch/i386/kernel/io_apic.c | 17 +- arch/ppc/config.in | 8 +- arch/s390/boot/Makefile | 16 +- arch/s390/boot/common.S | 298 +++ arch/s390/boot/dumpcommon.S | 220 +++ arch/s390/boot/dumpeckd.S | 548 ++++++ arch/s390/boot/dumptape.S | 183 ++ arch/s390/boot/ipldump.S | 179 -- arch/s390/defconfig | 38 +- arch/s390/kernel/entry.S | 5 +- arch/s390/kernel/reipl.S | 2 +- arch/s390/kernel/s390io.c | 107 +- arch/s390/kernel/setup.c | 2 + arch/s390/kernel/signal.c | 1 + arch/s390/kernel/smp.c | 28 +- arch/s390/kernel/traps.c | 15 +- arch/s390/mm/fault.c | 121 ++ drivers/Makefile | 4 + drivers/block/ide-pmac.c | 2 +- drivers/block/ll_rw_blk.c | 3 + drivers/char/mem.c | 6 + drivers/net/3c505.c | 8 +- drivers/s390/Config.in | 13 + drivers/s390/char/Makefile | 27 + drivers/s390/char/con3215.c | 2 +- drivers/s390/char/hwc.h | 5 +- drivers/s390/char/hwc_con.c | 2 +- drivers/s390/char/hwc_rw.c | 140 +- drivers/s390/char/hwc_rw.h | 2 +- drivers/s390/char/hwc_tty.c | 6 +- drivers/s390/char/hwc_tty.h | 2 +- drivers/s390/char/tape.c | 1120 +++++++++++ drivers/s390/char/tape.h | 203 ++ drivers/s390/char/tape3480.c | 156 ++ drivers/s390/char/tape3480.h | 23 + drivers/s390/char/tape3490.c | 156 ++ drivers/s390/char/tape3490.h | 24 + drivers/s390/char/tape34xx.c | 2382 ++++++++++++++++++++++ drivers/s390/char/tape34xx.h | 183 ++ drivers/s390/char/tapeblock.c | 598 ++++++ drivers/s390/char/tapeblock.h | 36 + drivers/s390/char/tapechar.c | 761 ++++++++ drivers/s390/char/tapechar.h | 34 + drivers/s390/char/tapedefs.h | 76 + drivers/s390/net/iucv.c | 7 + drivers/sbus/char/aurora.c | 2 +- drivers/usb/Makefile | 2 +- drivers/usb/acm.c | 44 +- drivers/usb/bluetooth.c | 3 +- drivers/usb/dabusb.c | 3 +- drivers/usb/dc2xx.c | 3 +- drivers/usb/hub.c | 625 +++--- drivers/usb/hub.h | 30 +- drivers/usb/mdc800.c | 3 +- drivers/usb/ov511.c | 3 +- drivers/usb/pegasus.c | 378 ++-- drivers/usb/pegasus.h | 202 ++ drivers/usb/plusb.c | 862 +++++--- drivers/usb/rio500.c | 3 +- drivers/usb/serial/belkin_sa.c | 3 +- drivers/usb/serial/digi_acceleport.c | 3 +- drivers/usb/serial/empeg.c | 165 +- drivers/usb/serial/ftdi_sio.c | 3 +- drivers/usb/serial/io_edgeport.c | 3 +- drivers/usb/serial/keyspan.c | 3 +- drivers/usb/serial/keyspan_pda.c | 3 +- drivers/usb/serial/mct_u232.c | 3 +- drivers/usb/serial/omninet.c | 3 +- drivers/usb/serial/usbserial.c | 3 +- drivers/usb/serial/visor.c | 3 +- drivers/usb/serial/whiteheat.c | 138 +- drivers/usb/usb.c | 239 ++- drivers/video/aty128fb.c | 51 +- drivers/video/macmodes.c | 20 +- drivers/video/offb.c | 1 + include/asm-s390/bitops.h | 4 +- include/asm-s390/byteorder.h | 12 +- include/asm-s390/current.h | 2 +- include/asm-s390/debug.h | 2 +- include/asm-s390/io.h | 2 +- include/asm-s390/lowcore.h | 29 +- include/asm-s390/pgtable.h | 4 +- include/asm-s390/processor.h | 2 +- include/asm-s390/system.h | 10 +- include/asm-s390/uaccess.h | 19 +- include/linux/fddidevice.h | 2 + include/linux/netdevice.h | 2 + include/linux/usb.h | 85 +- include/video/macmodes.h | 4 +- net/ipv4/ip_masq_irc.c | 59 +- 99 files changed, 12494 insertions(+), 1484 deletions(-) create mode 100644 Documentation/s390/Debugging390.txt create mode 100644 Documentation/s390/TAPE create mode 100644 Documentation/s390/s390dump.txt create mode 100644 arch/s390/boot/common.S create mode 100644 arch/s390/boot/dumpcommon.S create mode 100644 arch/s390/boot/dumpeckd.S create mode 100644 arch/s390/boot/dumptape.S delete mode 100644 arch/s390/boot/ipldump.S create mode 100644 drivers/s390/char/tape.c create mode 100644 drivers/s390/char/tape.h create mode 100644 drivers/s390/char/tape3480.c create mode 100644 drivers/s390/char/tape3480.h create mode 100644 drivers/s390/char/tape3490.c create mode 100644 drivers/s390/char/tape3490.h create mode 100644 drivers/s390/char/tape34xx.c create mode 100644 drivers/s390/char/tape34xx.h create mode 100644 drivers/s390/char/tapeblock.c create mode 100644 drivers/s390/char/tapeblock.h create mode 100644 drivers/s390/char/tapechar.c create mode 100644 drivers/s390/char/tapechar.h create mode 100644 drivers/s390/char/tapedefs.h create mode 100644 drivers/usb/pegasus.h diff --git a/Documentation/Debugging390.txt b/Documentation/Debugging390.txt index 6123b097dcf5..58a690be46c0 100644 --- a/Documentation/Debugging390.txt +++ b/Documentation/Debugging390.txt @@ -1,19 +1,21 @@ - Debugging on Linux for 390 + Debugging on Linux for s/390 & zSeries by Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation Best viewed with fixed width fonts Overview of Document: ===================== This document is intended to give an good overview of how to debug -Linux for S390 it isn't intended as a complete reference & not a +Linux for s/390 & zSeries it isn't intended as a complete reference & not a tutorial on the fundamentals of C & assembly, it dosen't go into 390 IO in any detail. It is intended to compliment the following books. +z/Architecture Principles of Operation SA22-7832-00 Enterprise Systems Architecture/390 Reference Summary SA22-7209-01 & the Enterprise Systems Architecture/390 Principles of Operation SA22-7201-05 +Linux for zSeries and S/390 Elf Application Binary Interface & any other worthwhile references you get. It is intended like the Enterprise Systems Architecture/390 Reference Summary @@ -22,21 +24,21 @@ problems occur. Contents ======== -S390 Register Set +Register Set Address Spaces on Linux -Address Spaces on Linux for S390 -The Linux for S390 Kernel Task Structure -Register Usage & Stackframes on Linux for S390 with glossary -Compiling programs for debugging on Linux for S390 +Address Spaces on Linux for s/390 & zSeries +The Linux for s/390 & zSeries Kernel Task Structure +Register Usage & Stackframes on Linux for s/390 & zSeries with glossary +Compiling programs for debugging on Linux for s/390 & zSeries Figuring out gcc compile errors Debugging Tools objdump strace Debugging under VM Stack Tracing under VM -S390 IO Overview -Debugging IO on S390 under VM -GDB on S390 +s/390 & zSeries IO Overview +Debugging IO on s/390 & zSeries under VM +GDB on s/390 & zSeries Stack chaining in gdb by hand Examining core dumps LDD @@ -44,13 +46,13 @@ Debugging modules The proc file system Starting points for debugging scripting languages etc. -S390 Register Set -================ -The current ESA 390 architecture has the following registers. +Register Set +============ +The current architectures have the following registers. -16 32 bit General propose registers ( r0-r15 or gpr0-gpr15) used for arithmetic & addressing +16 General propose registers, 32 bit on s/390 64 bit on zSeries, r0-r15 or gpr0-gpr15 used for arithmetic & addressing. -16 Control registers ( cr0-cr15 kernel usage only ) used for memory managment, +16 Control registers, 32 bit on s/390 64 bit on zSeries, ( cr0-cr15 kernel usage only ) used for memory managment, interrupt control,debugging control etc. 16 Access registers ( ar0-ar15 ) not used by normal programs but potentially could @@ -67,8 +69,8 @@ Linux (currently) always uses IEEE & emulates G5 IEEE format on older machines, The PSW is the most important register on the machine it -is 64 bit & serves the roles of a program counter (pc), -condition code register,memory space designator. +is 64 bit on s/390 & 128 bit on zSeries & serves the roles of +a program counter (pc), condition code register,memory space designator. In IBM standard notation I am counting bit 0 as the MSB. It has several advantages over a normal program counter in that you can change address translation & program counter @@ -77,73 +79,98 @@ e.g. switching address translation off requires that you have a logical=physical mapping for the address you are currently running at. -Bit Value + Bit Value +s/390 zSeries +0 0 Reserved ( must be 0 ) otherwise specification exception occurs. + +1 1 Program Event Recording 1 PER enabled, + PER is used to facilititate debugging e.g. single stepping. -0 Reserved ( must be 0 ) otherwise specification exception occurs. +2-4 2-4 Reserved ( must be 0 ). -1 Program Event Recording 1 PER enabled, - PER is used to facilititate debugging e.g. single stepping. +5 5 Dynamic address translation 1=DAT on. -2-4 Reserved ( must be 0 ). +6 6 Input/Output interrupt Mask -5 Dynamic address translation 1=DAT on. +7 7 External interrupt Mask used primarily for interprocessor signalling & + clock interupts. -6 Input/Output interrupt Mask +8-11 8-11 PSW Key used for complex memory protection mechanism not used under linux -7 External interrupt Mask used primarily for interprocessor signalling & - clock interupts. +12 12 1 on s/390 0 on zSeries -8-12 PSW Key used for complex memory protection mechanism not used under linux +13 13 Machine Check Mask 1=enable machine check interrupts -13 Machine Check Mask 1=enable machine check interrupts +14 14 Wait State set this to 1 to stop the processor except for interrupts & give + time to other LPARS used in CPU idle in the kernel to increase overall + usage of processor resources. -14 Wait State set this to 1 to stop the processor except for interrupts & give - time to other LPARS used in CPU idle in the kernel to increase overall - usage of processor resources. +15 15 Problem state ( if set to 1 certain instructions are disabled ) + all linux user programs run with this bit 1 + ( useful info for debugging under VM ). -15 Problem state ( if set to 1 certain instructions are disabled ) - all linux user programs run with this bit 1 ( useful info for debugging under VM ). +16-17 16-17 Address Space Control -16-17 Address Space Control + 00 Primary Space Mode when DAT on + The linux kernel currently runs in this mode, CR1 is affiliated with + this mode & points to the primary segment table origin etc. - 00 Primary Space Mode when DAT on - The linux kernel currently runs in this mode, CR1 is affiliated with this mode - & points to the primary segment table origin etc. + 01 Access register mode this mode is used in functions to + copy data between kernel & user space. - 01 Access register mode this mode is used in functions to - copy data between kernel & user space. + 10 Secondary space mode not used in linux however CR7 the + register affiliated with this mode is & this & normally + CR13=CR7 to allow us to copy data between kernel & user space. + We do this as follows: + We set ar2 to 0 to designate its + affiliated gpr ( gpr2 )to point to primary=kernel space. + We set ar4 to 1 to designate its + affiliated gpr ( gpr4 ) to point to secondary=home=user space + & then essentially do a memcopy(gpr2,gpr4,size) to + copy data between the address spaces, the reason we use home space for the + kernel & don't keep secondary space free is that code will not run in + secondary space. - 10 Secondary space mode not used in linux however CR7 the - register affiliated with this mode is & this & normally - CR13=CR7 to allow us to copy data between kernel & user space. - We do this as follows: - We set ar2 to 0 to designate its - affiliated gpr ( gpr2 )to point to primary=kernel space. - We set ar4 to 1 to designate its - affiliated gpr ( gpr4 ) to point to secondary=home=user space - & then essentially do a memcopy(gpr2,gpr4,size) to - copy data between the address spaces, the reason we use home space for the - kernel & don't keep secondary space free is that code will not run in secondary space. + 11 Home Space Mode all user programs run in this mode. + it is affiliated with CR13. - 11 Home Space Mode all user programs run in this mode. - it is affiliated with CR13. +18-19 18-19 Condition codes (CC) -18-19 Condition codes (CC) +20 20 Fixed point overflow mask if 1=FPU exceptions for this event + occur ( normally 0 ) -20 Fixed point overflow mask if 1=FPU exceptions for this event occur ( normally 0 ) +21 21 Decimal overflow mask if 1=FPU exceptions for this event occur + ( normally 0 ) -21 Decimal overflow mask if 1=FPU exceptions for this event occur ( normally 0 ) +22 22 Exponent underflow mask if 1=FPU exceptions for this event occur + ( normally 0 ) -22 Exponent underflow mask if 1=FPU exceptions for this event occur ( normally 0 ) +23 23 Significance Mask if 1=FPU exceptions for this event occur + ( normally 0 ) -23 Significance Mask if 1=FPU exceptions for this event occur ( normally 0 ) +24-31 24-30 Reserved Must be 0. -24-31 Reserved Must be 0. + 31 Extended Addressing Mode + 32 Basic Addressing Mode + Used to set addressing mode + PSW 31 PSW 32 + 0 0 24 bit + 0 1 31 bit + 1 1 64 bit -32 1=31 bit addressing mode 0=24 bit addressing mode (for backward compatibility ), - linux always runs with this bit set to 1 +32 1=31 bit addressing mode 0=24 bit addressing mode (for backward + compatibility ), linux always runs with this bit set to 1 -33-64 Instruction address. +33-64 Instruction address. + 33-63 Reserved must be 0 + 64-127 Address + In 24 bits mode bits 64-103=0 bits 104-127 Address + In 31 bits mode bits 64-96=0 bits 97-127 Address + Note: unlike 31 bit mode on s/390 bit 96 must be zero + when loading the address with LPSWE otherwise a + specification exception occurs, LPSW is fully backward + compatible. + Prefix Page ----------- @@ -221,8 +248,8 @@ Address Spaces on Linux for S390 Our addressing scheme is as follows -Himem 0x7fffffff 2GB ***************** **************** - * User Stack * * * +Himem 0x7fffffff 2GB on s/390 ***************** **************** +2^64 bytes on zSeries * User Stack * * * ***************** * * * Shared Libs * * * ***************** * * @@ -248,6 +275,7 @@ the __LC_KERNEL_STACK variable in the spare prefix area for this cpu The kernel stack pointer is intimately tied with the task stucture for each processor as follows. + s/390 ************************ * 1 page kernel stack * * ( 4K ) * @@ -256,9 +284,18 @@ each processor as follows. * ( 4K ) * 8K aligned ************************ + zSeries + ************************ + * 2 page kernel stack * + * ( 8K ) * + ************************ + * 2 page task_struct * + * ( 8K ) * +16K aligned ************************ + What this means is that we don't need to dedicate any register or global variable to point to the current running process & can retrieve it with the following -very simple construct +very simple construct for s/390 & one very similar for zSeries. static inline struct task_struct * get_current(void) { @@ -290,6 +327,10 @@ after a crash without a source level debugger. Note: To follow stackframes requires a knowledge of C or Pascal & limited knowledge of one assembly language. +It should be noted that there are some differences between the +s/390 & zSeries stack layouts as the zSeries stack layout didn't have +to maintain compatibility with older linkage formats. + Glossary: --------- alloca: @@ -404,8 +445,8 @@ r15 stack-pointer saved f0 argument 0 / return value ( float/double ) call-clobbered f2 argument 1 call-clobbered -f4 saved -f6 saved +f4 zSeries argument 2 saved +f6 zSeries argument 3 saved The remaining floating points f1,f3,f5 f7-f15 are call-clobbered. @@ -434,33 +475,36 @@ outgoing args area. It must be purely in the outgoing args area if crossing this boundary. 6) Floating point parameters are mixed with outgoing args on the outgoing args area in the order the are passed in as parameters. +7) Floating point arguments 2 & 3 are saved in the outgoing args area for zSeries + Stack Frame Layout ================== -0 back chain ( a 0 here signifies end of back chain ) -4 eos ( end of stack, not used on Linux for S390 used in other linkage formats ) -8 glue used in other linkage formats for saved routine descriptors etc. -12 glue used in other linkage formats for saved routine descriptors etc. -16 scratch area -20 scratch area -24 saved r6 of caller function -28 saved r7 of caller function -32 saved r8 of caller function -36 saved r9 of caller function -40 saved r10 of caller function -44 saved r11 of caller function -48 saved r12 of caller function -52 saved r13 of caller function -56 saved r14 of caller function -60 saved r15 of caller function -64 saved f4 of caller function -72 saved f6 of caller function -80 undefined -96 outgoing args passed from caller to callee -96+x possible stack alignment ( 8 bytes desirable ) -96+x+y alloca space of caller ( if used ) -96+x+y+z automatics of caller ( if used ) -0 back-chain +s/390 zSeries +0 0 back chain ( a 0 here signifies end of back chain ) +4 8 eos ( end of stack, not used on Linux for S390 used in other linkage formats ) +8 16 glue used in other s/390 linkage formats for saved routine descriptors etc. +12 24 glue used in other s/390 linkage formats for saved routine descriptors etc. +16 32 scratch area +20 40 scratch area +24 48 saved r6 of caller function +28 56 saved r7 of caller function +32 64 saved r8 of caller function +36 72 saved r9 of caller function +40 80 saved r10 of caller function +44 88 saved r11 of caller function +48 96 saved r12 of caller function +52 104 saved r13 of caller function +56 112 saved r14 of caller function +60 120 saved r15 of caller function +64 128 saved f4 of caller function +72 132 saved f6 of caller function +80 undefined +96 160 outgoing args passed from caller to callee +96+x 160+x possible stack alignment ( 8 bytes desirable ) +96+x+y 160+x+y alloca space of caller ( if used ) +96+x+y+z 160+x+y+z automatics of caller ( if used ) +0 back-chain A sample program with comments. =============================== @@ -551,14 +595,10 @@ have been warned. -Compiling programs for debugging on Linux for S390 -================================================== -Make sure that the gcc is compiling & linking with the -g flag on -this generates plain old gnu stabs, don't use --ggdb, -gxcoff+ or any other silly option these other options more than -likely don't work ( we haven't tested them ), -gstabs is supposed to add -extra extensions to the debugging info for debugging c++ we haven't got -round to testing this yet. +Compiling programs for debugging on Linux for s/390 & zSeries +============================================================= +-gdwarf2 now works & normal -g debugging works much better now +Thanks to the IBM java compiler developers bug reports. This is typically done adding/appending the flags -g to the CFLAGS & LDFLAGS variables Makefile of the program concerned. @@ -574,6 +614,10 @@ in registers onto the stack so backtracing & looking at passed in parameters will work, however some larger programs which use inline functions will not compile without optimisation. +Debugging with optimisation has since much improved after fixing +some bugs, please make sure you are using gdb-5.0 or later developed +after Nov'2000. + Figuring out gcc compile errors =============================== If you are getting a lot of syntax errors compiling a program & the problem @@ -931,8 +975,8 @@ As some of you are probably in a panic now this isn't as unintuitive as it may s as the 390 instructions are easy to decode mentally & you can make a good guess at a lot of them as all the operands are nibble ( half byte aligned ) & if you have an objdump listing also it is quite easy to follow, if you don't have an objdump listing keep a copy of -the ESA Reference Summary & look at between pages 2 & 7 or alternatively the -ESA principles of operation. +the s/390 Reference Summary & look at between pages 2 & 7 or alternatively the +s/390 principles of operation. e.g. even I can guess that 0001AFF8' LR 180F CC 0 is a ( load register ) lr r0,r15 @@ -1085,7 +1129,7 @@ The most common ones you will normally be tracing for is 10=segment translation exception 11=page translation exception -The full list of these is on page 22 of the current ESA Reference Summary. +The full list of these is on page 22 of the current s/390 Reference Summary. e.g. tr prog 10 will trace segment translation exceptions. tr prog on its own will trace all program interruption codes. @@ -1161,7 +1205,7 @@ We have stopped at a breakpoint 000151B0' SVC 0A05 -> 0001909A' CC 0 D 20.8 to check the SVC old psw in the prefix area & see was it from userspace -( for the layout of the prefix area consult P18 of the ESA 390 Reference Summary +( for the layout of the prefix area consult P18 of the s/390 390 Reference Summary if you have it available ). V00000020 070C2000 800151B2 The problem state bit wasn't set & it's also too early in the boot sequence @@ -1334,7 +1378,7 @@ S390 IO Overview I am not going to give a course in 390 IO architecture as this would take me quite a while & I'm no expert. Instead I'll give a 390 IO architecture summary for Dummies if you have -the ESA principles of operation available read this instead. If nothing else you may find a few +the s/390 principles of operation available read this instead. If nothing else you may find a few useful keywords in here & be able to use them on a web search engine like altavista to find more useful information. @@ -2031,4 +2075,6 @@ Various bits of man & info pages of Linux. Linux & GDB source. Various info & man pages. CMS Help on tracing commands. +Linux for zSeries and S/390 Elf Application Binary Interface ( Highly Recommended ) +z/Architecture Principles of Operation diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 208b7a0f1621..776f2fd464d6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -39,6 +39,11 @@ ip_fib_model - INTEGER IP Fragmentation: +ip_always_defrag - BOOLEAN + 0 - (DEFAULT) Normal setting for a normal router or host. + 1 - Reassemble all fragments before processing. Useful for + a firewall or transparent proxying hosts. + ipfrag_high_thresh - INTEGER Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, diff --git a/Documentation/proc.txt b/Documentation/proc.txt index eb14f689ac1b..613460ff1033 100644 --- a/Documentation/proc.txt +++ b/Documentation/proc.txt @@ -35,7 +35,7 @@ Contents 3.6 /proc/sys/dev - Device specific parameters 3.7 /proc/sys/sunrpc - Remote procedure calls 3.8 /proc/sys/net - Networking stuff -3.9 /proc/sys/net/ipv4 - IPV4 settings=20 +3.9 /proc/sys/net/ipv4 - IPV4 settings 3.10 Appletalk 3.11 IPX @@ -861,7 +861,7 @@ swapctl sc_age_cluster_fract -------------------- * resident set size - 1024 =20 + 1024 So if you want kswapd to scan the whole process, sc_age_cluster_fract needs to have a value of 1024. The minimum @@ -1050,7 +1050,7 @@ ip_always_defrag This is automagically enabled when enabling masquerading. -ipfrag_high_trash and ipfrag_low_trash +ipfrag_high_tresh and ipfrag_low_tresh Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, the fragment handler will toss packets until ipfrag_low_thresh is diff --git a/Documentation/s390/Debugging390.txt b/Documentation/s390/Debugging390.txt new file mode 100644 index 000000000000..32e668a6aa8e --- /dev/null +++ b/Documentation/s390/Debugging390.txt @@ -0,0 +1,2499 @@ + + Debugging on Linux for s/390 & z/Architecture + by + Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + Copyright (C) 2000-2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + Best viewed with fixed width fonts + +Overview of Document: +===================== +This document is intended to give an good overview of how to debug +Linux for s/390 & z/Architecture it isn't intended as a complete reference & not a +tutorial on the fundamentals of C & assembly, it dosen't go into +390 IO in any detail. It is intended to compliment the documents in the +reference section below & any other worthwhile references you get. + +It is intended like the Enterprise Systems Architecture/390 Reference Summary +to be printed out & used as a quick cheat sheet self help style reference when +problems occur. + +Contents +======== +Register Set +Address Spaces on Intel Linux +Address Spaces on Linux for s/390 & z/Architecture +The Linux for s/390 & z/Architecture Kernel Task Structure +Register Usage & Stackframes on Linux for s/390 & z/Architecture +A sample program with comments +Compiling programs for debugging on Linux for s/390 & z/Architecture +Figuring out gcc compile errors +Debugging Tools +objdump +strace +Performance Debugging +Debugging under VM +s/390 & z/Architecture IO Overview +Debugging IO on s/390 & z/Architecture under VM +GDB on s/390 & z/Architecture +Stack chaining in gdb by hand +Examining core dumps +ldd +Debugging modules +The proc file system +Starting points for debugging scripting languages etc. +SysRq +References +Special Thanks + +Register Set +============ +The current architectures have the following registers. + +16 General propose registers, 32 bit on s/390 64 bit on z/Architecture, r0-r15 or gpr0-gpr15 used for arithmetic & addressing. + +16 Control registers, 32 bit on s/390 64 bit on z/Architecture, ( cr0-cr15 kernel usage only ) used for memory managment, +interrupt control,debugging control etc. + +16 Access registers ( ar0-ar15 ) 32 bit on s/390 & z/Architecture +not used by normal programs but potentially could +be used as temporary storage. Their main purpose is their 1 to 1 +association with general purpose registers and are used in +the kernel for copying data between kernel & user address spaces. +Access register 0 ( & access register 1 on z/Architecture ( needs 64 bit +pointer ) ) is currently used by the pthread library as a pointer to +the current running threads private area. + +16 64 bit floating point registers (fp0-fp15 ) IEEE & HFP floating +point format compliant on G5 upwards & a Floating point control reg (FPC) +4 64 bit registers (fp0,fp2,fp4 & fp6) HFP only on older machines. +Note: +Linux (currently) always uses IEEE & emulates G5 IEEE format on older machines, +( provided the kernel is configured for this ). + + +The PSW is the most important register on the machine it +is 64 bit on s/390 & 128 bit on z/Architecture & serves the roles of +a program counter (pc), condition code register,memory space designator. +In IBM standard notation I am counting bit 0 as the MSB. +It has several advantages over a normal program counter +in that you can change address translation & program counter +in a single instruction. To change address translation, +e.g. switching address translation off requires that you +have a logical=physical mapping for the address you are +currently running at. + + Bit Value +s/390 z/Architecture +0 0 Reserved ( must be 0 ) otherwise specification exception occurs. + +1 1 Program Event Recording 1 PER enabled, + PER is used to facilititate debugging e.g. single stepping. + +2-4 2-4 Reserved ( must be 0 ). + +5 5 Dynamic address translation 1=DAT on. + +6 6 Input/Output interrupt Mask + +7 7 External interrupt Mask used primarily for interprocessor signalling & + clock interupts. + +8-11 8-11 PSW Key used for complex memory protection mechanism not used under linux + +12 12 1 on s/390 0 on z/Architecture + +13 13 Machine Check Mask 1=enable machine check interrupts + +14 14 Wait State set this to 1 to stop the processor except for interrupts & give + time to other LPARS used in CPU idle in the kernel to increase overall + usage of processor resources. + +15 15 Problem state ( if set to 1 certain instructions are disabled ) + all linux user programs run with this bit 1 + ( useful info for debugging under VM ). + +16-17 16-17 Address Space Control + + 00 Primary Space Mode when DAT on + The linux kernel currently runs in this mode, CR1 is affiliated with + this mode & points to the primary segment table origin etc. + + 01 Access register mode this mode is used in functions to + copy data between kernel & user space. + + 10 Secondary space mode not used in linux however CR7 the + register affiliated with this mode is & this & normally + CR13=CR7 to allow us to copy data between kernel & user space. + We do this as follows: + We set ar2 to 0 to designate its + affiliated gpr ( gpr2 )to point to primary=kernel space. + We set ar4 to 1 to designate its + affiliated gpr ( gpr4 ) to point to secondary=home=user space + & then essentially do a memcopy(gpr2,gpr4,size) to + copy data between the address spaces, the reason we use home space for the + kernel & don't keep secondary space free is that code will not run in + secondary space. + + 11 Home Space Mode all user programs run in this mode. + it is affiliated with CR13. + +18-19 18-19 Condition codes (CC) + +20 20 Fixed point overflow mask if 1=FPU exceptions for this event + occur ( normally 0 ) + +21 21 Decimal overflow mask if 1=FPU exceptions for this event occur + ( normally 0 ) + +22 22 Exponent underflow mask if 1=FPU exceptions for this event occur + ( normally 0 ) + +23 23 Significance Mask if 1=FPU exceptions for this event occur + ( normally 0 ) + +24-31 24-30 Reserved Must be 0. + + 31 Extended Addressing Mode + 32 Basic Addressing Mode + Used to set addressing mode + PSW 31 PSW 32 + 0 0 24 bit + 0 1 31 bit + 1 1 64 bit + +32 1=31 bit addressing mode 0=24 bit addressing mode (for backward + compatibility ), linux always runs with this bit set to 1 + +33-64 Instruction address. + 33-63 Reserved must be 0 + 64-127 Address + In 24 bits mode bits 64-103=0 bits 104-127 Address + In 31 bits mode bits 64-96=0 bits 97-127 Address + Note: unlike 31 bit mode on s/390 bit 96 must be zero + when loading the address with LPSWE otherwise a + specification exception occurs, LPSW is fully backward + compatible. + + +Prefix Page(s) +-------------- +This per cpu memory area is too intimately tied to the processor not to mention. +It exists between the real addresses 0-4096 on s/390 & 0-8192 z/Architecture & is exchanged +with a 1 page on s/390 or 2 pages on z/Architecture in absolute storage by the set +prefix instruction in linux'es startup. +This page is mapped to a different prefix for each processor in an SMP configuration +( assuming the os designer is sane of course :-) ). +Bytes 0-512 ( 200 hex ) on s/390 & 0-512,4096-4544,4604-5119 currently on z/Architecture +are used by the processor itself for holding such information as exception indications & +entry points for exceptions. +Bytes after 0xc00 hex are used by linux for per processor globals on s/390 & z/Architecture +( there is a gap on z/Architecure too currently between 0xc00 & 1000 which linux uses ). +The closest thing to this on traditional architectures is the interrupt +vector table. This is a good thing & does simplify some of the kernel coding +however it means that we now cannot catch stray NULL pointers in the +kernel without hard coded checks. + + + +Address Spaces on Intel Linux +============================= + +The traditional Intel Linux is approximately mapped as follows forgive +the ascii art. +0xFFFFFFFF 4GB Himem ***************** + * * + * Kernel Space * + * * + ***************** **************** +User Space Himem (typically 0xC0000000 3GB )* User Stack * * * + ***************** * * + * Shared Libs * * Next Process * + ***************** * to * + * * <== * Run * <== + * User Program * * * + * Data BSS * * * + * Text * * * + * Sections * * * +0x00000000 ***************** **************** + +Now it is easy to see that on Intel it is quite easy to recognise a kernel address +as being one greater than user space himem ( in this case 0xC0000000). +& addresses of less than this are the ones in the current running program on this +processor ( if an smp box ). +If using the virtual machine ( VM ) as a debugger it is quite difficult to +know which user process is running as the address space you are looking at +could be from any process in the run queue. + +The limitation of Intels addressing technique is that the linux +kernel uses a very simple real address to virtual addressing technique +of Real Address=Virtual Address-User Space Himem. +This means that on Intel the kernel linux can typically only address +Himem=0xFFFFFFFF-0xC0000000=1GB & this is all the RAM these machines +can typically use. +They can lower User Himem to 2GB or lower & thus be +able to use 2GB of RAM however this shrinks the maximum size +of User Space from 3GB to 2GB they have a no win limit of 4GB unless +they go to 64 Bit. + + +On 390 our limitations & strengths make us slightly different. +For backward compatibility we are only allowed use 31 bits (2GB) +of our 32 bit addresses,however, we use entirely separate address +spaces for the user & kernel. + +This means we can support 2GB of non Extended RAM on s/390, & more +with the Extended memory managment swap device & +currently 4TB of physical memory currently on z/Architecture. + + +Address Spaces on Linux for s/390 & z/Architecture +================================================== + +Our addressing scheme is as follows + + +Himem 0x7fffffff 2GB on s/390 ***************** **************** +currently 0x3ffffffffff (2^42)-1 * User Stack * * * +on z/Architecture. ***************** * * + * Shared Libs * * * + ***************** * * + * * * Kernel * + * User Program * * * + * Data BSS * * * + * Text * * * + * Sections * * * +0x00000000 ***************** **************** + +This also means that we need to look at the PSW problem state bit +or the addressing mode to decide whether we are looking at +user or kernel space. + +Virtual Addresses on s/390 & z/Architecture +=========================================== + +A virtual address on s/390 is made up of 3 parts +The SX ( segment index, roughly corresponding to the PGD & PMD in linux terminology ) +being bits 1-11. +The PX ( page index, corresponding to the page table entry (pte) in linux terminology ) +being bits 12-19. +The remaining bits BX (the byte index are the offset in the page ) +i.e. bits 20 to 31. + +On z/Architecture in linux we currently make up an address from 4 parts. +The region index bits (RX) 0-32 we currently use bits 22-32 +The segment index (SX) being bits 33-43 +The page index (PX) being bits 44-51 +The byte index (BX) being bits 52-63 + +Notes: +1) s/390 has no PMD so the PMD is really the PGD also. +A lot of this stuff is defined in pgtable.h. + +2) Also seeing as s/390's page indexes are only 1k in size +(bits 12-19 x 4 bytes per pte ) we use 1 ( page 4k ) +to make the best use of memory by updating 4 segment indices +entries each time we mess with a PMD & use offsets +0,1024,2048 & 3072 in this page as for our segment indexes. +On z/Architecture our page indexes are now 2k in size +( bits 12-19 x 8 bytes per pte ) we do a similar trick +but only mess with 2 segment indices each time we mess with +a PMD. + +3) As z/Architecture supports upto a massive 5-level page table lookup we +can only use 3 currently on Linux ( as this is all the generic kernel +currently supports ) however this may change in future +this allows us to access ( according to my sums ) +4TB of virtual storage per process i.e. +4096*512(PTES)*1024(PMDS)*2048(PGD) = 4398046511104 bytes, +enough for another 2 or 3 of years I think :-). +to do this we use a region-third-table designation type in +our address space control registers. + + +The Linux for s/390 & z/Architecture Kernel Task Structure +========================================================== +Each process/thread under Linux for S390 has its own kernel task_struct +defined in linux/include/linux/sched.h +The S390 on initialisation & resuming of a process on a cpu sets +the __LC_KERNEL_STACK variable in the spare prefix area for this cpu +( which we use for per processor globals). + +The kernel stack pointer is intimately tied with the task stucture for +each processor as follows. + + s/390 + ************************ + * 1 page kernel stack * + * ( 4K ) * + ************************ + * 1 page task_struct * + * ( 4K ) * +8K aligned ************************ + + z/Architecture + ************************ + * 2 page kernel stack * + * ( 8K ) * + ************************ + * 2 page task_struct * + * ( 8K ) * +16K aligned ************************ + +What this means is that we don't need to dedicate any register or global variable +to point to the current running process & can retrieve it with the following +very simple construct for s/390 & one very similar for z/Architecture. + +static inline struct task_struct * get_current(void) +{ + struct task_struct *current; + __asm__("lhi %0,-8192\n\t" + "nr %0,15" + : "=r" (current) ); + return current; +} + +i.e. just anding the current kernel stack pointer with the mask -8192. +Thankfully because Linux dosen't have support for nested IO interrupts +& our devices have large buffers can survive interrupts being shut for +short amounts of time we don't need a separate stack for interrupts. + + + + +Register Usage & Stackframes on Linux for s/390 & z/Architecture +================================================================= +Overview: +--------- +This is the code that gcc produces at the top & the bottom of +each function, it usually is fairly consistent & similar from +function to function & if you know its layout you can probalby +make some headway in finding the ultimate cause of a problem +after a crash without a source level debugger. + +Note: To follow stackframes requires a knowledge of C or Pascal & +limited knowledge of one assembly language. + +It should be noted that there are some differences between the +s/390 & z/Architecture stack layouts as the z/Architecture stack layout didn't have +to maintain compatibility with older linkage formats. + +Glossary: +--------- +alloca: +This is a built in compiler function for runtime allocation +of extra space on the callers stack which is obviously freed +up on function exit ( e.g. the caller may choose to allocate nothing +of a buffer of 4k if required for temporary purposes ), it generates +very efficent code ( a few cycles ) when compared to alternatives +like malloc. + +automatics: These are local variables on the stack, +i.e they aren't in registers & they aren't static. + +back-chain: +This is a pointer to the stack pointer before entering a +framed functions ( see frameless function ) prologue got by +deferencing the address of the current stack pointer, + i.e. got by accessing the 32 bit value at the stack pointers +current location. + +base-pointer: +This is a pointer to the back of the literal pool which +is an area just behind each procedure used to store constants +in each function. + +call-clobbered: The caller probably needs to save these registers if there +is something of value in them, on the stack or elsewhere before making a +call to another procedure so that it can restore it later. + +epilogue: +The code generated by the compiler to return to the caller. + +frameless-function +A frameless function in Linux for s390 & z/Architecture is one which doesn't +need more than the register save area ( 96 bytes on s/390, 160 on z/Architecture ) +given to it by the caller. +A frameless function never: +1) Sets up a back chain. +2) Calls alloca. +3) Calls other normal functions +4) Has automatics. + +GOT-pointer: +This is a pointer to the global-offset-table in ELF +( Executable Linkable Format, Linux'es most common executable format ), +all globals & shared library objects are found using this pointer. + +lazy-binding +ELF shared libraries are typically only loaded when routines in the shared +library are actually first called at runtime. This is lazy binding. + +procedure-linkage-table +This is a table found from the GOT which contains pointers to routines +in other shared libraries which can't be called to by easier means. + +prologue: +The code generated by the compiler to set up the stack frame. + +outgoing-args: +This is extra area allocated on the stack of the calling function if the +parameters for the callee's cannot all be put in registers, the same +area can be reused by each function the caller calls. + +routine-descriptor: +A COFF executable format based concept of a procedure reference +actually being 8 bytes or more as opposed to a simple pointer to the routine. +This is typically defined as follows +Routine Descriptor offset 0=Pointer to Function +Routine Descriptor offset 4=Pointer to Table of Contents +The table of contents/TOC is roughly equivalent to a GOT pointer. +& it means that shared libraries etc. can be shared between several +environments each with their own TOC. + + +static-chain: This is used in nested functions a concept adopted from pascal +by gcc not used in ansi C or C++ ( although quite useful ), basically it +is a pointer used to reference local variables of enclosing functions. +You might come across this stuff once or twice in your lifetime. + +e.g. +The function below should return 11 though gcc may get upset & toss warnings +about unused variables. +int FunctionA(int a) +{ + int b; + FunctionC(int c) + { + b=c+1; + } + FunctionC(10); + return(b); +} + + +s/390 & z/Architecture Register usage +===================================== +r0 used by syscalls/assembly call-clobbered +r1 used by syscalls/assembly call-clobbered +r2 argument 0 / return value 0 call-clobbered +r3 argument 1 / return value 1 (if long long) call-clobbered +r4 argument 2 call-clobbered +r5 argument 3 call-clobbered +r6 argument 5 saved +r7 pointer-to arguments 5 to ... saved +r8 this & that saved +r9 this & that saved +r10 static-chain ( if nested function ) saved +r11 frame-pointer ( if function used alloca ) saved +r12 got-pointer saved +r13 base-pointer saved +r14 return-address saved +r15 stack-pointer saved + +f0 argument 0 / return value ( float/double ) call-clobbered +f2 argument 1 call-clobbered +f4 z/Architecture argument 2 saved +f6 z/Architecture argument 3 saved +The remaining floating points +f1,f3,f5 f7-f15 are call-clobbered. + +Notes: +------ +1) The only requirement is that registers which are used +by the callee are saved, e.g. the compiler is perfectly +capible of using r11 for purposes other than a frame a +frame pointer if a frame pointer is not needed. +2) In functions with variable arguments e.g. printf the calling procedure +is identical to one without variable arguments & the same number of +parameters. However, the prologue of this function is somewhat more +hairy owing to it having to move these parameters to the stack to +get va_start, va_arg & va_end to work. +3) Access registers are currently unused by gcc but are used in +the kernel. Possibilities exist to use them at the moment for +temporary storage but it isn't recommended. +4) Only 4 of the floating point registers are used for +parameter passing as older machines such as G3 only have only 4 +& it keeps the stack frame compatible with other compilers. +However with IEEE floating point emulation under linux on the +older machines you are free to use the other 12. +5) A long long or double parameter cannot be have the +first 4 bytes in a register & the second four bytes in the +outgoing args area. It must be purely in the outgoing args +area if crossing this boundary. +6) Floating point parameters are mixed with outgoing args +on the outgoing args area in the order the are passed in as parameters. +7) Floating point arguments 2 & 3 are saved in the outgoing args area for +z/Architecture + + +Stack Frame Layout +------------------ +s/390 z/Architecture +0 0 back chain ( a 0 here signifies end of back chain ) +4 8 eos ( end of stack, not used on Linux for S390 used in other linkage formats ) +8 16 glue used in other s/390 linkage formats for saved routine descriptors etc. +12 24 glue used in other s/390 linkage formats for saved routine descriptors etc. +16 32 scratch area +20 40 scratch area +24 48 saved r6 of caller function +28 56 saved r7 of caller function +32 64 saved r8 of caller function +36 72 saved r9 of caller function +40 80 saved r10 of caller function +44 88 saved r11 of caller function +48 96 saved r12 of caller function +52 104 saved r13 of caller function +56 112 saved r14 of caller function +60 120 saved r15 of caller function +64 128 saved f4 of caller function +72 132 saved f6 of caller function +80 undefined +96 160 outgoing args passed from caller to callee +96+x 160+x possible stack alignment ( 8 bytes desirable ) +96+x+y 160+x+y alloca space of caller ( if used ) +96+x+y+z 160+x+y+z automatics of caller ( if used ) +0 back-chain + +A sample program with comments. +=============================== + +Comments on the function test +----------------------------- +1) It didn't need to set up a pointer to the constant pool gpr13 as it isn't used +( :-( ). +2) This is a frameless function & no stack is bought. +3) The compiler was clever enough to recognise that it could return the +value in r2 as well as use it for the passed in parameter ( :-) ). +4) The basr ( branch relative & save ) trick works as follows the instruction +has a special case with r0,r0 with some instruction operands is understood as +the literal value 0, some risc architectures also do this ). So now +we are branching to the next address & the address new program counter is +in r13,so now we subtract the size of the function prologue we have executed ++ the size of the literal pool to get to the top of the literal pool +0040037c int test(int b) +{ # Function prologue below + 40037c: 90 de f0 34 stm %r13,%r14,52(%r15) # Save registers r13 & r14 + 400380: 0d d0 basr %r13,%r0 # Set up pointer to constant pool using + 400382: a7 da ff fa ahi %r13,-6 # basr trick + return(5+b); + # Huge main program + 400386: a7 2a 00 05 ahi %r2,5 # add 5 to r2 + + # Function epilogue below + 40038a: 98 de f0 34 lm %r13,%r14,52(%r15) # restore registers r13 & 14 + 40038e: 07 fe br %r14 # return +} + +Comments on the function main +----------------------------- +1) The compiler did this function optimally ( 8-) ) + +Literal pool for main. +400390: ff ff ff ec .long 0xffffffec +main(int argc,char *argv[]) +{ # Function prologue below + 400394: 90 bf f0 2c stm %r11,%r15,44(%r15) # Save necessary registers + 400398: 18 0f lr %r0,%r15 # copy stack pointer to r0 + 40039a: a7 fa ff a0 ahi %r15,-96 # Make area for callee saving + 40039e: 0d d0 basr %r13,%r0 # Set up r13 to point to + 4003a0: a7 da ff f0 ahi %r13,-16 # literal pool + 4003a4: 50 00 f0 00 st %r0,0(%r15) # Save backchain + + return(test(5)); # Main Program Below + 4003a8: 58 e0 d0 00 l %r14,0(%r13) # load relative address of test from + # literal pool + 4003ac: a7 28 00 05 lhi %r2,5 # Set first parameter to 5 + 4003b0: 4d ee d0 00 bas %r14,0(%r14,%r13) # jump to test setting r14 as return + # address using branch & save instruction. + + # Function Epilogue below + 4003b4: 98 bf f0 8c lm %r11,%r15,140(%r15)# Restore necessary registers. + 4003b8: 07 fe br %r14 # return to do program exit +} + + +Compiler updates +---------------- + +main(int argc,char *argv[]) +{ + 4004fc: 90 7f f0 1c stm %r7,%r15,28(%r15) + 400500: a7 d5 00 04 bras %r13,400508 + 400504: 00 40 04 f4 .long 0x004004f4 + # compiler now puts constant pool in code to so it saves an instruction + 400508: 18 0f lr %r0,%r15 + 40050a: a7 fa ff a0 ahi %r15,-96 + 40050e: 50 00 f0 00 st %r0,0(%r15) + return(test(5)); + 400512: 58 10 d0 00 l %r1,0(%r13) + 400516: a7 28 00 05 lhi %r2,5 + 40051a: 0d e1 basr %r14,%r1 + # compiler adds 1 extra instruction to epilogue this is done to + # avoid processor pipeline stalls owing to data dependencies on g5 & + # above as register 14 in the old code was needed directly after being loaded + # by the lm %r11,%r15,140(%r15) for the br %14. + 40051c: 58 40 f0 98 l %r4,152(%r15) + 400520: 98 7f f0 7c lm %r7,%r15,124(%r15) + 400524: 07 f4 br %r4 +} + + +Hartmut ( our compiler developer ) also has been threatening to take out the +stack backchain in optimised code as this also causes pipeline stalls, you +have been warned. + +64 bit z/Architecture code disassembly +-------------------------------------- + +If you understand the stuff above you'll understand the stuff +below too so I'll avoid repeating myself & just say that +some of the instructions have g's on the end of them to indicate +they are 64 bit & the stack offsets are a bigger, +the only other difference you'll find between 32 & 64 bit is that +we now use f4 & f6 for floating point arguments on 64 bit. +00000000800005b0 : +int test(int b) +{ + return(5+b); + 800005b0: a7 2a 00 05 ahi %r2,5 + 800005b4: b9 14 00 22 lgfr %r2,%r2 # downcast to integer + 800005b8: 07 fe br %r14 + 800005ba: 07 07 bcr 0,%r7 + + +} + +00000000800005bc
: +main(int argc,char *argv[]) +{ + 800005bc: eb bf f0 58 00 24 stmg %r11,%r15,88(%r15) + 800005c2: b9 04 00 1f lgr %r1,%r15 + 800005c6: a7 fb ff 60 aghi %r15,-160 + 800005ca: e3 10 f0 00 00 24 stg %r1,0(%r15) + return(test(5)); + 800005d0: a7 29 00 05 lghi %r2,5 + # brasl allows jumps > 64k & is overkill here bras would do fune + 800005d4: c0 e5 ff ff ff ee brasl %r14,800005b0 + 800005da: e3 40 f1 10 00 04 lg %r4,272(%r15) + 800005e0: eb bf f0 f8 00 04 lmg %r11,%r15,248(%r15) + 800005e6: 07 f4 br %r4 +} + + + +Compiling programs for debugging on Linux for s/390 & z/Architecture +==================================================================== +-gdwarf-2 now works it should be considered the default debugging +format for s/390 & z/Architecture as it is more reliable for debugging +shared libraries, normal -g debugging works much better now +Thanks to the IBM java compiler developers bug reports. + +This is typically done adding/appending the flags -g or -gdwarf-2 to the +CFLAGS & LDFLAGS variables Makefile of the program concerned. + +If using gdb & you would like accurate displays of registers & + stack traces compile without optimisation i.e make sure +that there is no -O2 or similar on the CFLAGS line of the Makefile & +the emitted gcc commands, obviously this will produce worse code +( not advisable for shipment ) but it is an aid to the debugging process. + +This aids debugging because the compiler will copy parameters passed in +in registers onto the stack so backtracing & looking at passed in +parameters will work, however some larger programs which use inline functions +will not compile without optimisation. + +Debugging with optimisation has since much improved after fixing +some bugs, please make sure you are using gdb-5.0 or later developed +after Nov'2000. + +Figuring out gcc compile errors +=============================== +If you are getting a lot of syntax errors compiling a program & the problem +isn't blatantly obvious from the source. +It often helps to just preprocess the file, this is done with the -E +option in gcc. +What this does is that it runs through the very first phase of compilation +( compilation in gcc is done in several stages & gcc calls many programs to +achieve its end result ) with the -E option gcc just calls the gcc preprocessor (cpp). +The c preprocessor does the following, it joins all the files #included together +recursively ( #include files can #include other files ) & also the c file you wish to compile. +It puts a fully qualified path of the #included files in a comment & it +does macro expansion. +This is useful for debugging because +1) You can double check whether the files you expect to be included are the ones +that are being included ( e.g. double check that you aren't going to the i386 asm directory ). +2) Check that macro definitions aren't clashing with typedefs, +3) Check that definitons aren't being used before they are being included. +4) Helps put the line emitting the error under the microscope if it contains macros. + +For convenience the Linux kernel's makefile will do preprocessing automatically for you +by suffixing the file you want built with .i ( instead of .o ) + +e.g. +from the linux directory type +make arch/s390/kernel/signal.i +this will build + +s390-gcc -D__KERNEL__ -I/home1/barrow/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +-fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -E arch/s390/kernel/signal.c +> arch/s390/kernel/signal.i + +Now look at signal.i you should see something like. + + +# 1 "/home1/barrow/linux/include/asm/types.h" 1 +typedef unsigned short umode_t; +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; + +If instead you are getting errors further down e.g. +unknown instruction:2515 "move.l" or better still unknown instruction:2515 +"Fixme not implemented yet, call Martin" you are probably are attempting to compile some code +meant for another architecture or code that is simply not implemented, with a fixme statement +stuck into the inline assembly code so that the author of the file now knows he has work to do. +To look at the assembly emitted by gcc just before it is about to call gas ( the gnu assembler ) +use the -S option. +Again for your convenience the Linux kernel's Makefile will hold your hand & +do all this donkey work for you also by building the file with the .s suffix. +e.g. +from the Linux directory type +make arch/s390/kernel/signal.s + +s390-gcc -D__KERNEL__ -I/home1/barrow/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +-fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -S arch/s390/kernel/signal.c +-o arch/s390/kernel/signal.s + + +This will output something like, ( please note the constant pool & the useful comments +in the prologue to give you a hand at interpreting it ). + +.LC54: + .string "misaligned (__u16 *) in __xchg\n" +.LC57: + .string "misaligned (__u32 *) in __xchg\n" +.L$PG1: # Pool sys_sigsuspend +.LC192: + .long -262401 +.LC193: + .long -1 +.LC194: + .long schedule-.L$PG1 +.LC195: + .long do_signal-.L$PG1 + .align 4 +.globl sys_sigsuspend + .type sys_sigsuspend,@function +sys_sigsuspend: +# leaf function 0 +# automatics 16 +# outgoing args 0 +# need frame pointer 0 +# call alloca 0 +# has varargs 0 +# incoming args (stack) 0 +# function length 168 + STM 8,15,32(15) + LR 0,15 + AHI 15,-112 + BASR 13,0 +.L$CO1: AHI 13,.L$PG1-.L$CO1 + ST 0,0(15) + LR 8,2 + N 5,.LC192-.L$PG1(13) + +Adding -g to the above output makes the output even more useful +e.g. typing +make CC:="s390-gcc -g" kernel/sched.s + +which compiles. +s390-gcc -g -D__KERNEL__ -I/home/barrow/linux-2.3/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -S kernel/sched.c -o kernel/sched.s + +also outputs stabs ( debugger ) info, from this info you can find out the +offsets & sizes of various elements in structures. +e.g. the stab for the structure +struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; +is +.stabs "rlimit:T(151,2)=s8rlim_cur:(0,5),0,32;rlim_max:(0,5),32,32;;",128,0,0,0 +from this stab you can see that +rlimit_cur starts at bit offset 0 & is 32 bits in size +rlimit_max starts at bit offset 32 & is 32 bits in size. + + +Debugging Tools: +================ + +objdump +======= +This is a tool with many options the most useful being ( if compiled with -g). +objdump --source > + + +The whole kernel can be compiled like this ( Doing this will make a 17MB kernel +& a 200 MB listing ) however you have to strip it before building the image +using the strip command to make it a more reasonable size to boot it. + +A source/assembly mixed dump of the kernel can be done with the line +objdump --source vmlinux > vmlinux.lst +Also if the file isn't compiled -g this will output as much debugging information +as it can ( e.g. function names ), however, this is very slow as it spends lots +of time searching for debugging info, the following self explanitory line should be used +instead if the code isn't compiled -g. +objdump --disassemble-all --syms vmlinux > vmlinux.lst +as it is much faster + +As hard drive space is valuble most of us use the following approach. +1) Look at the emitted psw on the console to find the crash address in the kernel. +2) Look at the file System.map ( in the linux directory ) produced when building +the kernel to find the closest address less than the current PSW to find the +offending function. +3) use grep or similar to search the source tree looking for the source file + with this function if you don't know where it is. +4) rebuild this object file with -g on, as an example suppose the file was +( /arch/s390/kernel/signal.o ) +5) Assuming the file with the erroneous function is signal.c Move to the base of the +Linux source tree. +6) rm /arch/s390/kernel/signal.o +7) make /arch/s390/kernel/signal.o +8) watch the gcc command line emitted +9) type it in again or alernatively cut & paste it on the console adding the -g option. +10) objdump --source arch/s390/kernel/signal.o > signal.lst +This will output the source & the assembly intermixed, as the snippet below shows +This will unfortunately output addresses which aren't the same +as the kernel ones you should be able to get around the mental arithmetic +by playing with the --adjust-vma parameter to objdump. + + + + +extern inline void spin_lock(spinlock_t *lp) +{ + a0: 18 34 lr %r3,%r4 + a2: a7 3a 03 bc ahi %r3,956 + __asm__ __volatile(" lhi 1,-1\n" + a6: a7 18 ff ff lhi %r1,-1 + aa: 1f 00 slr %r0,%r0 + ac: ba 01 30 00 cs %r0,%r1,0(%r3) + b0: a7 44 ff fd jm aa + saveset = current->blocked; + b4: d2 07 f0 68 mvc 104(8,%r15),972(%r4) + b8: 43 cc + return (set->sig[0] & mask) != 0; +} + +6) If debugging under VM go down to that section in the document for more info. + + +I now have a tool which takes the pain out of --adjust-vma +& you are able to do something like +make /arch/s390/kernel/traps.lst +& it automatically generates the correctly relocated entries for +the text segment in traps.lst. +This tool is now standard in linux distro's in scripts/makelst + +strace: +------- +Q. What is it ? +A. It is a tool for intercepting calls to the kernel & logging them +to a file & on the screen. + +Q. What use is it ? +A. You can used it to find out what files a particular program opens. + + + +Example 1 +--------- +If you wanted to know does ping work but didn't have the source +strace ping -c 1 127.0.0.1 +& then look at the man pages for each of the syscalls below, +( In fact this is sometimes easier than looking at some spagetti +source which conditionally compiles for several architectures ) +Not everything that it throws out needs to make sense immeadiately + +Just looking quickly you can see that it is making up a RAW socket +for the ICMP protocol. +Doing an alarm(10) for a 10 second timeout +& doing a gettimeofday call before & after each read to see +how long the replies took, & writing some text to stdout so the user +has an idea what is going on. + +socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3 +getuid() = 0 +setuid(0) = 0 +stat("/usr/share/locale/C/libc.cat", 0xbffff134) = -1 ENOENT (No such file or directory) +stat("/usr/share/locale/libc/C", 0xbffff134) = -1 ENOENT (No such file or directory) +stat("/usr/local/share/locale/C/libc.cat", 0xbffff134) = -1 ENOENT (No such file or directory) +getpid() = 353 +setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 +setsockopt(3, SOL_SOCKET, SO_RCVBUF, [49152], 4) = 0 +fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(3, 1), ...}) = 0 +mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40008000 +ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0 +write(1, "PING 127.0.0.1 (127.0.0.1): 56 d"..., 42PING 127.0.0.1 (127.0.0.1): 56 data bytes +) = 42 +sigaction(SIGINT, {0x8049ba0, [], SA_RESTART}, {SIG_DFL}) = 0 +sigaction(SIGALRM, {0x8049600, [], SA_RESTART}, {SIG_DFL}) = 0 +gettimeofday({948904719, 138951}, NULL) = 0 +sendto(3, "\10\0D\201a\1\0\0\17#\2178\307\36"..., 64, 0, {sin_family=AF_INET, +sin_port=htons(0), sin_addr=inet_addr("127.0.0.1")}, 16) = 64 +sigaction(SIGALRM, {0x8049600, [], SA_RESTART}, {0x8049600, [], SA_RESTART}) = 0 +sigaction(SIGALRM, {0x8049ba0, [], SA_RESTART}, {0x8049600, [], SA_RESTART}) = 0 +alarm(10) = 0 +recvfrom(3, "E\0\0T\0005\0\0@\1|r\177\0\0\1\177"..., 192, 0, +{sin_family=AF_INET, sin_port=htons(50882), sin_addr=inet_addr("127.0.0.1")}, [16]) = 84 +gettimeofday({948904719, 160224}, NULL) = 0 +recvfrom(3, "E\0\0T\0006\0\0\377\1\275p\177\0"..., 192, 0, +{sin_family=AF_INET, sin_port=htons(50882), sin_addr=inet_addr("127.0.0.1")}, [16]) = 84 +gettimeofday({948904719, 166952}, NULL) = 0 +write(1, "64 bytes from 127.0.0.1: icmp_se"..., +5764 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=28.0 ms + +Example 2 +--------- +strace passwd 2>&1 | grep open +produces the following output +open("/etc/ld.so.cache", O_RDONLY) = 3 +open("/opt/kde/lib/libc.so.5", O_RDONLY) = -1 ENOENT (No such file or directory) +open("/lib/libc.so.5", O_RDONLY) = 3 +open("/dev", O_RDONLY) = 3 +open("/var/run/utmp", O_RDONLY) = 3 +open("/etc/passwd", O_RDONLY) = 3 +open("/etc/shadow", O_RDONLY) = 3 +open("/etc/login.defs", O_RDONLY) = 4 +open("/dev/tty", O_RDONLY) = 4 + +The 2>&1 is done to redirect stderr to stdout & grep is then filtering this input +through the pipe for each line containing the string open. + + +Example 3 +--------- +Getting sophistocated +telnetd crashes on & I don't know why +Steps +----- +1) Replace the following line in /etc/inetd.conf +telnet stream tcp nowait root /usr/sbin/in.telnetd -h +with +telnet stream tcp nowait root /blah + +2) Create the file /blah with the following contents to start tracing telnetd +#!/bin/bash +/usr/bin/strace -o/t1 -f /usr/sbin/in.telnetd -h +3) chmod 700 /blah to make it executable only to root +4) +killall -HUP inetd +or ps aux | grep inetd +get inetd's process id +& kill -HUP inetd to restart it. + +Important options +----------------- +-o is used to tell strace to output to a file in our case t1 in the root directory +-f is to follow children i.e. +e.g in our case above telnetd will start the login process & subsequently a shell like bash. +You will be able to tell which is which from the process ID's listed on the left hand side +of the strace output. +-p will tell strace to attach to a running process, yup this can be done provided + it isn't being traced or debugged already & you have enough privileges, +the reason 2 processes cannot trace or debug the same program is that strace +becomes the parent process of the one being debugged & processes ( unlike people ) +can have only one parent. + + +However the file /t1 will get big quite quickly +to test it telnet 127.0.0.1 + +now look at what files in.telnetd execve'd +413 execve("/usr/sbin/in.telnetd", ["/usr/sbin/in.telnetd", "-h"], [/* 17 vars */]) = 0 +414 execve("/bin/login", ["/bin/login", "-h", "localhost", "-p"], [/* 2 vars */]) = 0 + +Whey it worked!. + + +Other hints: +------------ +If the program is not very interactive ( i.e. not much keyboard input ) +& is crashing in one architecture but not in another you can do +an strace of both programs under as identical a scenario as you can +on both architectures outputting to a file then. +do a diff of the two traces using the diff program +i.e. +diff output1 output2 +& maybe you'll be able to see where the call paths differed, this +is possibly near the cause of the crash. + +More info +--------- +Look at man pages for strace & the various syscalls +e.g. man strace, man alarm, man socket. + + +Performance Debugging +===================== +gcc is capible of compiling in profiling code just add the -p option +to the CFLAGS, this obviously affects program size & performance. +This can be used by the gprof gnu profiling tool or the +gcov the gnu code coverage tool ( code coverage is a means of testing +code quality by checking if all the code in an executable in exercised by +a tester ). + + +Using top to find out where processes are sleeping in the kernel +---------------------------------------------------------------- +To do this copy the System.map from the root directory where +the linux kernel was built to the /boot directory on your +linux machine. +Start top +Now type fU +You should see a new field called WCHAN which +tells you where each process is sleeping here is a typical output. + + 6:59pm up 41 min, 1 user, load average: 0.00, 0.00, 0.00 +28 processes: 27 sleeping, 1 running, 0 zombie, 0 stopped +CPU states: 0.0% user, 0.1% system, 0.0% nice, 99.8% idle +Mem: 254900K av, 45976K used, 208924K free, 0K shrd, 28636K buff +Swap: 0K av, 0K used, 0K free 8620K cached + + PID USER PRI NI SIZE RSS SHARE WCHAN STAT LIB %CPU %MEM TIME COMMAND + 750 root 12 0 848 848 700 do_select S 0 0.1 0.3 0:00 in.telnetd + 767 root 16 0 1140 1140 964 R 0 0.1 0.4 0:00 top + 1 root 8 0 212 212 180 do_select S 0 0.0 0.0 0:00 init + 2 root 9 0 0 0 0 down_inte SW 0 0.0 0.0 0:00 kmcheck + +The time command +---------------- +Another related command is the time command which gives you an indication +of where a process is spending the majority of its time. +e.g. +time ping -c 5 nc +outputs +real 0m4.054s +user 0m0.010s +sys 0m0.010s + +Debugging under VM +================== + +Notes +----- +Addresses & values in the VM debugger are always hex never decimal +Address ranges are of the format - or . +e.g. The address range 0x2000 to 0x3000 can be described described as +2000-3000 or 2000.1000 + +The VM Debugger is case insensitive. + +VM's strengths are usually other debuggers weaknesses you can get at any resource +no matter how sensitive e.g. memory managment resources,change address translation +in the PSW. For kernel hacking you will reap dividends if you get good at it. + +The VM Debugger displays operators but not operands, probably because some +of it was written when memory was expensive & the programmer was probably proud that +it fitted into 2k of memory & the programmers & didn't want to shock hardcore VM'ers by +changing the interface :-), also the debugger displays useful information on the same line & +the author of the code probably felt that it was a good idea not to go over +the 80 columns on the screen. + +As some of you are probably in a panic now this isn't as unintuitive as it may seem +as the 390 instructions are easy to decode mentally & you can make a good guess at a lot +of them as all the operands are nibble ( half byte aligned ) & if you have an objdump listing +also it is quite easy to follow, if you don't have an objdump listing keep a copy of +the s/390 Reference Summary & look at between pages 2 & 7 or alternatively the +s/390 principles of operation. +e.g. even I can guess that +0001AFF8' LR 180F CC 0 +is a ( load register ) lr r0,r15 + +Also it is very easy to tell the length of a 390 instruction from the 2 most significant +bits in the instruction ( not that this info is really useful except if you are trying to +make sense of a hexdump of code ). +Here is a table +Bits Instruction Length +------------------------------------------ +00 2 Bytes +01 4 Bytes +10 4 Bytes +11 6 Bytes + + + + +The debugger also displays other useful info on the same line such as the +addresses being operated on destination addresses of branches & condition codes. +e.g. +00019736' AHI A7DAFF0E CC 1 +000198BA' BRC A7840004 -> 000198C2' CC 0 +000198CE' STM 900EF068 >> 0FA95E78 CC 2 + + + +Useful VM debugger commands +--------------------------- + +I suppose I'd better mention this before I start +to list the current active traces do +Q TR +there can be a maximum of 255 of these per set +( more about trace sets later ). +To stop traces issue a +TR END. +To delete a particular breakpoint issue +TR DEL + +The PA1 key drops to CP mode so you can issue debugger commands, +Doing alt c (on my 3270 console at least ) clears the screen. +hitting b comes back to the running operating system +from cp mode ( in our case linux ). +It is typically useful to add shortcuts to your profile.exec file +if you have one ( this is roughly equivalent to autoexec.bat in DOS ). +file here are a few from mine. +/* this gives me command history on issuing f12 */ +set pf12 retrieve +/* this continues */ +set pf8 imm b +/* goes to trace set a */ +set pf1 imm tr goto a +/* goes to trace set b */ +set pf2 imm tr goto b +/* goes to trace set c */ +set pf3 imm tr goto c + + + +Instruction Tracing +------------------- +Setting a simple breakpoint +TR I PSWA
+To debug a particular function try +TR I R +TR I on its own will single step. +TR I DATA will trace for particular mnemonics +e.g. +TR I DATA 4D R 0197BC.4000 +will trace for BAS'es ( opcode 4D ) in the range 0197BC.4000 +if you were inclined you could add traces for all branch instructions & +suffix them with the run prefix so you would have a backtrace on screen +when a program crashes. +TR BR will trace branches into or out of an address. +e.g. +TR BR INTO 0 is often quite useful if a program is getting awkward & deciding +to branch to 0 & crashing as this will stop at the address before in jumps to 0. +TR I R
RUN cmd d g +single steps a range of addresses but stays running & +displays the gprs on each step. + + + +Displaying & modifying Registers +-------------------------------- +D G will display all the gprs +Adding a extra G to all the commands is neccessary to access the full 64 bit +content in VM on z/Architecture obviously this isn't required for access registers +as these are still 32 bit. +e.g. DGG instead of DG +D X will display all the control registers +D AR will display all the access registers +D AR4-7 will display access registers 4 to 7 +CPU ALL D G will display the GRPS of all CPUS in the configuration +D PSW will display the current PSW +st PSW 2000 will put the value 2000 into the PSW & +cause crash your machine. +D PREFIX displays the prefix offset + + +Displaying Memory +----------------- +To display memory mapped using the current PSW's mapping try +D +To make VM display a message each time it hits a particular address & continue try +D I will disassemble/display a range of instructions. +ST addr 32 bit word will store a 32 bit aligned address +D T will display the EBCDIC in an address ( if you are that way inclined ) +D R will display real addresses ( without DAT ) but with prefixing. +There are other complex options to display if you need to get at say home space +but are in primary space the easiest thing to do is to temporarily +modify the PSW to the other addressing mode, display the stuff & then +restore it. + + + +Hints +----- +If you want to issue a debugger command without halting your virtual machine with the +PA1 key try prefixing the command with #CP e.g. +#cp tr i pswa 2000 +also suffixing most debugger commands with RUN will cause them not +to stop just display the mnemonic at the current instruction on the console. +If you have several breakpoints you want to put into your program & +you get fed up of cross referencing with System.map +you can do the following trick for several symbols. +grep do_signal System.map +which emits the following among other things +0001f4e0 T do_signal +now you can do + +TR I PSWA 0001f4e0 cmd msg * do_signal +This sends a message to your own console each time do_signal is entered. +( As an aside I wrote a perl script once which automatically generated a REXX +script with breakpoints on every kernel procedure, this isn't a good idea +because there are thousands of these routines & VM can only set 255 breakpoints +at a time so you nearly had to spend as long pruning the file down as you would +entering the msg's by hand ),however, the trick might be useful for a single object file. +On linux'es 3270 emulator x3270 there is a very useful option under the file ment +Save Screens In File this is very good of keeping a copy of traces. + +From CMS help will give you online help on a particular command. +e.g. +HELP DISPLAY + +Also CP has a file called profile.exec which automatically gets called +on startup of CMS ( like autoexec.bat ), keeping on a DOS analogy session +CP has a feature similar to doskey, it may be useful for you to +use profile.exec to define some keystrokes. +e.g. +SET PF9 IMM B +This does a single step in VM on pressing F8. +SET PF10 ^ +This sets up the ^ key. +which can be used for ^c (ctrl-c),^z (ctrl-z) which can't be typed directly into some 3270 consoles. +SET PF11 ^- +This types the starting keystrokes for a sysrq see SysRq below. +SET PF12 RETRIEVE +This retrieves command history on pressing F12. + + +Sometimes in VM the display is set up to scroll automatically this +can be very annoying if there are messages you wish to look at +to stop this do +TERM MORE 255 255 +This will nearly stop automatic screen updates, however it will +cause a denial of service if lots of messages go to the 3270 console, +so it would be foolish to use this as the default on a production machine. + + +Tracing particular processes +---------------------------- +The kernels text segment is intentionally at an address in memory that it will +very seldom collide with text segments of user programs ( thanks Martin ), +this simplifies debugging the kernel. +However it is quite common for user processes to have addresses which collide +this can make debugging a particular process under VM painful under normal +circumstances as the process may change when doing a +TR I R
. +Thankfully after reading VM's online help I figured out how to debug +I particular process. + +Your first problem is to find the STD ( segment table designation ) +of the program you wish to debug. +There are several ways you can do this here are a few +1) objdump --syms | grep main +To get the address of main in the program. +tr i pswa
+Start the program, if VM drops to CP on what looks like the entry +point of the main function this is most likely the process you wish to debug. +Now do a D X13 or D XG13 on z/Architecture. +On 31 bit the STD is bits 1-19 ( the STO segment table origin ) +& 25-31 ( the STL segment table length ) of CR13. +now type +TR I R STD 0.7fffffff +e.g. +TR I R STD 8F32E1FF 0.7fffffff +Another very useful variation is +TR STORE INTO STD
+for finding out when a particular variable changes. + +An alternative way of finding the STD of a currently running process +is to do the following, ( this method is more complex but +could be quite convient if you aren't updating the kernel much & +so your kernel structures will stay constant for a reasonable period of +time ). + +grep task /proc//status +from this you should see something like +task: 0f160000 ksp: 0f161de8 pt_regs: 0f161f68 +This now gives you a pointer to the task structure. +Now make CC:="s390-gcc -g" kernel/sched.s +To get the task_struct stabinfo. +( task_struct is defined in include/linux/sched.h ). +Now we want to look at +task->active_mm->pgd +on my machine the active_mm in the task structure stab is +active_mm:(4,12),672,32 +its offset is 672/8=84=0x54 +the pgd member in the mm_struct stab is +pgd:(4,6)=*(29,5),96,32 +so its offset is 96/8=12=0xc + +so we'll +hexdump -s 0xf160054 /dev/mem | more +i.e. task_struct+active_mm offset +to look at the active_mm member +f160054 0fee cc60 0019 e334 0000 0000 0000 0011 +hexdump -s 0x0feecc6c /dev/mem | more +i.e. active_mm+pgd offset +feecc6c 0f2c 0000 0000 0001 0000 0001 0000 0010 +we get something like +now do +TR I R STD 0.7fffffff +i.e. the 0x7f is added because the pgd only +gives the page table origin & we need to set the low bits +to the maximum possible segment table length. +TR I R STD 0f2c007f 0.7fffffff +on z/Architecture you'll probably need to do +TR I R STD 0.ffffffffffffffff +to set the TableType to 0x1 & the Table length to 3. + + + +Tracing Program Exceptions +-------------------------- +If you get a crash which says something like +illegal operation or specification exception followed by a register dump +You can restart linux & trace these using the tr prog trace option. + + + +The most common ones you will normally be tracing for is +1=operation exception +2=privileged operation exception +4=protection exception +5=addressing exception +6=specification exception +10=segment translation exception +11=page translation exception + +The full list of these is on page 22 of the current s/390 Reference Summary. +e.g. +tr prog 10 will trace segment translation exceptions. +tr prog on its own will trace all program interruption codes. + +Trace Sets +---------- +On starting VM you are initially in the INITIAL trace set. +You can do a Q TR to verify this. +If you have a complex tracing situation where you wish to wait for instance +till a driver is open before you start tracing IO, but know in your +heart that you are going to have to make several runs through the code till you +have a clue whats going on. + +What you can do is +TR I PSWA +hit b to continue till breakpoint +reach the breakpoint +now do your +TR GOTO B +TR IO 7c08-7c09 inst int run +or whatever the IO channels you wish to trace are & hit b + +To got back to the initial trace set do +TR GOTO INITIAL +& the TR I PSWA will be the only active breakpoint again. + + +Tracing linux syscalls under VM +------------------------------- +Syscalls are implemented on Linux for S390 by the Supervisor call instruction (SVC) there 256 +possibilities of these as the instruction is made up of a 0xA opcode & the second byte being +the syscall number. They are traced using the simple command. +TR SVC +the syscalls are defined in linux/include/asm-s390/unistd.h +e.g. to trace all file opens just do +TR SVC 5 ( as this is the syscall number of open ) + + +SMP Specific commands +--------------------- +To find out how many cpus you have +Q CPUS displays all the CPU's available to your virtual machine +To find the cpu that the current cpu VM debugger commands are being directed at do +Q CPU to change the current cpu cpu VM debugger commands are being directed at do +CPU + +On a SMP guest issue a command to all CPUs try prefixing the command with cpu all. +To issue a command to a particular cpu try cpu e.g. +CPU 01 TR I R 2000.3000 +If you are running on a guest with several cpus & you have a IO related problem +& cannot follow the flow of code but you know it isnt smp related. +from the bash prompt issue +shutdown -h now or halt. +do a Q CPUS to find out how many cpus you have +detach each one of them from cp except cpu 0 +by issueing a +DETACH CPU 01-(number of cpus in configuration) +& boot linux again. +TR SIGP will trace inter processor signal processor instructions. +DEFINE CPU 01-(number in configuration) +will get your guests cpus back. + + +Help for displaying ascii textstrings +------------------------------------- +On the very latest VM Nucleus'es VM can now display ascii +( thanks Neale for the hint ) by doing +D TX. +e.g. +D TX0.100 + +Alternatively +============= +Under older VM debuggers ( I love EBDIC too ) you can use this little program I wrote which +will convert a command line of hex digits to ascii text which can be compiled under linux & +you can copy the hex digits from your x3270 terminal to your xterm if you are debugging +from a linuxbox. + +This is quite useful when looking at a parameter passed in as a text string +under VM ( unless you are good at decoding ASCII in your head ). + +e.g. consider tracing an open syscall +TR SVC 5 +We have stopped at a breakpoint +000151B0' SVC 0A05 -> 0001909A' CC 0 + +D 20.8 to check the SVC old psw in the prefix area & see was it from userspace +( for the layout of the prefix area consult P18 of the s/390 390 Reference Summary +if you have it available ). +V00000020 070C2000 800151B2 +The problem state bit wasn't set & it's also too early in the boot sequence +for it to be a userspace SVC if it was we would have to temporarily switch the +psw to user space addressing so we could get at the first parameter of the open in +gpr2. +Next do a +D G2 +GPR 2 = 00014CB4 +Now display what gpr2 is pointing to +D 00014CB4.20 +V00014CB4 2F646576 2F636F6E 736F6C65 00001BF5 +V00014CC4 FC00014C B4001001 E0001000 B8070707 +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 +outputs +Decoded Hex:=/ d e v / c o n s o l e 0x00 +We were opening the console device, + +You can compile the code below yourself for practice :-), +/* + * hex2ascii.c + * a useful little tool for converting a hexadecimal command line to ascii + * + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation. + */ +#include + +int main(int argc,char *argv[]) +{ + int cnt1,cnt2,len,toggle=0; + int startcnt=1; + unsigned char c,hex; + + if(argc>1&&(strcmp(argv[1],"-a")==0)) + startcnt=2; + printf("Decoded Hex:="); + for(cnt1=startcnt;cnt1='0'&&c<='9') + c=c-'0'; + if(c>='A'&&c<='F') + c=c-'A'+10; + if(c>='a'&&c<='F') + c=c-'a'+10; + switch(toggle) + { + case 0: + hex=c<<4; + toggle=1; + break; + case 1: + hex+=c; + if(hex<32||hex>127) + { + if(startcnt==1) + printf("0x%02X ",(int)hex); + else + printf("."); + } + else + { + printf("%c",hex); + if(startcnt==1) + printf(" "); + } + toggle=0; + break; + } + } + } + printf("\n"); +} + + + + +Stack tracing under VM +---------------------- +A basic backtrace +----------------- + +Here are the tricks I use 9 out of 10 times it works pretty well, + +When your backchain reaches a dead end +-------------------------------------- +This can happen when an exception happens in the kernel & the kernel is entered twice +if you reach the NULL pointer at the end of the back chain you should be +able to sniff further back if you follow the following tricks. +1) A kernel address should be easy to recognise since it is in +primary space & the problem state bit isn't set & also +The Hi bit of the address is set. +2) Another backchain should also be easy to recognise since it is an +address pointing to another address approximately 100 bytes or 0x70 hex +behind the current stackpointer. + + +Here is some practice. +boot the kernel & hit PA1 at some random time +d g to display the gprs, this should display something like +GPR 0 = 00000001 00156018 0014359C 00000000 +GPR 4 = 00000001 001B8888 000003E0 00000000 +GPR 8 = 00100080 00100084 00000000 000FE000 +GPR 12 = 00010400 8001B2DC 8001B36A 000FFED8 +Note that GPR14 is a return address but as we are real men we are going to +trace the stack. +display 0x40 bytes after the stack pointer. + +V000FFED8 000FFF38 8001B838 80014C8E 000FFF38 +V000FFEE8 00000000 00000000 000003E0 00000000 +V000FFEF8 00100080 00100084 00000000 000FE000 +V000FFF08 00010400 8001B2DC 8001B36A 000FFED8 + + +Ah now look at whats in sp+56 (sp+0x38) this is 8001B36A our saved r14 if +you look above at our stackframe & also agrees with GPR14. + +now backchain +d 000FFF38.40 +we now are taking the contents of SP to get our first backchain. + +V000FFF38 000FFFA0 00000000 00014995 00147094 +V000FFF48 00147090 001470A0 000003E0 00000000 +V000FFF58 00100080 00100084 00000000 001BF1D0 +V000FFF68 00010400 800149BA 80014CA6 000FFF38 + +This displays a 2nd return address of 80014CA6 + +now do d 000FFFA0.40 for our 3rd backchain + +V000FFFA0 04B52002 0001107F 00000000 00000000 +V000FFFB0 00000000 00000000 FF000000 0001107F +V000FFFC0 00000000 00000000 00000000 00000000 +V000FFFD0 00010400 80010802 8001085A 000FFFA0 + + +our 3rd return address is 8001085A + +as the 04B52002 looks suspiciously like rubbish it is fair to assume that the kernel entry routines +for the sake of optimisation dont set up a backchain. + +now look at System.map to see if the addresses make any sense. + +grep -i 0001b3 System.map +outputs among other things +0001b304 T cpu_idle +so 8001B36A +is cpu_idle+0x66 ( quiet the cpu is asleep, don't wake it ) + + +grep -i 00014 System.map +produces among other things +00014a78 T start_kernel +so 0014CA6 is start_kernel+some hex number I can't add in my head. + +grep -i 00108 System.map +this produces +00010800 T _stext +so 8001085A is _stext+0x5a + +Congrats you've done your first backchain. + + + +s/390 & z/Architecture IO Overview +================================== + +I am not going to give a course in 390 IO architecture as this would take me quite a +while & I'm no expert. Instead I'll give a 390 IO architecture summary for Dummies if you have +the s/390 principles of operation available read this instead. If nothing else you may find a few +useful keywords in here & be able to use them on a web search engine like altavista to find +more useful information. + +Unlike other bus architectures modern 390 systems do their IO using mostly +fibre optics & devices such as tapes & disks can be shared between several mainframes, +also S390 can support upto 65536 devices while a high end PC based system might be choking +with around 64. Here is some of the common IO terminology + +Subchannel: +This is the logical number most IO commands use to talk to an IO device there can be upto +0x10000 (65536) of these in a configuration typically there is a few hundred. Under VM +for simplicity they are allocated contiguously, however on the native hardware they are not +they typically stay consistent between boots provided no new hardware is inserted or removed. +Under Linux for 390 we use these as IRQ's & also when issuing an IO command (CLEAR SUBCHANNEL, +HALT SUBCHANNEL,MODIFY SUBCHANNEL,RESUME SUBCHANNEL,START SUBCHANNEL,STORE SUBCHANNEL & +TEST SUBCHANNEL ) we use this as the ID of the device we wish to talk to, the most +important of these instructions are START SUBCHANNEL ( to start IO ), TEST SUBCHANNEL ( to check +whether the IO completed successfully ), & HALT SUBCHANNEL ( to kill IO ), a subchannel +can have up to 8 channel paths to a device this offers redunancy if one is not available. + + +Device Number: +This number remains static & Is closely tied to the hardware, there are 65536 of these +also they are made up of a CHPID ( Channel Path ID, the most significant 8 bits ) +& another lsb 8 bits. These remain static even if more devices are inserted or removed +from the hardware, there is a 1 to 1 mapping between Subchannels & Device Numbers provided +devices arent inserted or removed. + +Channel Control Words: +CCWS are linked lists of instructions initially pointed to by an operation request block (ORB), +which is initially given to Start Subchannel (SSCH) command along with the subchannel number +for the IO subsystem to process while the CPU continues executing normal code. +These come in two flavours, Format 0 ( 24 bit for backward ) +compatibility & Format 1 ( 31 bit ). These are typically used to issue read & write +( & many other instructions ) they consist of a length field & an absolute address field. +For each IO typically get 1 or 2 interrupts one for channel end ( primary status ) when the +channel is idle & the second for device end ( secondary status ) sometimes you get both +concurrently, you check how the IO went on by issueing a TEST SUBCHANNEL at each interrupt, +from which you receive an Interruption response block (IRB). If you get channel & device end +status in the IRB without channel checks etc. your IO probably went okay. If you didn't you +probably need a doctorto examine the IRB & extended status word etc. +If an error occurs more sophistocated control units have a facitity known as +concurrent sense this means that if an error occurs Extended sense information will +be presented in the Extended status word in the IRB if not you have to issue a +subsequent SENSE CCW command after the test subchannel. + + +TPI( Test pending interrupt) can also be used for polled IO but in multitasking multiprocessor +systems it isn't recommended except for checking special cases ( i.e. non looping checks for +pending IO etc. ). + +Store Subchannel & Modify Subchannel can be used to examine & modify operating characteristics +of a subchannel ( e.g. channel paths ). + +Other IO related Terms: +Sysplex: S390's Clustering Technology +QDIO: S390's new high speed IO architecture to support devices such as gigabit ethernet, +this architecture is also designed to be forward compatible with up & coming 64 bit machines. + + +General Concepts + +Input Output Processors (IOP's) are responsible for communicating between +the mainframe CPU's & the channel & relieve the mainframe CPU's from the +burden of communicating with IO devices directly, this allows the CPU's to +concentrate on data processing. + +IOP's can use one or more links ( known as channel paths ) to talk to each +IO device. It first checks for path availability & chooses an available one, +then starts ( & sometimes terminates IO ). +There are two types of channel path ESCON & the Paralell IO interface. + +IO devices are attached to control units, control units provide the +logic to interface the channel paths & channel path IO protocols to +the IO devices, they can be integrated with the devices or housed separately +& often talk to several similar devices ( typical examples would be raid +controllers or a control unit which connects to 1000 3270 terminals ). + + + +---------------------------------------------------------------+ + | +-----+ +-----+ +-----+ +-----+ +----------+ +----------+ | + | | CPU | | CPU | | CPU | | CPU | | Main | | Expanded | | + | | | | | | | | | | Memory | | Storage | | + | +-----+ +-----+ +-----+ +-----+ +----------+ +----------+ | + |---------------------------------------------------------------+ + | IOP | IOP | IOP | + |--------------------------------------------------------------- + | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | + ---------------------------------------------------------------- + || || + || Bus & Tag Channel Path || ESCON + || ====================== || Channel + || || || || Path + +----------+ +----------+ +----------+ + | | | | | | + | CU | | CU | | CU | + | | | | | | + +----------+ +----------+ +----------+ + | | | | | ++----------+ +----------+ +----------+ +----------+ +----------+ +|I/O Device| |I/O Device| |I/O Device| |I/O Device| |I/O Device| ++----------+ +----------+ +----------+ +----------+ +----------+ + CPU = Central Processing Unit + C = Channel + IOP = IP Processor + CU = Control Unit + +The 390 IO systems come in 2 flavours the current 390 machines support both + +The Older 360 & 370 Interface,sometimes called the paralell I/O interface, +sometimes called Bus-and Tag & sometimes Original Equipment Manufacturers +Interface (OEMI). + +This byte wide paralell channel path/bus has parity & data on the "Bus" cable +& control lines on the "Tag" cable. These can operate in byte multiplex mode for +sharing between several slow devices or burst mode & monopolize the channel for the +whole burst. Upto 256 devices can be addressed on one of these cables. These cables are +about one inch in diameter. The maximum unextended length supported by these cables is +125 Meters but this can be extended up to 2km with a fibre optic channel extended +such as a 3044. The maximum burst speed supported is 4.5 megabytes per second however +some really old processors support only transfer rates of 3.0, 2.0 & 1.0 MB/sec. +One of these paths can be daisy chained to up to 8 control units. + + +ESCON if fibre optic it is also called FICON +Was introduced by IBM in 1990. Has 2 fibre optic cables & uses either leds or lasers +for communication at a signaling rate of upto 200 megabits/sec. As 10bits are transferred +for every 8 bits info this drops to 160 megabits/sec & to 18.6 Megabytes/sec once +control info & CRC are added. ESCON only operates in burst mode. + +ESCONs typical max cable length is 3km for the led version & 20km for the laser version +known as XDF ( extended distance facility ). This can be further extended by using an +ESCON director which triples the above mentioned ranges. Unlike Bus & Tag as ESCON is +serial it uses a packet switching architecture the standard Bus & Tag control protocol +is however present within the packets. Upto 256 devices can be attached to each control +unit that uses one of these interfaces. + +Common 390 Devices include: +Network adapters typically OSA2,3172's,2116's & OSA-E gigabit ethernet adapters, +Consoles 3270 & 3215 ( a teletype emulated under linux for a line mode console ). +DASD's direct access storage devices ( otherwise known as hard disks ). +Tape Drives. +CTC ( Channel to Channel Adapters ), +ESCON or Paralell Cables used as a very high speed serial link +between 2 machines. We use 2 cables under linux to do a bi-directional serial link. + + +Debugging IO on s/390 & z/Architecture under VM +=============================================== + +Now we are ready to go on with IO tracing commands under VM + +A few self explanatory queries: +Q OSA +Q CTC +Q DISK ( This command is CMS specific ) +Q DASD + + + + + + +Q OSA on my machine returns +OSA 7C08 ON OSA 7C08 SUBCHANNEL = 0000 +OSA 7C09 ON OSA 7C09 SUBCHANNEL = 0001 +OSA 7C14 ON OSA 7C14 SUBCHANNEL = 0002 +OSA 7C15 ON OSA 7C15 SUBCHANNEL = 0003 + +If you have a guest with certain priviliges you may be able to see devices +which don't belong to you to avoid this do add the option V. +e.g. +Q V OSA + +Now using the device numbers returned by this command we will +Trace the io starting up on the first device 7c08 & 7c09 +In our simplest case we can trace the +start subchannels +like TR SSCH 7C08-7C09 +or the halt subchannels +or TR HSCH 7C08-7C09 +MSCH's ,STSCH's I think you can guess the rest + +Ingo's favourite trick is tracing all the IO's & CCWS & spooling them into the reader of another +VM guest so he can ftp the logfile back to his own machine.I'll do a small bit of this & give you + a look at the output. + +1) Spool stdout to VM reader +SP PRT TO (another vm guest ) or * for the local vm guest +2) Fill the reader with the trace +TR IO 7c08-7c09 INST INT CCW PRT RUN +3) Start up linux +i 00c +4) Finish the trace +TR END +5) close the reader +C PRT +6) list reader contents +RDRLIST +7) copy it to linux4's minidisk +RECEIVE / LOG TXT A1 ( replace +8) +filel & press F11 to look at it +You should see someting like. + +00020942' SSCH B2334000 0048813C CC 0 SCH 0000 DEV 7C08 + CPA 000FFDF0 PARM 00E2C9C4 KEY 0 FPI C0 LPM 80 + CCW 000FFDF0 E4200100 00487FE8 0000 E4240100 ........ + IDAL 43D8AFE8 + IDAL 0FB76000 +00020B0A' I/O DEV 7C08 -> 000197BC' SCH 0000 PARM 00E2C9C4 +00021628' TSCH B2354000 >> 00488164 CC 0 SCH 0000 DEV 7C08 + CCWA 000FFDF8 DEV STS 0C SCH STS 00 CNT 00EC + KEY 0 FPI C0 CC 0 CTLS 4007 +00022238' STSCH B2344000 >> 00488108 CC 0 SCH 0000 DEV 7C08 + +If you don't like messing up your readed ( because you possibly booted from it ) +you can alternatively spool it to another readers guest. + + +Other common VM device related commands +--------------------------------------------- +These commands are listed only because they have +been of use to me in the past & may be of use to +you too. For more complete info on each of the commands +use type HELP from CMS. +detaching devices +DET +ATT +attach a device to guest * for your own guest +READY cause VM to issue a fake interrupt. + +The VARY command is normally only available to VM administrators. +VARY ON PATH TO +VARY OFF PATH FROM +This is used to switch on or off channel paths to devices. + +Q CHPID +This displays state of devices using this channel path +D SCHIB +This displays the subchannel information SCHIB block for the device. +this I believe is also only available to administrators. +DEFINE CTC +defines a virtual CTC channel to channel connection +2 need to be defined on each guest for the CTC driver to use. +COUPLE devno userid remote devno +Joins a local virtual device to a remote virtual device +( commonly used for the CTC driver ). + +Building a VM ramdisk under CMS which linux can use +def vfb- +blocksize is commonly 4096 for linux. +Formatting it +format (blksize + +Sharing a disk between multiple guests +LINK userid devno1 devno2 mode password + + + +GDB on S390 +=========== +N.B. if compiling for debugging gdb works better without optimisation +( see Compiling programs for debugging ) + +invocation +---------- +gdb + +Online help +----------- +help: gives help on commands +e.g. +help +help display +Note gdb's online help is very good use it. + + +Assembly +-------- +info registers: displays registers other than floating point. +info all-registers: displays floating points as well. +disassemble: dissassembles +e.g. +disassemble without parameters will disassemble the current function +disassemble $pc $pc+10 + +Viewing & modifying variables +----------------------------- +print or p: displays variable or register +e.g. p/x $sp will display the stack pointer + +display: prints variable or register each time program stops +e.g. +display/x $pc will display the program counter +display argc + +undisplay : undo's display's + +info breakpoints: shows all current breakpoints + +info stack: shows stack back trace ( if this dosent work too well, I'll show you the +stacktrace by hand below ). + +info locals: displays local variables. + +info args: display current procedure arguments. + +set args: will set argc & argv each time the victim program is invoked. + +set =value +set argc=100 +set $pc=0 + + + +Modifying execution +------------------- +step: steps n lines of sourcecode +step steps 1 line. +step 100 steps 100 lines of code. + +next: like step except this will not step into subroutines + +stepi: steps a single machine code instruction. +e.g. stepi 100 + +nexti: steps a single machine code instruction but will not step into subroutines. + +finish: will run until exit of the current routine + +run: (re)starts a program + +cont: continues a program + +quit: exits gdb. + + +breakpoints +------------ + +break +sets a breakpoint +e.g. + +break main + +break *$pc + +break *0x400618 + +heres a really useful one for large programs +rbr +Set a breakpoint for all functions matching REGEXP +e.g. +rbr 390 +will set a breakpoint with all functions with 390 in their name. + +info breakpoints +lists all breakpoints + +delete: delete breakpoint by number or delete them all +e.g. +delete 1 will delete the first breakpoint +delete will delete them all + +watch: This will set a watchpoint ( usually hardware assisted ), +This will watch a variable till it changes +e.g. +watch cnt, will watch the variable cnt till it changes. +As an aside unfortunately gdb's, architecture independent watchpoint code +is inconsistent & not very good, watchpoints usually work but not always. + +info watchpoints: Display currently active watchpoints + +condition: ( another useful one ) +Specify breakpoint number N to break only if COND is true. +Usage is `condition N COND', where N is an integer and COND is an +expression to be evaluated whenever breakpoint N is reached. + + + +User defined functions/macros +----------------------------- +define: ( Note this is very very useful,simple & powerful ) +usage define end + +examples which you should consider putting into .gdbinit in your home directory +define d +stepi +disassemble $pc $pc+10 +end + +define e +nexti +disassemble $pc $pc+10 +end + + +Other hard to classify stuff +---------------------------- +signal n: +sends the victim program a signal. +e.g. signal 3 will send a SIGQUIT. + +info signals: +what gdb does when the victim receives certain signals. + +list: +e.g. +list lists current function source +list 1,10 list first 10 lines of curret file. +list test.c:1,10 + + +directory: +Adds directories to be searched for source if gdb cannot find the source. +(note it is a bit sensititive about slashes ) +e.g. To add the root of the filesystem to the searchpath do +directory // + + +call +This calls a function in the victim program, this is pretty powerful +e.g. +(gdb) call printf("hello world") +outputs: +$1 = 11 + +You might now be thinking that the line above didn't work, something extra had to be done. +(gdb) call fflush(stdout) +hello world$2 = 0 +As an aside the debugger also calls malloc & free under the hood +to make space for the "hello world" string. + + + +hints +----- +1) command completion works just like bash +( if you are a bad typist like me this really helps ) +e.g. hit br & cursor up & down :-). + +2) if you have a debugging problem that takes a few steps to recreate +put the steps into a file called .gdbinit in your current working directory +if you have defined a few extra useful user defined commands put these in +your home directory & they will be read each time gdb is launched. + +A typical .gdbinit file might be. +break main +run +break runtime_exception +cont + + +stack chaining in gdb by hand +----------------------------- +This is done using a the same trick described for VM +p/x (*($sp+56))&0x7fffffff get the first backchain. + +For z/Architecture +Replace 56 with 112 & ignore the &0x7fffffff +in the macros below & do nasty casts to longs like the following +as gdb unfortunately deals with printed arguments as ints which +messes up everything. +i.e. here is a 3rd backchain dereference +p/x *(long *)(***(long ***)$sp+112) + + +this outputs +$5 = 0x528f18 +on my machine. +Now you can use +info symbol (*($sp+56))&0x7fffffff +you might see something like. +rl_getc + 36 in section .text telling you what is located at address 0x528f18 +Now do. +p/x (*(*$sp+56))&0x7fffffff +This outputs +$6 = 0x528ed0 +Now do. +info symbol (*(*$sp+56))&0x7fffffff +rl_read_key + 180 in section .text +now do +p/x (*(**$sp+56))&0x7fffffff +& so on. + +Disassembling instructions without debug info +--------------------------------------------- +gdb typically compains if there is a lack of debugging +symbols in the disassemble command with +"No function contains specified address." to get around +this do +x/xi
+e.g. +x/20xi 0x400730 + + + +Note: Remember gdb has history just like bash you don't need to retype the +whole line just use the up & down arrows. + + + +For more info +------------- +From your linuxbox do +man gdb or info gdb. + +core dumps +---------- +What a core dump ?, +A core dump is a file generated by the kernel ( if allowed ) which contains the registers, +& all active pages of the program which has crashed. +From this file gdb will allow you to look at the registers & stack trace & memory of the +program as if it just crashed on your system, it is usually called core & created in the +current working directory. +This is very useful in that a customer can mail a core dump to a technical support department +& the technical support department can reconstruct what happened. +Provided the have an indentical copy of this program with debugging symbols compiled in & +the source base of this build is available. +In short it is far more useful than something like a crash log could ever hope to be. + +In theory all that is missing to restart a core dumped program is a kernel patch which +will do the following. +1) Make a new kernel task structure +2) Reload all the dumped pages back into the kernels memory managment structures. +3) Do the required clock fixups +4) Get all files & network connections for the process back into an identical state ( really difficult ). +5) A few more difficult things I haven't thought of. + + + +Why have I never seen one ?. +Probably because you haven't used the command +ulimit -c unlimited in bash +to allow core dumps, now do +ulimit -a +to verify that the limit was accepted. + +A sample core dump +To create this I'm going to do +ulimit -c unlimited +gdb +to launch gdb (my victim app. ) now be bad & do the following from another +telnet/xterm session to the same machine +ps -aux | grep gdb +kill -SIGSEGV +or alternatively use killall -SIGSEGV gdb if you have the killall command. +Now look at the core dump. +./gdb ./gdb core +Displays the following +GNU gdb 4.18 +Copyright 1998 Free Software Foundation, Inc. +GDB is free software, covered by the GNU General Public License, and you are +welcome to change it and/or distribute copies of it under certain conditions. +Type "show copying" to see the conditions. +There is absolutely no warranty for GDB. Type "show warranty" for details. +This GDB was configured as "s390-ibm-linux"... +Core was generated by `./gdb'. +Program terminated with signal 11, Segmentation fault. +Reading symbols from /usr/lib/libncurses.so.4...done. +Reading symbols from /lib/libm.so.6...done. +Reading symbols from /lib/libc.so.6...done. +Reading symbols from /lib/ld-linux.so.2...done. +#0 0x40126d1a in read () from /lib/libc.so.6 +Setting up the environment for debugging gdb. +Breakpoint 1 at 0x4dc6f8: file utils.c, line 471. +Breakpoint 2 at 0x4d87a4: file top.c, line 2609. +(top-gdb) info stack +#0 0x40126d1a in read () from /lib/libc.so.6 +#1 0x528f26 in rl_getc (stream=0x7ffffde8) at input.c:402 +#2 0x528ed0 in rl_read_key () at input.c:381 +#3 0x5167e6 in readline_internal_char () at readline.c:454 +#4 0x5168ee in readline_internal_charloop () at readline.c:507 +#5 0x51692c in readline_internal () at readline.c:521 +#6 0x5164fe in readline (prompt=0x7ffff810 "\177ÿøx\177ÿ÷Ø\177ÿøxÀ") + at readline.c:349 +#7 0x4d7a8a in command_line_input (prrompt=0x564420 "(gdb) ", repeat=1, + annotation_suffix=0x4d6b44 "prompt") at top.c:2091 +#8 0x4d6cf0 in command_loop () at top.c:1345 +#9 0x4e25bc in main (argc=1, argv=0x7ffffdf4) at main.c:635 + + +LDD +=== +This is a program which lists the shared libraries which a library needs, +Note you also get the relocations of the shared library text segments which +help when using objdump --source. +e.g. + ldd ./gdb +outputs +libncurses.so.4 => /usr/lib/libncurses.so.4 (0x40018000) +libm.so.6 => /lib/libm.so.6 (0x4005e000) +libc.so.6 => /lib/libc.so.6 (0x40084000) +/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) + + +Debugging shared libraries +========================== +Most programs use shared libraries, however it can be very painful +when you single step instruction into a function like printf for the +first time & you end up in functions like _dl_runtime_resolve this is +the ld.so doing lazy binding, lazy binding is a concept in ELF where +shared library functions are not loaded into memory unless they are +actually used, great for saving memory but a pain to debug. +To get around this either relink the program -static or exit gdb type +export LD_BIND_NOW=true this will stop lazy binding & restart the gdb'ing +the program in question. + + + +Debugging modules +================= +As modules are dynamically loaded into the kernel their address can be +anywhere to get around this use the -m option with insmod to emit a load +map which can be piped into a file if required. + +The proc file system +==================== +What is it ?. +It is a filesystem created by the kernel with files which are created on demand +by the kernel if read, or can be used to modify kernel parameters, +it is a powerful concept. + +e.g. + +cat /proc/sys/net/ipv4/ip_forward +On my machine outputs +0 +telling me ip_forwarding is not on to switch it on I can do +echo 1 > /proc/sys/net/ipv4/ip_forward +cat it again +cat /proc/sys/net/ipv4/ip_forward +On my machine now outputs +1 +IP forwarding is on. +There is a lot of useful info in here best found by going in & having a look around, +so I'll take you through some entries I consider important. + +All the processes running on the machine have there own entry defined by +/proc/ +So lets have a look at the init process +cd /proc/1 + +cat cmdline +emits +init [2] + +cd /proc/1/fd +This contains numerical entries of all the open files, +some of these you can cat e.g. stdout (2) + +cat /proc/29/maps +on my machine emits + +00400000-00478000 r-xp 00000000 5f:00 4103 /bin/bash +00478000-0047e000 rw-p 00077000 5f:00 4103 /bin/bash +0047e000-00492000 rwxp 00000000 00:00 0 +40000000-40015000 r-xp 00000000 5f:00 14382 /lib/ld-2.1.2.so +40015000-40016000 rw-p 00014000 5f:00 14382 /lib/ld-2.1.2.so +40016000-40017000 rwxp 00000000 00:00 0 +40017000-40018000 rw-p 00000000 00:00 0 +40018000-4001b000 r-xp 00000000 5f:00 14435 /lib/libtermcap.so.2.0.8 +4001b000-4001c000 rw-p 00002000 5f:00 14435 /lib/libtermcap.so.2.0.8 +4001c000-4010d000 r-xp 00000000 5f:00 14387 /lib/libc-2.1.2.so +4010d000-40111000 rw-p 000f0000 5f:00 14387 /lib/libc-2.1.2.so +40111000-40114000 rw-p 00000000 00:00 0 +40114000-4011e000 r-xp 00000000 5f:00 14408 /lib/libnss_files-2.1.2.so +4011e000-4011f000 rw-p 00009000 5f:00 14408 /lib/libnss_files-2.1.2.so +7fffd000-80000000 rwxp ffffe000 00:00 0 + + +Showing us the shared libraries init uses where they are in memory +& memory access permissions for each virtual memory area. + +/proc/1/cwd is a softlink to the current working directory. +/proc/1/root is the root of the filesystem for this process. + +/proc/1/mem is the current running processes memory which you +can read & write to like a file. +strace uses this sometimes as it is a bit faster than the +rather inefficent ptrace interface for peeking at DATA. + + +cat status + +Name: init +State: S (sleeping) +Pid: 1 +PPid: 0 +Uid: 0 0 0 0 +Gid: 0 0 0 0 +Groups: +VmSize: 408 kB +VmLck: 0 kB +VmRSS: 208 kB +VmData: 24 kB +VmStk: 8 kB +VmExe: 368 kB +VmLib: 0 kB +SigPnd: 0000000000000000 +SigBlk: 0000000000000000 +SigIgn: 7fffffffd7f0d8fc +SigCgt: 00000000280b2603 +CapInh: 00000000fffffeff +CapPrm: 00000000ffffffff +CapEff: 00000000fffffeff + +User PSW: 070de000 80414146 +task: 004b6000 tss: 004b62d8 ksp: 004b7ca8 pt_regs: 004b7f68 +User GPRS: +00000400 00000000 0000000b 7ffffa90 +00000000 00000000 00000000 0045d9f4 +0045cafc 7ffffa90 7fffff18 0045cb08 +00010400 804039e8 80403af8 7ffff8b0 +User ACRS: +00000000 00000000 00000000 00000000 +00000001 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +00000000 00000000 00000000 00000000 +Kernel BackChain CallChain BackChain CallChain + 004b7ca8 8002bd0c 004b7d18 8002b92c + 004b7db8 8005cd50 004b7e38 8005d12a + 004b7f08 80019114 +Showing among other things memory usage & status of some signals & +the processes'es registers from the kernel task_structure +as well as a backchain which may be useful if a process crashes +in the kernel for some unknown reason. + +Some driver debugging techniques +================================ +debug feature +------------- +Some of our drivers now support a "debug feature" in +/proc/s390dbf see s390dbf.txt in the linux/Documentation directory +for more info. +e.g. +to switch on the lcs "debug feature" +echo 5 > /proc/s390dbf/lcs/level +& then after the error occured. +cat /proc/s390dbf/lcs/sprintf >/logfile +the logfile now contains some information which may help +tech support resolve a problem in the field. + + + +high level debugging network drivers +------------------------------------ +ifconfig is a quite useful command +it gives the current state of network drivers. + +If you suspect your network device driver is dead +one way to check is type +ifconfig +e.g. tr0 +You should see something like +tr0 Link encap:16/4 Mbps Token Ring (New) HWaddr 00:04:AC:20:8E:48 + inet addr:9.164.185.132 Bcast:9.164.191.255 Mask:255.255.224.0 + UP BROADCAST RUNNING MULTICAST MTU:2000 Metric:1 + RX packets:246134 errors:0 dropped:0 overruns:0 frame:0 + TX packets:5 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:100 + +if the device doesn't say up +try +/etc/rc.d/init.d/network start +( this starts the network stack & hopefully calls ifconfig tr0 up ). +ifconfig looks at the output of /proc/net/dev & presents it in a more presentable form +Now ping the device from a machine in the same subnet. +if the RX packets count & TX packets counts don't increment you probably +have problems. +next +cat /proc/net/arp +Do you see any hardware addresses in the cache if not you may have problems. +Next try +ping -c 5 i.e. the Bcast field above in the output of +ifconfig. Do you see any replies from machines other than the local machine +if not you may have problems. also if the TX packets count in ifconfig +hasn't incremented either you have serious problems in your driver +(e.g. the txbusy field of the network device being stuck on ) +or you may have multiple network devices connected. + + +chandev +------- +There is a new device layer for channel devices, some +drivers e.g. lcs are registered with this layer. +If the device uses the channel device layer you'll be +able to find what interupts it uses & the current state +of the device. +See the manpage chandev.8 &type cat /proc/chandev for more info. + + + +Starting points for debugging scripting languages etc. +====================================================== + +bash/sh + +bash -x +e.g. bash -x /usr/bin/bashbug +displays the following lines as it executes them. ++ MACHINE=i586 ++ OS=linux-gnu ++ CC=gcc ++ CFLAGS= -DPROGRAM='bash' -DHOSTTYPE='i586' -DOSTYPE='linux-gnu' -DMACHTYPE='i586-pc-linux-gnu' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./lib -O2 -pipe ++ RELEASE=2.01 ++ PATCHLEVEL=1 ++ RELSTATUS=release ++ MACHTYPE=i586-pc-linux-gnu + +perl -d runs the perlscript in a fully intercative debugger +. +Type 'h' in the debugger for help. + +for debugging java type +jdb another fully interactive gdb style debugger. +& type ? in the debugger for help. + + + +SysRq +===== +This is now supported by linux for s/390 & z/Architecture. +To enable it do compile the kernel with +Kernel Hacking -> Magic SysRq Key Enabled +echo "1" > /proc/sys/kernel/sysrq. +On 390 all commands are prefixed with +^- +e.g. +^-t will show tasks. +^-? or some unknown command will display help. +The sysrq key reading is very picky ( I have to type the keys in an + xterm session & paste them into the x3270 console ) +& it may be wise to predefine the keys as described in the VM hints above + +This is particularly useful for syncing disks unmounting & rebooting +if the machine gets partially hung. + +Read Documentation/sysrq.txt for more info + +References: +=========== +Enterprise Systems Architecture Reference Summary +Enterprise Systems Architecture Principles of Operation +Hartmut Penners s390 stack frame sheet. +IBM Mainframe Channel Attachment a technology brief from a CISCO webpage +Various bits of man & info pages of Linux. +Linux & GDB source. +Various info & man pages. +CMS Help on tracing commands. +Linux for s/390 Elf Application Binary Interface +Linux for z/Series Elf Application Binary Interface ( Both Highly Recommended ) +z/Architecture Principles of Operation SA22-7832-00 +Enterprise Systems Architecture/390 Reference Summary SA22-7209-01 & the +Enterprise Systems Architecture/390 Principles of Operation SA22-7201-05 + +Special Thanks +============== +Special thanks to Neale Ferguson who maintains a much +prettier HTML version of this page at +http://penguinvm.princeton.edu/notes.html#Debug390 + diff --git a/Documentation/s390/TAPE b/Documentation/s390/TAPE new file mode 100644 index 000000000000..6329276b41bf --- /dev/null +++ b/Documentation/s390/TAPE @@ -0,0 +1,122 @@ +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 --git a/Documentation/s390/s390dump.txt b/Documentation/s390/s390dump.txt new file mode 100644 index 000000000000..3c01e089473a --- /dev/null +++ b/Documentation/s390/s390dump.txt @@ -0,0 +1,177 @@ +L/390 stand alone dump tools for Linux 2.2.x + +-----------------------------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. +----------------------------------------------------------------------------- + +1. Overview + +Two stand alone dump tools are provided for generating system memory dumps +on dasd volumes and tapes: dumpeckd.boot and dumptape.boot +respectively. The tools should be installed on the device which will +be used for dumping the system memory; We shall refer to this device as +dump device. A system memory dump can be initiated by the operator +at any time. Normally this should be done after a system crash. +In order to take a dump an ipl of the dump device is required. +This is destructive which means that the actual running Linux Operating +System will be killed. +The ipl process then writes the system memory to the ipl device (either tape +or dasd). + + +2. Build the dump utilities + +The dump tools are compiled with the Kernel: +> make menuconfig +> make dep +> make image + +Then under linux/arch/s390/boot the following two files are built: +- dumpeckd.boot +- dumptape.boot + + + +3. Install the Dump utilities + +Make sure that there is enough space (memory-size + 1MB) on your dump device +to hold all your system memory. + +3.1 Install DASD dump utility + +You have to have an unused dasd partition for dumping. The dump utility is +installed there and dumps are written afterwards to this dasd. This is how +to install the dump tool on dasd: + + 1. Format dasd with 4K blocksize: + > dasdfmt -f /dev/dasdx -b 4096 + + 2. Copy dump utility to dasd (e.g. /dev/dasdx) by issuing the following + two commands: + > dd if=dumpeckd.boot of=/dev/dasdx + > sync + +3.2 Install tape dump utility + + 1. Insert empty dump cartridge into your tapedevice (e.g. /dev/ntibm0). + 2. Ensure the tape is rewound (e.g mt -f /dev/ntibm0 rewind) + 3. Copy dump utility to tape: + > dd if=/boot/dumptape.boot of=/dev/ntibm0 bs=4096 + + + +4. Taking the dump + +The operator has to do the following tasks in order to take a dump: + - Ensure that tape is rewound (if using tapedump.boot) + - Stop all cpus (only under VM) + - Store status on ipl cpu + - IPL the dump tool on ipl cpu + +The dump process can take several minutes, depending on the devicetype you are +using and the amount of your system memory. +After the dump has completed, the ipl CPU should go into disabled wait. + +The following PSW indicates that the dump has been taken successfully: + +PSW: 000A0000 00000000 + +Any other disabled wait PSW indicates an error. + +On VM (a three processor machine in this example) this could look like the +following: + + #cp cpu all stop + #cp store status + #cp I 193 (if 193 is the dump device) + 01: The virtual machine is placed in CP mode due to a SIGP stop from CPU 00. + 02: The virtual machine is placed in CP mode due to a SIGP stop from CPU 00. + "CP entered; disabled wait PSW 000A0000 00000000" + #cp I 192 (the linux dasd) + +When initiating the dump process e.g. from a service element check the +"Store Status" checkbox of the ipl panel. + +For detailed information on the actual steps to be performed please consult the +appropriate manual of your processor. + + + +5. Copying the dump to a filesystem + +After the dump has been created by one of the dump utilities the dump should +normally be copied over to a filesystem in order to send it e.g. +to a service organization for analysis. + +To copy the dump to a filesystem, you can use command dd as illustrated below: + +5.1 Dasd: + +Copying the dump from dasd to a filesystem is only necessary, if the dump +cannot be analyzed locally on the system, but must be e.g. sent to the service +organization. + +- Copy the dump from raw dasd to filesystem (e.g. Dump dasd is /dev/dasdx): + + > dd if=/dev/dasdx1 of=/DUMPDIR/mydump bs=1M \ + count= + + or if you want to compress the dump: + + > dd if=/dev/dasdx1 bs=1M count= | bzip2 \ + > /DUMPDIR/dump.bz2 + +5.2 Tape: + + 1. Rewind the tape: + + > mt -f /dev/ntibm0 rewind + + 2. Skip first file on tape (first file is the dump utility itself): + + > mt -f /dev/ntibm0 fsf + + 3. Copy dump from tape to filesystem: + + > dd if=/dev/ntibm0 of=/DUMPDIR/mydump bs=32k + + or if you want to compress the dump: + + > dd if=/dev/ntibm0 bs=32k | bzip2 > /DUMPDIR/mydump.bz2 + +Note: mt is a tape utility which has be be installed first. You can use +any other available tape tool to skip the first file. + +6. Analyzing the dumps + +The dump analysis tool 'lcrash' can be used to analyze the generated dumps. +The required version of the lkcdutilities is 3.1.2 +(see ftp://oss.sgi.com/projects/lkcd/download/3.1.2/lkcdutils/) + +lcrash gets three input files: +- System.map of the crashed kernel +- the dump +- Kerntypes file of the crashed kernel with Kernel type information + There is provided a patch for the Kerntypes file under + http://oss.software.ibm.com/developerworks/opensource/linux390 + +Call lcrash: +> lcrash System.map mydump Kerntypes + +Note: If you used dumpeckd.boot to take the dump it is possible to specify +the /dev/dasdx1 dump device directly as input file to lcrash instead of +a copy in the filesystem: +> lcrash System.map /dev/dasdx1 Kerntypes + +7. Supported Devices + +The dump tools should work with the following devices: + +- ECKD Dasds: + + 3380 + + 3390 + +- Tape units: + + 3480 + + 3490 diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index c12592e642eb..b4483a802b08 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -73,6 +73,11 @@ Current status: the port to use for the HotSync transfer. The "Generic" port can be used for other device communication, such as a PPP link. + If after pressing the sync button, nothing shows up in the system log, + try resetting the Visor, first a hot reset, and then a cold reset if + necessary. Some Visors need this before they can talk to the USB port + properly. + There is a webpage and mailing lists for this portion of the driver at: http://usbvisor.sourceforge.net/ @@ -103,22 +108,22 @@ Current status: Keyspan USA-series Serial Adapters - Single and Dual port adapters - driver uses Keyspan supplied + Single, Dual and Quad port adapters - driver uses Keyspan supplied firmware and is being developed with their support. - Driver isn't as far advanced as Keyspan PDA driver mentioned above. - Current status: - Things that work: - Firmware upload for USA-18X, USA-28, USA-28X, USA-19 and USA-19W - Simple character I/O fixed at 9600 baud on USA-19 only - - Things that don't: - Everything else. (for now...) + The USA-18X, USA-28X, USA-19, USA-19W and USA-49W are supported and + have been pretty throughly tested at various baud rates with 8-N-1 + character settings. Other character lengths and parity setups are + presently untested. + + The USA-28 isn't yet supported though doing so should be pretty + straightforward. Contact the maintainer if you require this + functionality. + + More information is available at: + http://www.linuxcare.com.au/hugh/keyspan.html - Big Things on the todo list: - Driver is in infancy, much functionality remains to be added - FTDI Single Port Serial Driver @@ -139,8 +144,8 @@ Digi AccelePort Driver (plus a parallel port) and 4 port USB serial converters. The driver does NOT yet support the Digi AccelePort USB 8. - This driver works under SMP with the usb-uhci driver. It does not work - under SMP with the uhci driver. + This driver works under SMP with the usb-uhci driver. It does not + work under SMP with the uhci driver. The driver is generally working, though we still have a few more ioctls to implement and final testing and debugging to do. The paralled port @@ -157,6 +162,7 @@ Digi AccelePort Driver Belkin USB Serial Adapter F5U103 Single port DB-9/PS-2 serial adapter from Belkin with firmware by eTEK Labs. + The Peracom single port serial adapter also works with this driver. Current status: The following have been tested and work: @@ -190,10 +196,55 @@ Empeg empeg-car Mark I/II Driver (empeg.c) This is an experimental driver to provide connectivity support for the client synchronization tools for an Empeg empeg-car mp3 player. + Tips: + + * Don't forget to create the device nodes for ttyUSB{0,1,2,...} + * modprobe empeg (modprobe is your friend) + * emptool --usb /dev/ttyUSB0 (or whatever you named your device node) + The driver is still pretty new, so some testing 'in the wild' would be helpful. :) +MCT USB Single Port Serial Adapter U232 + + This driver is for the MCT USB-RS232 Converter (25 pin, Model No. + U232-P25) from Magic Control Technology Corp. (there is also a 9 pin + Model No. U232-P9). More information about this device can be found + at the manufacture's web-site: http://www.mct.com.tw. + + The driver is generally working, though it still needs some more + testing. It is derived from the Belkin USB Serial Adapter F5U103 + driver and its TODO list is valid for this driver as well. + + This driver has also been found to work for other products, which have + the same Vendor ID but different Product IDs. Sitecom's U232-P25 + serial converter uses Product ID 0x230 and Vendor ID 0x711 and works with + this driver. Also, D-Link's DU-H3SP USB BAY also works with this driver. + + +Inside Out Networks Edgeport Driver + + This driver supports all devices made by Inside Out Networks, specifically + the following models: + Edgeport/4 + Rapidport/4 + Edgeport/4t + Edgeport/2 + Edgeport/4i + Edgeport/2i + Edgeport/421 + Edgeport/21 + Edgeport/8 + Edgeport/8 Dual + Edgeport/2D8 + Edgeport/4D8 + Edgeport/8i + Edgeport/2 DIN + Edgeport/4 DIN + Edgeport/16 Dual + + Generic Serial driver If your device is not one of the above listed devices, compatible with @@ -205,7 +256,7 @@ Generic Serial driver To enable the generic driver to recognize your device, build the driver as a module and load it by the following invocation: - insmod usb-serial vendor=0x#### product=0x#### + insmod usbserial vendor=0x#### product=0x#### where the #### is replaced with the hex representation of your device's vendor id and product id. diff --git a/MAINTAINERS b/MAINTAINERS index dc4baf984238..0baaa585b47d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -533,17 +533,13 @@ L: linux-irda@list.uit.no W: http://www.cs.uit.no/linux-irda/ S: Maintained -ISDN SUBSYSTEM (general) -P: Fritz Elfert -M: fritz@isdn4linux.de -L: isdn4linux@listserv.isdn4linux.de -W: www.isdn4linux.de -S: Maintained - -ISDN SUBSYSTEM (HiSax) +ISDN SUBSYSTEM P: Karsten Keil -M: keil@isdn4linux.de +M: kkeil@suse.de +P: Kai Germaschewski +M: kai.germaschewski@gmx.de L: isdn4linux@listserv.isdn4linux.de +W: http://www.isdn4linux.de S: Maintained ISDN SUBSYSTEM (Eicon active card driver) diff --git a/Makefile b/Makefile index 4a289c8b8aa2..8d14fc6605e0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 20 -EXTRAVERSION = pre8 +EXTRAVERSION = pre9 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -200,6 +200,10 @@ endif ifeq ($(CONFIG_USB),y) DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o +else + ifdef CONFIG_INPUT_ADBHID + DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o + endif endif ifeq ($(CONFIG_I2O),y) diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index ebeedeabaad3..a637ad4f4d13 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -199,9 +199,9 @@ static void name##_IO_APIC_irq(unsigned int irq) \ /* * We disable IO-APIC IRQs by setting their 'destination CPU mask' to * zero. Trick by Ramesh Nalluri. + * Not anymore. This causes problems on some IO-APIC's, notably AMD 760MP's + * So we do it a more 2.4 kind of way now which should be safer -jerdfelt */ -DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync(entry->apic))/* destination = 0x00 */ -DO_ACTION( enable, 1, |= 0xff000000, ) /* destination = 0xff */ DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ DO_ACTION( unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ @@ -611,8 +611,8 @@ void __init setup_IO_APIC_irqs(void) entry.delivery_mode = dest_LowestPrio; entry.dest_mode = 1; /* logical delivery */ - entry.mask = 0; /* enable IRQ */ - entry.dest.logical.logical_dest = 0; /* but no route */ + entry.mask = 1; /* disable IRQ */ + entry.dest.logical.logical_dest = 0xff; idx = find_irq_entry(apic,pin,mp_INT); if (idx == -1) { @@ -1059,13 +1059,10 @@ static inline void self_IPI(unsigned int irq) static void enable_edge_ioapic_irq(unsigned int irq) { self_IPI(irq); - enable_IO_APIC_irq(irq); + unmask_IO_APIC_irq(irq); } -static void disable_edge_ioapic_irq(unsigned int irq) -{ - disable_IO_APIC_irq(irq); -} +static void disable_edge_ioapic_irq(unsigned int irq) { /* nothing */ } /* * Starting up a edge-triggered IO-APIC interrupt is @@ -1281,7 +1278,7 @@ static inline void check_timer(void) pin1 = find_timer_pin(mp_INT); pin2 = find_timer_pin(mp_ExtINT); - enable_IO_APIC_irq(0); + unmask_IO_APIC_irq(0); if (!timer_irq_works()) { if (pin1 != -1) diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 7f88609894e9..452d3efc0f42 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -179,12 +179,10 @@ source drivers/usb/Config.in mainmenu_option next_comment comment 'Mac device drivers' -if [ "$CONFIG_INPUT_KEYBDEV" = "y" -o "$CONFIG_INPUT_MOUSEDEV" = "y" ]; then - bool 'Use input layer for ADB keyboard and mouse' CONFIG_INPUT_ADBHID -fi +bool 'Use input layer for ADB keyboard and mouse' CONFIG_INPUT_ADBHID if [ "$CONFIG_INPUT_ADBHID" = "y" ]; then - define_bool CONFIG_INPUT_KEYBDEV $CONFIG_VT - define_bool CONFIG_INPUT_MOUSEDEV y + define_bool CONFIG_INPUT_KEYBDEV $CONFIG_INPUT_ADBHID $CONFIG_VT + define_bool CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT_ADBHID define_bool CONFIG_MAC_HID y bool ' Support for ADB raw keycodes' CONFIG_MAC_ADBKEYCODES bool ' Support for mouse button 2+3 emulation' CONFIG_MAC_EMUMOUSEBTN diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 693522a6c22e..225dbddf2750 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -24,15 +24,27 @@ include $(TOPDIR)/Rules.make $(OBJCOPY) -O binary $< $@ image: $(CONFIGURE) $(TOPDIR)/vmlinux \ - iplfba.boot ipleckd.boot ipldump.boot + iplfba.boot ipleckd.boot dumptape.boot dumpeckd.boot $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image $(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aU] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map +dumptape.lnk: dumptape.o + $(LD) -Ttext 0x2000 -o $@ $< + +dumpeckd.boot: dumpeckd.lnk + $(OBJCOPY) -O binary $< dumpeckd.boot2 + dd if=dumpeckd.boot2 > dumpeckd.boot + dd if=dumpeckd.boot2 >> dumpeckd.boot + rm dumpeckd.boot2 + +dumpeckd.lnk: dumpeckd.o + $(LD) -Ttext 0x2000 -o $@ $< + listing: ../../../vmlinux $(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing dep: clean: - rm -f image listing iplfba.boot ipleckd.boot ipldump.boot + rm -f image listing iplfba.boot ipleckd.boot dumptape.boot dumpeckd.boot *.lnk *.o diff --git a/arch/s390/boot/common.S b/arch/s390/boot/common.S new file mode 100644 index 000000000000..5882fc7550a3 --- /dev/null +++ b/arch/s390/boot/common.S @@ -0,0 +1,298 @@ +# +# Common code for the different second stage boot loaders +# Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), +# + +# some definitions from kernel land +__LC_IPLDEV = 0xC6C +IPL_DEVICE = 0x10404 +INITRD_START = 0x1040C +INITRD_SIZE = 0x10414 +COMMAND_LINE = 0x10480 + +# error codes shown as address in disabled wait psw + +EENABLE_DEV = 0x00000100 # enable device failed +EDISABLE_DEV = 0x00000101 # disable device failed +ESSCH = 0x00000102 # start subchannel failed + + .macro stage2_start +# +# Function entry point at 0x2000 is called with C linkage +# %r2-%r3: load descriptor +# %r3 : device subchannel id +# %r4 : load address +# + basr %r1,0 + b _load_blocklist-.(%r1) + .align 8 + +# +# Program execution of the second stage boot loader starts at 0x1008 +# + .globl _start +_start: stm %r0,%r15,0x180 # store all registers + basr %r13,0 +0: l %r15,3f-0b(%r13) # load end of stack address + l %r11,0xb8 # load ipl device subchannel id + lr %r2,%r11 + bas %r14,_enable_device-0b(%r13) + lm %r2,%r3,STAGE2_DESC # get block with load descriptors + lr %r4,%r11 + l %r5,4f-0b(%r13) + la %r12,0(%r5) # FIXME: provide the correct offset + bas %r14,_load_direct-0b(%r13) +1: lm %r2,%r5,0(%r12) # load 16 bytes = one load descriptor + cl %r4,6f-0b(%r13) # check type range + bh 2f + sll %r4,2 + l %r1,5f-0b(%r4,%r13) # function pointer according to type + lr %r4,%r11 # pass subchannel id + bas %r14,0(%r1) +2: la %r12,16(%r12) # next load descriptor + b 1b-0b(%r13) +3: .long 0x10000-96 +4: .long 0x8000 +5: .long _load_kernel # type 0: kernel image + .long _load_parmfile # type 1: parameter file + .long _load_ramdisk # type 2: initial ramdisk + .long _jump_target # type 3: jump to target +6: .long 3 # highest index in list + +_load_kernel: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,1f-0b(%r13) # create stack frame + lr %r12,%r4 + bas %r14,_load_blocklist-0b(%r13) + st %r12,__LC_IPLDEV # old ipl device storage location + l %r6,2f-0b(%r13) # load address of IPL_DEVICE + st %r12,0(%r6) # new ipl device storage location + l %r6,3f-0b(%r13) # load address of INITRD_START + xc 0(4,%r6),0(%r6) # clear INITRD_START + l %r6,4f-0b(%r13) # load address of INITRD_SIZE + xc 0(4,%r6),0(%r6) # clear INITRD_SIZE + lm %r6,%r15,120(%r15) + br %r14 +1: .long 96 +2: .long IPL_DEVICE +3: .long INITRD_START +4: .long INITRD_SIZE + +_load_parmfile: # load parameter file to 0x10480 + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,1f-0b(%r13) # create stack frame + bas %r14,_load_blocklist-0b(%r13) + l %r1,12(%r12) + l %r2,2f-0b(%r13) # load address of kernel command line + mvc 0x0(256,%r2),0(%r1) # move command line to 0x10480 + mvc 0x100(256,%r2),0x100(%r1) + mvc 0x200(256,%r2),0x200(%r1) + mvc 0x300(127,%r2),0x300(%r1) + xc 0x37f(1,%r2),0x37f(%r2) + lm %r6,%r15,120(%r15) + br %r14 +1: .long 96 +2: .long COMMAND_LINE + +_load_ramdisk: # load initial ramdisk + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,1f-0b(%r13) # create stack frame + lr %r12,%r5 # save load address + bas %r14,_load_blocklist-0b(%r13) + l %r1,2f-0b(%r13) # ramdisk start storage location + st %r12,0(%r1) # store start of ramdisk + slr %r2,%r12 + l %r1,3f-0b(%r13) # ramdisk size storage location + st %r2,0(%r1) # store size of ramdisk + lm %r6,%r15,120(%r15) + br %r14 +1: .long 96 +2: .long INITRD_START +3: .long INITRD_SIZE + +_jump_target: + basr %r1,0 +0: lr %r2,%r11 + lr %r14,%r5 # make branch target return address + b _disable_device-0b(%r1) + + .endm + +# +# The load descriptor is 16 bytes in length and contains 3 entries: +# offset 0 : a blocklist descriptor (fba or eckd) +# offset 8 : a type +# offset 12: an address +# The meaning of the address and the validity of the blocklst +# depends on the type. +# type = 0 : kernel image, blocklist valid, address = load address +# type = 1 : parameter line, blocklist valid, address = load address +# type = 2 : initial ramdisk, blocklist valid, address = load address +# type = 3 : jump command, blocklist invalid, address = branch address +# + .macro blocklist_traverser +# parameter +# %r2+%r3: blocklist head descriptor +# %r4 : device subchannel id +# %r5 : load address +_load_blocklist: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,4f-0b(%r13) # create stack frame +1: lr %r12,%r4 # save device subchannel id + lr %r11,%r5 # save load address + lr %r8,%r2 # save descriptor + lr %r9,%r3 + bas %r14,_extract_length-0b(%r13) # get length from descriptor + lr %r10,%r2 # save returned length + lr %r2,%r8 # reload descriptor to %r2/%r3 + lr %r3,%r9 + lr %r4,%r12 # reload device id to %r4 + l %r5,6f-0b(%r13) # get memory area for indirect block + bas %r14,_load_direct-0b(%r13) # load indirect block + lr %r5,%r11 # restore load address + lr %r9,%r10 # (length / 8 - 1) = # direct descr. + srl %r9,3 + bctr %r9,0 + l %r8,6f-0b(%r13) +2: clc 0(8,%r8),5f-0b(%r13) # test block descriptor + be 3f-0b(%r13) # descriptor == 0 -> done + lm %r2,%r3,0(%r8) # pass block descriptor + lr %r4,%r12 # pass device subchannel id + bas %r14,_load_direct-0b(%r13) # load indirect block + lr %r5,%r2 # move updated load address + la %r8,8(%r8) # next descriptor + bct %r9,2b-0b(%r13) + lm %r2,%r3,0(%r8) # load continuation descriptor + lr %r4,%r12 # move device id for next round + clc 0(8,%r8),5f-0b(%r13) # test continuation descriptor + bne 1b-0b(%r13) # != 0 -> load next indirect block +3: lr %r2,%r5 # return updated load address + lm %r6,%r15,120(%r15) + br %r14 +4: .long 96 +5: .long 0,0 +6: .long 0x8200 # memory address for indirect blocks + + .endm + + .macro device_fn +# +# Enable I/O on the ipl device. +# %r2 : device subchannel id +# +_enable_device: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,1f-0b(%r13) + lr %r1,%r2 + l %r2,4f-0b(%r13) # set panik code early + stsch 2f-0b(%r13) + oi 2f-0b+5(%r13),0x84 # enable ssch and multipath mode + msch 2f-0b(%r13) + bnz _panik-0b(%r13) # subchannel busy or in error ? + lctl %c6,%c6,3f-0b(%r13) # enable all interrupt classes + lm %r6,%r15,120(%r15) + br %r14 +1: .long 96 + .align 8 +2: .fill 64,1,0 +3: .long 0xff000000 # CR6 initial value +4: .long EENABLE_DEV + +# +# Disable I/O on the ipl device. +# %r2 : device subchannel id +# +_disable_device: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,1f-0b(%r13) + lr %r1,%r2 + l %r2,3f-0b(%r13) # set panik code early + lctl %c6,%c6,2f-0b(%r13) # disable all interrupt classes + stsch 2f-0b(%r13) + ni 2f-0b+5(%r13),0x7B # disable ssch and multipath mode + msch 2f-0b(%r13) + bnz _panik-0b(%r13) # subchannel busy or in error ? + lm %r6,%r15,120(%r15) + br %r14 +1: .long 96 + .align 8 +2: .long 0x00000000 # CR6 (all interrupts classes disabled) +3: .long EDISABLE_DEV + .endm + + .macro io_subroutines +# +# Start I/O +# %r2 : device subchannel id +# %r3 : address of orb +# %r4 : address of irb +# %r5 : retry count +# +_ssch: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,6f-0b(%r13) # create stack frame + lr %r12,%r2 # save subchannel id + lr %r11,%r3 # save orb + lr %r10,%r4 # save irb + lr %r9,%r5 # save retry count +1: lr %r1,%r12 + ssch 0(%r11) # go + bnz 4f-0b(%r13) # houston, we have a problem +2: lr %r2,%r12 # call _wait4de with subchannel id + lr %r3,%r10 # and irb address as parameters + bas %r14,_wait4de-0b(%r13) # wait until DE or error + tm 9(%r10),0xff # test channel status + bnz 4f-0b(%r13) + tm 8(%r10),0xf3 # test device status + bz 5f-0b(%r13) + bct %r9,1b-0b(%r13) # something went wrong, retry. +4: l %r2,7f-0b(%r13) + bas %r4,_panik-0b(%r13) # won't return +5: lm %r6,%r15,120(%r15) + br %r14 +6: .long 96 +7: .long ESSCH + +# +# Wait for interrupt subroutine +# %r2 : device subchannel id +# %r3 : address of irb +# +_wait4de: + lr %r1,%r2 + basr %r4,0 +0: mvc 0x78(8),5f-0b(%r4) # set i/o new psw +1: lpsw 4f-0b(%r4) +2: c %r1,0xb8 # compare subchannel id + bne 1b-0b(%r4) # unequal -> continue waiting + tsch 0(%r3) + tm 9(%r3),0xff # test channel status + bnz 3f-0b(%r4) + tm 8(%r3),0xf3 # got something unusual ? + bnz 3f-0b(%r4) + tm 8(%r3),0x04 # got device end ? + bz 1b-0b(%r4) # still busy -> continue waiting +3: br %r14 + .align 8 +4: .long 0x020a0000,0x80000000+1b +5: .long 0x00080000,0x80000000+2b # io new psw + +# +# Panik routine. Loads a disabled wait psw +# %r2 : panik code +# +_panik: + basr %r1,0 +0: st %r2,1f-0b+4(%r1) # store code in address part of psw + lpsw 1f-0b(%r1) + .align 8 +1: .long 0x000a0000,0x00000000 + .endm diff --git a/arch/s390/boot/dumpcommon.S b/arch/s390/boot/dumpcommon.S new file mode 100644 index 000000000000..bbc51d255561 --- /dev/null +++ b/arch/s390/boot/dumpcommon.S @@ -0,0 +1,220 @@ +/* + * dumpcommon.S + * + * Common routines for dump records + * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Michael Holzheu + * + * Uses extern functions: + * - _dump_mem (device dependent function to write dump) + * + * Functions: + * - _take_dump + */ + + +.include "common.S" + +/* General defines */ +#define PAGE_SIZE 0x1000 /* 4096 */ +#define HEADER_SIZE 0x1000 /* 4096 */ +#define END_MARKER_SIZE 0x10 +#define DUMP_END_MARKER 0x44554d50,0x5f454e44 /* DUMP_END */ +#define IPL_SC 0xb8 /* Address of ipl subchannel */ +#define S390_DUMP_MAGIC 0xa8190173,0x618f23fd /* magic number */ +#define ARCH_S390 0x1 /* arch flag for s390 */ + +/* Error codes */ +#define OK 0x00000000 /* Dump completed successfully */ +#define EMEM 0x00000001 /* Device too small for dump */ +#define EDEV_INVAL 0x00000002 /* Device not supported */ + +################################################################################ +# Take the dump +################################################################################ + +.macro dump_common_fn: + +_take_dump: + stm %r6,%r15,24(%r15) + basr %r13,0 +.Lbase: s %r15,.Lc96-.Lbase(%r13) # create stack frame + bas %r14,_store_status-.Lbase(%r13) # store status + bas %r14,_count_mem-.Lbase(%r13) # count memory + l %r14,.Ldump_mem-.Lbase(%r13) + basr %r14,%r14 # dump memory + la %r2,OK + bas %r14,_panik-.Lbase(%r13) # everything ok: stop now + +################################################################################ +# Find out memory size: +# When accessing a page which is not there, we get a program check +################################################################################ + +_count_mem: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + slr %r9,%r9 # base register for zero page + mvc 104(8,%r9),.Lcount_mem_psw-0b(%r13) # setup program check new psw + slr %r10,%r10 + la %r11,1 + sll %r11,20 # 1 MB +.Lloop0: + l %r12,0(%r10) # test page + ar %r10,%r11 # add 1M + bnm .Lloop0-0b(%r13) # r10 < 0x80000000 -> loop +.Lchkmem0: + n %r10,.L4malign0-0b(%r13) # align to multiples of 4M + st %r10,.Ldh_mem_size+4-0b(%r13) # store memory size + st %r10,.Ldh_mem_end+4-0b(%r13) # store memory end + srl %r10,12 # calculate page count (/ 4096) + st %r10,.Ldh_num_pages-0b(%r13) # store page count + mvc 88(32,%r9),.Lnew_psws-0b(%r13) # restore disabled wait new psws + lm %r6,%r15,120(%r15) + br %r14 +.Lcount_mem_psw: .long 0x00080000,0x80000000 + .Lchkmem0 +.Lnew_psws: +.long 0x000a0000,0x00000058 # external new psw +.long 0x000a0000,0x00000060 # svc new psw +.long 0x000a0000,0x00000068 # program check new psw +.long 0x000a0000,0x00000070 # machine check new psw +.L4malign0: +.long 0xffc00000 + +################################################################################ +# store status of all cpus in their lowcores +################################################################################ + + +_store_status: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) + la %r7,0x0 # base register for 0 page + + ######## move lowcore info (assume user has made store ######## + ######## status) to prefix-page ######## + + lr %r2,%r7 # zero page (source) + bas %r14,_copy_lowcore-0b(%r13) + + ######## stop all cpus and store status in prefix pages ######## + +.Lstore_all: + la %r8,0 # first cpu + stap .Lcurrent_cpu+2-0b(%r13) # store current cpu address + +.Lstatus_loop: + cl %r8,.Lcurrent_cpu-0b(%r13) # is ipl cpu ? + be .Lnext_cpu-0b(%r13) # if yes get next cpu +.Lstop_loop: + sigp %r9,%r8,0x5 # stop cpu + bc 8,.Lcpu_stopped-0b(%r13) # accepted + bc 4,.Lnext_cpu-0b(%r13) # status stored in register: + # next cpu + bc 2,.Lstop_loop-0b(%r13) # busy: try again + bc 1,.Lnext_cpu-0b(%r13) # not op: next cpu +.Lcpu_stopped: + lr %r9,%r7 + sigp %r9,%r8,0xe # store status of cpu + bc 8,.Lcpu_stored-0b(%r13) # accepted + bc 4,.Lnext_cpu-0b(%r13) # status stored: next cpu + bc 2,.Lcpu_stopped-0b(%r13) # busy: try again + bc 1,.Lnext_cpu-0b(%r13) # not op: next cpu +.Lcpu_stored: + lr %r2,%r7 # zero page (source) + bas %r14,_copy_lowcore-0b(%r13) +.Lnext_cpu: + la %r8,1(%r8) # next cpu (r8 +=1) + cl %r8,.Llast_cpu-0b(%r13) # is last possible cpu ? + bl .Lstatus_loop-0b(%r13) # jump if not last cpu +.Lstore_status_exit: + lm %r6,%r15,120(%r15) + br %r14 # return to caller +.Lcurrent_cpu: + .long 0x0 +.Llast_cpu: + .long 0x0000ffff + +############1################################################################### +# copy lowcore to prefix page +# r2: address of source lowcore (input by caller) +################################################################################ + +_copy_lowcore: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) + + l %r3,0x108(%r2) # get prefix page from lowcore + + ###### check if lowcore address looks valid ###### + + cl %r3,.Llinux_start-0b(%r13) # looks valid ? + bl .Lcpy_locore_exit-0b(%r13) # if < linux-start addr + l %r6,.Lpage_align-0b(%r13) # check page alignment + nr %r3,%r6 + cl %r3,0x108(%r2) + bnz .Lcpy_locore_exit-0b(%r13) # if not page aligned + + ###### copy lowcore ###### + + # |-----------------------------------------------------------| + # | Decimal | Length | Data | + # | Address | in Bytes | | + # |_________|___________|_____________________________________| + # | 212 | 4 | Extended save area address | + # | 216 | 8 | CPU timer | + # | 224 | 8 | Clock comparator | + # | 256 | 8 | Current PSW | + # | 264 | 4 | Prefix register | + # | 288 | 64 | Access registers 0 through 15 | + # | 352 | 32 | Floating-point registers 0 through 6| + # | 384 | 64 | General registers 0 through 15 | + # | 448 | 64 | Control registers 0 through 15 | + # |_________|___________|_____________________________________| + + mvc 212(20,%r3),212(%r2) + mvc 256(12,%r3),256(%r2) + mvc 288(224,%r3),288(%r2) + +.Lcpy_locore_exit: + lm %r6,%r15,120(%r15) + br %r14 # return to caller +.Lpage_align: + .long -4096 + +.align 4 +.Ldump_mem: .long _dump_mem # address of function + +.Llinux_start: + .long 0x10000 +.Lc96: .long 96 # for creating stackframes +.align 8 +# +# The Dump header +# +.Ldh_dumpheader: +.Ldh_magic_number:.long S390_DUMP_MAGIC +.Ldh_version: .long 0x00000001 +.Ldh_header_size: .long HEADER_SIZE +.Ldh_dump_level: .long 0x00000004 # DUMP_ALL +.Ldh_page_size: .long PAGE_SIZE +.Ldh_mem_size: .long 0x00000000,0x00000000 +.Ldh_mem_start: .long 0x00000000,0x00000000 +.Ldh_mem_end: .long 0x00000000,0x00000000 +.Ldh_num_pages: .long 0x00000000 +.Ldh_pad: .long 0x00000000 +.Ldh_time: .long 0x00000000,0x00000000 +.Ldh_cpuid: .long 0x00000000,0x00000000 +.Ldh_arch: .long ARCH_S390 +.Ldh_free: .long 0x00000000 +# +# Dump End Marker +# +.align 8 +.Ld_endmarker: .long DUMP_END_MARKER +.Ld_end_time: .long 0x00000000,0x00000000 + +.endm diff --git a/arch/s390/boot/dumpeckd.S b/arch/s390/boot/dumpeckd.S new file mode 100644 index 000000000000..3cbbdff3ae9e --- /dev/null +++ b/arch/s390/boot/dumpeckd.S @@ -0,0 +1,548 @@ +/* + * Dump boot loader for 3380/3390 DASDs + * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Michael Holzheu + * Holger Smolinski + * + * Uses extern functions: + * - _panik + * - _enable_device + * - _take_dump + * + * Functions: + * - _dump_mem + */ + +#include "dumpcommon.S" + +/* General defines */ + +#define IPL_BS 0x1000 +#define BLOCKS_PER_WRITE 64 /* makes 256K with 4K blksize */ + +#ifdef ZIPL + +################################################################################ +# Function entry point at 0x2000 (not used for dump) is called with C linkage +# %r2-%r3: load descriptor +# %r3 : device subchannel id +# %r4 : load address +################################################################################ + + basr %r1,0 + b _not_implemented-.(%r1) + .align 8 + +#else + +#define DUMP_TOOL_START 0x2000 + +################################################################################ +# one psw and two ccws: +# Re-Read enough of bootsector to start +################################################################################ + +.psw: .long 0x00080000,0x80000000+_start +.ccw1: .long 0x06000000 + DUMP_TOOL_START,0x00000000 + IPL_BS +.ccw2: .long 0x00000000,0x00000000 + +#endif /* ZIPL */ + +################################################################################ +# Program execution of the second stage boot loader starts at 0x2008 +################################################################################ + + .globl _start +_start: basr %r13,0 +0: l %r15,1f-0b(%r13) # load end of stack address + l %r11,IPL_SC # load ipl device subchannel id + lr %r2,%r11 + l %r14,.Lenable_device-0b(%r13) + basr %r14,%r14 + bas %r14,_get_device_characteristics-0b(%r13) + l %r5,.Ldev_end_rec-0b(%r13) # endblock (first block = nr 1) + st %r5,.Ldev_bpt-0b(%r13) + l %r5,.Ldev_blk_size-0b(%r13) # get blocksize + stcm %r5,3,.Lwrccw+2-0b(%r13) # store blocksize into wr template + stcm %r5,3,.Llodata+14-0b(%r13) # store blocksize into lodata + l %r14,.Ltake_dump-0b(%r13) + basr %r14,%r14 +1: .long 0x10000-96 # end of stack + +#ifdef ZIPL + +################################################################################ +# Get the device characteristics: +# +# Get device characteristics from zipl parameters (at 0x218) +# The following parameter format is expected 2x8 byte: +# +# Dump Start: CCHH RBBN +# Dump End: CCHH RBBX +# +# - CC: Start/End Cylinder Number +# - HH: Start/End Head Number +# - R : Start/End Record Number +# - BB: Blocksize +# - N : Number of Heads of DASD +# - X : Unused +# +# Cylinder, Heads are counted starting with 0. +# Records are counted starting with 1. +# We assume that the End Record Number is at track boundary. +# This allows us to determine the number of Blocks Per Track. +################################################################################ + +_get_device_characteristics: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + la %r12,0 # load base register + mvc .Ldev_start_cyl-0b+2(2,%r13),0x218(%r12) + mvc .Ldev_start_hd-0b+2(2,%r13),0x21a(%r12) + mvc .Ldev_start_rec-0b+3(1,%r13),0x21c(%r12) + mvc .Ldev_blk_size-0b+2(2,%r13),0x21d(%r12) + mvc .Ldev_nr_hds-0b+3(1,%r13),0x21f(%r12) + mvc .Ldev_end_cyl-0b+2(2,%r13),0x220(%r12) + mvc .Ldev_end_hd-0b+2(2,%r13),0x222(%r12) + mvc .Ldev_end_rec-0b+3(1,%r13),0x224(%r12) + lm %r6,%r15,120(%r15) + br %r14 + +#else + +################################################################################ +# Get the device characteristics: +# +# The following is fix: +# - blocksize = 4K +# - start cylinder = 0 +# - start head = 0 +# - start record = 4 +# - end cylinder = unlimited +# - end head = nr of heads +# - end record = blocks per track +# +# The following is read from the device characteristics +# +# - model 3380/90 ==> blocks per track +# - nr of heads +# +################################################################################ + +_get_device_characteristics: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + la %r6,.Lrdcccw-0b(%r13) + st %r6,.Lorb+8-0b(%r13) # store cp-address to orb + l %r2,IPL_SC + la %r3,.Lorb-0b(%r13) + la %r4,.Lirb-0b(%r13) + la %r5,2 + bas %r14,_ssch-0b(%r13) # start I/O: Read device characteristic + + # find out blocks per track (bpt) + + la %r6,9 + clc .Lrdcdata+3-0b(2,%r13),.L9345-0b(%r13) + be 1f-0b(%r13) + la %r6,10 + clc .Lrdcdata+3-0b(2,%r13),.L3380-0b(%r13) + be 1f-0b(%r13) + la %r6,12 + clc .Lrdcdata+3-0b(2,%r13),.L3390-0b(%r13) + be 1f-0b(%r13) + + # not supported device panik + + la %r2,EDEV_INVAL + l %r14,.Lpanik-0b(%r13) + basr %r14,%r14 + +1: + # store dev characteristic + + st %r6,.Ldev_end_rec-0b(%r13) + + slr %r6,%r6 + icm %r6,3,.Lrdcdata+14-0b(%r13) + st %r6,.Ldev_end_hd-0b(%r13) + st %r6,.Ldev_nr_hds-0b(%r13) + + la %r6,0 + st %r6,.Ldev_start_cyl-0b(%r13) + la %r6,0 + st %r6,.Ldev_start_hd-0b(%r13) + la %r6,4 + st %r6,.Ldev_start_rec-0b(%r13) + l %r6,.Lblk_size-0b(%r13) + st %r6,.Ldev_blk_size-0b(%r13) + l %r6,.Lend_cyl-0b(%r13) + st %r6,.Ldev_end_cyl-0b(%r13) + + lm %r6,%r15,120(%r15) + br %r14 +.L3390: + .word 0x3390 +.L9345: + .word 0x9345 +.L3380: + .word 0x3380 +.Lend_cyl: + .long 0x0000ffff +.Lblk_size: + .long 0x1000 +.align 8 + +.Lrdcdata: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lrdcccw: + .long 0x64000040,0x00000000+.Lrdcdata # read device characteristics + +#endif /* ZIPL */ + +################################################################################ +# Dump memory +################################################################################ + +_dump_mem: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + + # calculate start and end block + + la %r6,0 + l %r7,.Ldev_start_cyl-0b(%r13) + la %r8,0 + l %r9,.Ldev_start_hd-0b(%r13) + l %r10,.Ldev_start_rec-0b(%r13) + l %r11,.Ldev_nr_hds-0b(%r13) + l %r12,.Ldev_bpt-0b(%r13) # = blocks per track + + # calculate start block + ####################### + + # cyl * nr_hds * blkptr + + mr %r6,%r11 + mr %r6,%r12 + + # hd * recs + + mr %r8,%r12 + + # start block = cyl * nr_hds * bptr + hd * bptr + rec + + ar %r7,%r9 + ar %r7,%r10 + + bctr %r7,0 # decrement, we start counting + # beginning with block 0 + + st %r7,.Ldev_start_blk-0b(%r13) + + + + # calculate end block + ####################### + + l %r7,.Ldev_end_cyl-0b(%r13) + l %r9,.Ldev_end_hd-0b(%r13) + + # cyl * nr_hds * blkptr + + mr %r6,%r11 + mr %r6,%r12 + + # hd * recs + + mr %r8,%r12 + + # end block = cyl * nr_hds * bptr + hd * bptr + rec + + ar %r7,%r9 + ar %r7,%r12 + + bctr %r7,0 # decrement, we start counting + # beginning with block 0 + + st %r7,.Ldev_end_blk-0b(%r13) + + # calculate bytes per write (blksize * blwr) + + l %r11,.Ldev_blk_size-0b(%r13) + mh %r11,.Lblocks_per_write-0b(%r13) + st %r11,.Lbytes_per_write-0b(%r13) + +# write header + +.Lheaders: # write dump headers + stck .Ldh_time-0b(%r13) # store time + stidp .Ldh_cpuid-0b(%r13) # store cpu id + + l %r11,.Ldev_start_blk-0b(%r13) # start block + + lr %r2,%r11 + la %r3,.Ldh_dumpheader-0b(%r13) # address of dump header + + l %r4,.Lheader_size-0b(%r13) + srda %r4,32 # shift ==> 64 bit number + l %r6,.Ldev_blk_size-0b(%r13) # get blocksize + + dr %r4,%r6 # nr of blocks for header = + # HEADER_SIZE / BLOCKSIZE = r5 + lr %r4,%r5 + lr %r12,%r5 # save nr of blocks + bas %r14,_writeblock-0b(%r13) # write block to disk + ar %r11,%r12 # update block counter + +.Lmemory: # write memory + +# write memory + + la %r10,0 # start at address 0 + +.Lmloop: + la %r4,BLOCKS_PER_WRITE # write so many blocks at a time + lr %r2,%r11 # restore r2 + lr %r3,%r10 # restore r3 + bas %r14,_writeblock-0b(%r13) # write block to disk + l %r2,.Lbytes_per_write-0b(%r13) + ar %r10,%r2 # update data address + ah %r11,.Lblocks_per_write-0b(%r13) # skip to next block + l %r3,.Ldh_mem_size+4-0b(%r13) # get memsize + clr %r10,%r3 # enough ? + bl .Lmloop-0b(%r13) # branch if r10 < r3 + +# write end marker + +.lendmarker: # terminate dump file + la %r4,1 # write endmaker with one block + stck .Ld_end_time-0b(%r13) # store end time + lr %r2,%r11 # restore r2 + la %r3,.Ld_endmarker-0b(%r13) # address of dump end marker + la %r4,1 # write 4k at a time + bas %r14,_writeblock-0b(%r13) # write block to disk + + lm %r6,%r15,120(%r15) + br %r14 # return to caller +.Lbytes_per_write: .long 0x00000000 +.Lheader_size: .long HEADER_SIZE +.Lblocks_per_write: .word BLOCKS_PER_WRITE + +################################################################################ +# This function writes a block number given in r2 to disk +# r2: number of first block to write ( input by caller ) +# We start counting with Block Nr 0 !!! +# r3: address to write data from ( input by caller ) +# r4: number of blocks to write ( input by caller ) +################################################################################ + +_writeblock: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + + # check if blocks are within range: + + lr %r11,%r2 + ar %r11,%r4 # End block + l %r12,.Ldev_end_blk-0b(%r13) + clr %r11,%r12 # End block < dev_end_blk ? + bl 1f-0b(%r13) # no + la %r2,EMEM # if yes panik + l %r14,.Lpanik-0b(%r13) + basr %r14,%r14 + +1: la %r12,.Ldeccw-0b(%r13) + st %r12,8+.Lorb-0b(%r13) # store cpaddr to orb + la %r12,.Lwrccw-0b(%r13) + oi 1(%r12),0x40 # set CC in wr template + + # first setup the write channel program + + lr %r11,%r4 # save number of blocks + + l %r6,.Ldev_bpt-0b(%r13) # load bpt to r6 + l %r7,.Ldev_nr_hds-0b(%r13) # load heads to r7 + la %r5,.Lwrloop-0b(%r13) + br %r5 /* FIXME */ + +2: # loop for number of block (nbl) time + + # switch to next write CCW + + l %r5,.Ldev_blk_size-0b(%r13) + ar %r3,%r5 # add blksize to destination addr + ah %r12,.Lccw_size-0b(%r13) # add sizeof(ccw) to base address + mvc 0(8,%r12),.Lwrccw-0b(%r13) # copy template to this ccw + +.Lwrloop: + + # build up next write CCW + + st %r3,4(%r12) # store target addr to this ccw + bct %r4,2b-0b(%r13) # decrement no of blks still to do + ni 1(%r12),0x3f # no command chaining for last ccw + + # write CCWs are setup now + + lr %r4,%r11 # restore number of blocks + stcm %r4,3,.Llodata+2-0b(%r13) # store number of blocks to lodata + + # compute end block + + ar %r4,%r2 # r4: ebl = blk + nbl + bctr %r4,0 # decrement r4 (last blk touched) + + # compute start track and start block on track + + srda %r2,32 # shift ==> 64 bit number + dr %r2,%r6 # trk = blk / bpt, bot = blk % bpt + # r3: trk, r2: bot + la %r2,1(%r2) # bot++ ( we start counting at 1 ) + stcm %r2,1,.Llodata+12-0b(%r13) # store bot to lodata + + # compute start cylinder and head + + xr %r2,%r2 # clear bot + dr %r2,%r7 # cy=trk / heads, hd=trk % heads + # r3: cy, r2: hd + sll %r3,16 # combine to CCHH in r3 + or %r3,%r2 + st %r3,.Ldedata+8-0b(%r13) # store cchh to dedata + st %r3,.Llodata+4-0b(%r13) # store cchh to lodata + st %r3,.Llodata+8-0b(%r13) # store cchh to lodata + + # compute end track and end block on track + + lr %r11,%r5 # save r5 + srda %r4,32 # shift ==> 64 bit number + dr %r4,%r6 # tr2 = ebl / bpt + # r5: tr2, r4: bot2 + # compute end cylinder and head + + xr %r4,%r4 # cy2=tr2/heads, hd2=hd2 % heads + dr %r4,%r7 # r5: cy2, r4: hd2 + stcm %r5,3,.Ldedata+12-0b(%r13) # store cy2,hd2 to dedata + stcm %r4,3,.Ldedata+14-0b(%r13) # store cy2,hd2 to dedata + lr %r5,%r11 # restore r5 + + # CCWs are setup now, arent they? + + l %r2,IPL_SC # subchannel id + la %r3,.Lorb-0b(%r13) + la %r4,.Lirb-0b(%r13) + la %r5,10 # initialize retries + + bas %r14,_ssch-0b(%r13) # start I/O + + lm %r6,%r15,120(%r15) + br %r14 # return to caller +.Lccw_size: + .word 0x8 +.align 8 +.Lorb: + .long 0x0049504c,0x0080ff00 # intparm is " IPL" + .long 0x00000000,0x00000000 +.Lirb: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Ldedata: + .long 0x80c00000,0x00000000 + .long 0x00000000,0x00000000 +.Llodata: + .long 0x01800001,0x00000000 + .long 0x00000000,0x03000000 + .long 0x12345678,0x00000000 + +################################################################################ +# read function not implemented: return EINVAL +################################################################################ + +_not_implemented: + la %r2,22 /* EINVAL */ + lcr %r2,%r2 /* -EINVAL */ + br %r14 + +################################################################################ +# expand Macros +################################################################################ + + dump_common_fn + device_fn + io_subroutines + + +################################################################################ +# DATA +################################################################################ + +# extern functions + +.Lpanik: + .long _panik +.Lenable_device: + .long _enable_device +.Ltake_dump: + .long _take_dump + +# device characteristics +.align 8 +.Ldev_start_cyl: + .long 0x00000000 +.Ldev_start_hd: + .long 0x00000000 +.Ldev_start_rec: + .long 0x00000000 +.Ldev_blk_size: + .long 0x00000000 +.Ldev_nr_hds: + .long 0x00000000 +.Ldev_end_cyl: + .long 0x00000000 +.Ldev_end_hd: + .long 0x00000000 +.Ldev_end_rec: + .long 0x00000000 + + +.Ldev_start_blk: + .long 0x00000000 +.Ldev_end_blk: + .long 0x00000000 +.Ldev_bpt: + .long 0x00000000 + + +# +# CCWs +# + +.align 8 + +# channel program for one write + +.Ldeccw: + .long 0x63400010,0x00000000+.Ldedata # define extent +.Lloccw: + .long 0x47400010,0x00000000+.Llodata # locate record +.Lwrccw: + .long 0x8d000000,0x00000000 # update Key & data + +.org IPL_BS diff --git a/arch/s390/boot/dumptape.S b/arch/s390/boot/dumptape.S new file mode 100644 index 000000000000..3417ad2835eb --- /dev/null +++ b/arch/s390/boot/dumptape.S @@ -0,0 +1,183 @@ +/* + * Dump boot loader for 3480/3490 tape devices + * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Michael Holzheu (holzheu@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * Uses extern functions: + * - _panik + * - _enable_device + * - _take_dump + * + * Functions: + * - _dump_mem + */ + +#include "dumpcommon.S" + +#define IPL_BS 0x1000 +#define BLOCK_SIZE 0x8000 /* 32 KB */ +#define DUMP_TOOL_START 0x2000 /* tool is loaded to this address in order */ + /* not to overwrite page 0 */ + +################################################################################ +# The first 24 bytes are loaded by ipl to addresses 0-23. (a PSW and two CCWs) +################################################################################ + + # psw + .long 0x00080000,0x80000000+_start + # rewind ccw + .long 0x07000000,0x60000001 + # ccw to load dump utility to 0x1000 + .long 0x02000000+DUMP_TOOL_START ,0x20000000+IPL_BS + +.globl _start +_start: basr %r13,0 +0: l %r15,1f-0b(%r13) # load end of stack address + l %r11,IPL_SC # load ipl device subchannel id + lr %r2,%r11 + l %r14,.Lenable_device-0b(%r13) + basr %r14,%r14 + l %r14,.Ltake_dump-0b(%r13) + basr %r14,%r14 +1: .long 0x10000-96 # end of stack + +################################################################################ +# Dump memory +################################################################################ + +_dump_mem: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame +# +# first write a tape mark +# + bas %r14,_tapemark-0b(%r13) +# +# write header +# + stck .Ldh_time-0b(%r13) # store time + stidp .Ldh_cpuid-0b(%r13) # store cpu id + la %r2,.Ldh_dumpheader-0b(%r13) # start of header + l %r3,.Lheader_size-0b(%r13) # size of header + lr %r4,%r3 # blocksize + bas %r14,_writer-0b(%r13) + +# +# write real storage to tape +# + + la %r2,0 # start + l %r3,.Ldh_mem_size+4-0b(%r13) # length + l %r4,.Lblock_size-0b(%r13) # blocksize + bas %r14,_writer-0b(%r13) # write page +# +# write end marker +# + stck .Ld_end_time-0b(%r13) # store end time + la %r2,.Ld_endmarker-0b(%r13) # address of end marker + la %r3,END_MARKER_SIZE # size of end marker + la %r4,END_MARKER_SIZE # blocksize + bas %r14,_writer-0b(%r13) +# +# write another tape mark +# + bas %r14,_tapemark-0b(%r13) + + lm %r6,%r15,120(%r15) + br %r14 # return to caller +.Lheader_size: .long HEADER_SIZE +.Lblock_size: .long BLOCK_SIZE + +################################################################################ +# subroutine for writing to tape +# Parameters: +# -r2: start address +# -r3: length +# -r4: blocksize +################################################################################ + +_writer: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + + lr %r10,%r2 # save start address + lr %r11,%r3 # save length + ar %r11,%r2 # end address + lr %r12,%r4 # save blocksize + + st %r10,.Lccwwrite+4-0b(%r13) # initialize CCW data addresses + sth %r12,.Lccwwrite+2-0b(%r13) # setup blocksize +.Lldlp: + l %r2,IPL_SC # subchannel id + la %r3,.Lorbwrite-0b(%r13) # address of orb + la %r4,.Lirb-0b(%r13) # address of irb + la %r5,10 # 10 retries + bas %r14,_ssch-0b(%r13) # write chunk of PAGE_SIZE bytes + + l %r0,.Lccwwrite+4-0b(%r13) # update CCW data addresses + ar %r0,%r12 # add block size + st %r0,.Lccwwrite+4-0b(%r13) + clr %r0,%r11 # enough ? + bl .Lldlp-0b(%r13) + + lm %r6,%r15,120(%r15) + br %r14 + +################################################################################ +# write tapemark +################################################################################ + +_tapemark: + stm %r6,%r15,24(%r15) + basr %r13,0 # base register +0: s %r15,.Lc96-0b(%r13) # create stack frame + + l %r2,IPL_SC # subchannel id + la %r3,.Lorbmark-0b(%r13) # r12 = address of orb + la %r4,.Lirb-0b(%r13) # r5 = address of irb + la %r5,10 # retries + bas %r14,_ssch-0b(%r13) # write a tape mark + + lm %r6,%r15,120(%r15) + br %r14 # return to caller + +################################################################################ +# expand Macros +################################################################################ + +dump_common_fn +device_fn +io_subroutines + +################################################################################ +# DATA +################################################################################ + +# extern functions + +.Lpanik: + .long _panik +.Lenable_device: + .long _enable_device +.Ltake_dump: + .long _take_dump + +# irbs, orbs etc. + + .align 8 +.Lorbwrite: + .long 0x00000000,0x0080ff00,.Lccwwrite + .align 8 +.Lorbmark: + .long 0x00000000,0x0080ff00,.Lccwmark + .align 8 +.Lccwwrite: + .long 0x01200000,0x00000000 +.Lccwmark: + .long 0x1f200001,0x00000000 + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.org IPL_BS diff --git a/arch/s390/boot/ipldump.S b/arch/s390/boot/ipldump.S deleted file mode 100644 index 7de5fbd70f32..000000000000 --- a/arch/s390/boot/ipldump.S +++ /dev/null @@ -1,179 +0,0 @@ -/* - * arch/s390/boot/ipldump.S - * - * S390 version - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), - * - * Tape dump ipl record. Put it on a tape and ipl from it and it will - * write a dump of the real storage after the ipl record on that tape. - */ - -#include -#include -#include - -#define IPL_BS 1024 - .org 0 - .long 0x00080000,0x80000000+_start # The first 24 bytes are loaded - .long 0x07000000,0x60000001 # by ipl to addresses 0-23. - .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). - .long 0x00000000,0x00000000 - .long 0x00000000,0x00000000 # svc old psw - .long 0x00000000,0x00000000 # program check old psw - .long 0x00000000,0x00000000 # machine check old psw - .long 0x00000000,0x00000000 # io old psw - .long 0x00000000,0x00000000 - .long 0x00000000,0x00000000 - .long 0x00000000,0x00000000 - .long 0x000a0000,0x00000058 # external new psw - .long 0x000a0000,0x00000060 # svc new psw - .long 0x000a0000,0x00000068 # program check new psw - .long 0x000a0000,0x00000070 # machine check new psw - .long 0x00080000,0x80000000+.Lioint # io new psw - - .org 0x100 - .globl _start -_start: - l %r1,0xb8 # load ipl subchannel number -# -# find out memory size -# - mvc 104(8,0),.Lpcmem0 # setup program check handler - slr %r3,%r3 - lhi %r2,1 - sll %r2,20 -.Lloop0: - l %r0,0(%r3) # test page - ar %r3,%r2 # add 1M - jnm .Lloop0 # r1 < 0x80000000 -> loop -.Lchkmem0: - n %r3,.L4malign0 # align to multiples of 4M - st %r3,.Lmemsize # store memory size -.Lmemok: - -# -# first write a tape mark -# - bras %r14,.Ltapemark -# -# write real storage to tape -# - slr %r2,%r2 # start at address 0 - bras %r14,.Lwriter # load ramdisk -# -# write another tape mark -# - bras %r14,.Ltapemark -# -# everything written, stop processor -# - lpsw .Lstopped -# -# subroutine for writing to tape -# Paramters: -# R1 = device number -# R2 = start address -# R3 = length -.Lwriter: - st %r14,.Lldret - la %r12,.Lorbread # r12 = address of orb - la %r5,.Lirb # r5 = address of irb - st %r2,.Lccwwrite+4 # initialize CCW data addresses - lctl %c6,%c6,.Lcr6 - slr %r2,%r2 -.Lldlp: - lhi %r6,3 # 3 retries -.Lssch: - ssch 0(%r12) # write chunk of IPL_BS bytes - jnz .Llderr -.Lw4end: - bras %r14,.Lwait4io - tm 8(%r5),0x82 # do we have a problem ? - jnz .Lrecov - l %r0,.Lccwwrite+4 # update CCW data addresses - ahi %r0,IPL_BS - st %r0,.Lccwwrite+4 - clr %r0,%r3 # enough ? - jl .Lldlp -.Ldone: - l %r14,.Lldret - br %r14 # r2 contains the total size -.Lrecov: - bras %r14,.Lsense # do the sensing - brct %r6,.Lssch # dec. retry count & branch - j .Llderr -.Ltapemark: - st %r14,.Lldret - la %r12,.Lorbmark # r12 = address of orb - la %r5,.Lirb # r5 = address of irb - lctl %c6,%c6,.Lcr6 - ssch 0(%r12) # write a tape mark - jnz .Llderr - bras %r14,.Lwait4io - l %r14,.Lldret - br %r14 -# -# Sense subroutine -# -.Lsense: - st %r14,.Lsnsret - la %r7,.Lorbsense - ssch 0(%r7) # start sense command - jnz .Llderr - bras %r14,.Lwait4io - l %r14,.Lsnsret - tm 8(%r5),0x82 # do we have a problem ? - jnz .Llderr - br %r14 -# -# Wait for interrupt subroutine -# -.Lwait4io: - lpsw .Lwaitpsw -.Lioint: - c %r1,0xb8 # compare subchannel number - jne .Lwait4io - tsch 0(%r5) - slr %r0,%r0 - tm 8(%r5),0x82 # do we have a problem ? - jnz .Lwtexit - tm 8(%r5),0x04 # got device end ? - jz .Lwait4io -.Lwtexit: - br %r14 -.Llderr: - lpsw .Lcrash - - .align 8 -.Lorbread: - .long 0x00000000,0x0080ff00,.Lccwwrite - .align 8 -.Lorbsense: - .long 0x00000000,0x0080ff00,.Lccwsense - .align 8 -.Lorbmark: - .long 0x00000000,0x0080ff00,.Lccwmark - .align 8 -.Lccwwrite: - .long 0x01200000+IPL_BS,0x00000000 -.Lccwsense: - .long 0x04200001,0x00000000 -.Lccwmark: - .long 0x1f200001,0x00000000 -.Lwaitpsw: - .long 0x020a0000,0x80000000+.Lioint - -.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -.Lcr6: .long 0xff000000 - .align 8 -.Lcrash:.long 0x000a0000,0x00000000 -.Lstopped: .long 0x000a0000,0x00001234 -.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 -.L4malign0:.long 0xffc00000 -.Lmemsize:.long 0 -.Lldret:.long 0 -.Lsnsret: .long 0 - - .org IPL_BS - diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 77b3c0443ad3..5e1f6ff9aba3 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -1,5 +1,5 @@ # -# Automatically generated by make menuconfig: don't edit +# Automatically generated make config: don't edit # CONFIG_ARCH_S390=y @@ -7,7 +7,7 @@ CONFIG_ARCH_S390=y # Code maturity level options # CONFIG_EXPERIMENTAL=y -CONFIG_PROCESS_DEBUG=n +# CONFIG_PROCESS_DEBUG is not set # # Processor type and features @@ -42,11 +42,15 @@ CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_RAM_SIZE=24576 CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_XPRAM=m # CONFIG_MDISK is not set CONFIG_DASD=y + +# +# DASD disciplines +# CONFIG_DASD_ECKD=y CONFIG_DASD_FBA=y CONFIG_DASD_MDSK=y @@ -55,6 +59,10 @@ CONFIG_DASD_MDSK=y # S/390 Network device support # CONFIG_NETDEVICES=y + +# +# S390 Network devices +# CONFIG_CTC=y CONFIG_IUCV=y # CONFIG_DUMMY is not set @@ -68,6 +76,15 @@ CONFIG_3215=y CONFIG_3215_CONSOLE=y CONFIG_HWC=y CONFIG_HWC_CONSOLE=y + +# +# S/390 character device drivers +# +# CONFIG_S390_TAPE is not set + +# +# Character devices +# CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 @@ -88,16 +105,26 @@ CONFIG_IP_MULTICAST=y # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set # CONFIG_IP_ALIAS is not set # CONFIG_SYN_COOKIES is not set + +# +# (it is safe to leave these untouched) +# # CONFIG_INET_RARP is not set CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set + +# +# +# # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set +# CONFIG_NET_DIVERT is not set # CONFIG_LLC is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set @@ -141,8 +168,10 @@ CONFIG_EXT2_FS=y # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set CONFIG_NFSD=y -# CONFIG_NFSD_SUN 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 @@ -153,6 +182,7 @@ CONFIG_LOCKD=y # # CONFIG_BSD_DISKLABEL is not set # CONFIG_MAC_PARTITION is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index f95a1e950fb8..6cab841d3f11 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -657,7 +657,6 @@ pgm_sv: lh %r7,__LC_PGM_ILC # load instruction length pgm_no_sv: lh %r8,__LC_PGM_INT_CODE # N.B. saved int code used later KEEP it - stosm 24(%r15),0x03 # reenable interrupts lr %r3,%r8 la %r0,0x7f nr %r3,%r0 # clear per-event-bit @@ -672,6 +671,9 @@ pgm_no_sv: l %r5,SP_PSW+4(15) # load psw addr sr %r5,%r7 # substract ilc from psw st %r5,SP_PSW+4(15) # store corrected psw addr +pgm_per:cl %r3,BASED(.Lc20) # pseudo page fault ? + be BASED(pgm_go) # if yes then don't reenable interrupts + stosm 24(%r15),0x03 # reenable interrupts pgm_go: basr %r14,%r9 # branch to interrupt-handler pgm_dn: la %r0,0x80 nr %r8,%r0 # check for per exception @@ -834,6 +836,7 @@ restart_go: .Lc_ac: .long 0,0,1 .Lc_ENOSYS: .long -ENOSYS .Lc4: .long 4 +.Lc20: .long 20 .Lc0x1202: .long 0x1202 .Lc0x1004: .long 0x1004 .Lc0x2401: .long 0x2401 diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S index c30fe433c42f..d4c158c8969a 100644 --- a/arch/s390/kernel/reipl.S +++ b/arch/s390/kernel/reipl.S @@ -55,7 +55,7 @@ do_reipl: basr %r13,0 .Ldispsw: .long 0x000a0000,0x00000000 .Liplccws: .long 0x02000000,0x60000018 .long 0x08000008,0x20000001 -.Liplorb: .long 0x0049504c,0x0000ff80 +.Liplorb: .long 0x0049504c,0x0040ff80 .long 0x00000000+.Liplccws .Lschib: .long 0x00000000,0x00000000 .long 0x00000000,0x00000000 diff --git a/arch/s390/kernel/s390io.c b/arch/s390/kernel/s390io.c index 0d62ca7fd298..054fc97a2833 100644 --- a/arch/s390/kernel/s390io.c +++ b/arch/s390/kernel/s390io.c @@ -6,6 +6,9 @@ * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Ingo Adlung (adlung@de.ibm.com) + * + * ChangeLog : 03/14/2001 Ingo Adlung Save not_oper func pointer prior + * to wipe it out */ #include @@ -121,7 +124,10 @@ void s390_displayhex(char *str,void *ptr,s32 cnt) } } - +/* + * Note : internal use of irqflags SA_PROBE for NOT path grouping + * + */ int s390_request_irq_special( int irq, io_handler_func_t io_handler, not_oper_handler_func_t not_oper_handler, @@ -172,7 +178,8 @@ int s390_request_irq_special( int irq, { if ( !retval ) { - s390_DevicePathVerification( irq, 0 ); + if ( !(irqflags & SA_PROBE)) + s390_DevicePathVerification( irq, 0 ); } else { @@ -1947,6 +1954,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, int new_irq; #endif int use_irq = irq; + int cpu = smp_processor_id(); // // fix me !!! @@ -1974,6 +1982,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, } /* endif */ + irq_enter(cpu, use_irq); s390irq_spin_lock(use_irq); #ifdef CONFIG_FAST_IRQ @@ -2012,6 +2021,7 @@ asmlinkage void do_IRQ( struct pt_regs regs, #endif /* CONFIG_FAST_IRQ */ s390irq_spin_unlock(use_irq); + irq_exit(cpu, use_irq); return; } @@ -2141,9 +2151,28 @@ int s390_process_IRQ( unsigned int irq ) * secondary status are presented with different interrupts. */ if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl - & ( SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_INTER_STATUS ) ) - { - ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count; + & ( SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_INTER_STATUS ) ) { + + /* + * If the subchannel status shows status pending + * and we received a check condition, the count + * information is not meaningful. + */ + + if ( !( (dp->ii.irb.scsw.stctl & SCSW_STCTL_STATUS_PEND) + && ( dp->ii.irb.scsw.cstat + & ( SCHN_STAT_CHN_DATA_CHK + | SCHN_STAT_CHN_CTRL_CHK + | SCHN_STAT_INTF_CTRL_CHK + | SCHN_STAT_PROG_CHECK + | SCHN_STAT_PROT_CHECK + | SCHN_STAT_CHAIN_CHECK )))) { + + dp->rescnt = dp->ii.irb.scsw.count; + } else { + dp->rescnt = SENSE_MAX_COUNT; + } + ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa; #ifdef CONFIG_DEBUG_IO @@ -3363,7 +3392,7 @@ int read_dev_chars( int irq, void **buffer, int length ) { ret = request_irq( irq, init_IRQ_handler, - 0, "RDC", &devstat ); + SA_PROBE, "RDC", &devstat ); if ( !ret ) { @@ -3519,7 +3548,7 @@ int read_conf_data( int irq, void **buffer, int *length, __u8 lpm ) pdevstat = &devstat; ret = request_irq( irq, init_IRQ_handler, - 0, "RCD", pdevstat ); + SA_PROBE, "RCD", pdevstat ); if ( !ret ) { @@ -3961,9 +3990,9 @@ void s390_device_recognition_irq( int irq ) int irq_ret; devstat_t devstat; - irq_ret = request_irq( irq, + irq_ret = request_irq( irq, init_IRQ_handler, - 0, + SA_PROBE, "INIT", &devstat); @@ -4028,8 +4057,6 @@ void s390_device_recognition_irq( int irq ) } /* endif */ #endif - s390_DevicePathVerification( irq, 0 ); - disable_cpu_sync_isc( irq ); ioinfo[irq]->ui.flags.syncio = 0; // global @@ -4463,7 +4490,7 @@ int s390_SenseID( int irq, senseid_t *sid, __u8 lpm ) * requests and evaluate the devstat area on return therefore * we don't need a real I/O handler in place. */ - irq_ret = request_irq( irq, init_IRQ_handler, 0, "SID", &devstat); + irq_ret = request_irq( irq, init_IRQ_handler, SA_PROBE, "SID", &devstat); if ( irq_ret == 0 ) inlreq = 1; @@ -4972,6 +4999,7 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) ccw1_t spid_ccw[2]; /* ccw area for SPID command */ devstat_t devstat; /* required by request_irq() */ devstat_t *pdevstat = &devstat; + unsigned long flags; int irq_ret = 0; /* return code */ int retry = 5; /* retry count */ @@ -5005,7 +5033,7 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) */ irq_ret = request_irq( irq, init_IRQ_handler, - 0, + SA_PROBE, "SPID", pdevstat); @@ -5020,7 +5048,7 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) if ( irq_ret == 0 ) { - s390irq_spin_lock( irq); + s390irq_spin_lock_irqsave( irq, flags); spid_ccw[0].cmd_code = 0x5B; /* suspend multipath reconnect */ spid_ccw[0].cda = 0; @@ -5093,9 +5121,9 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) } /* endif */ } -#ifdef CONFIG_DEBUG_IO else { +#ifdef CONFIG_DEBUG_IO printk( "SPID - device %04X," " unit check," " retry %d, cnt %02d," @@ -5112,9 +5140,10 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) pdevstat->ii.sense.data[5], pdevstat->ii.sense.data[6], pdevstat->ii.sense.data[7]); - - } /* endif */ #endif + retry--; + } /* endif */ + } else if ( pdevstat->flag & DEVSTAT_NOT_OPER ) { @@ -5140,7 +5169,7 @@ int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) } while ( retry > 0 ); - s390irq_spin_unlock( irq); + s390irq_spin_unlock_irqrestore( irq, flags); /* * If we installed the irq action handler we have to @@ -5170,6 +5199,7 @@ int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) int irq_ret = 0; /* return code */ int retry = 5; /* retry count */ int inlreq = 0; /* inline request_irq() */ + unsigned long flags; if ( (irq > highest_subchannel) || (irq < 0 ) ) { @@ -5198,7 +5228,7 @@ int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) */ irq_ret = request_irq( irq, init_IRQ_handler, - 0, + SA_PROBE, "SNID", pdevstat); @@ -5214,7 +5244,7 @@ int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) if ( irq_ret == 0 ) { - s390irq_spin_lock( irq); + s390irq_spin_lock_irqsave( irq, flags); snid_ccw.cmd_code = CCW_CMD_SENSE_PGID; snid_ccw.cda = (__u32)virt_to_phys( pgid ); @@ -5324,7 +5354,7 @@ int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) } while ( retry > 0 ); - s390irq_spin_unlock( irq); + s390irq_spin_unlock_irqrestore( irq, flags); /* * If we installed the irq action handler we have to @@ -5372,10 +5402,10 @@ void s390_do_crw_pending( crwe_t *pcrwe ) * If the device isn't known yet * we can't lock it ... */ - if ( ioinfo[irq] != INVALID_STORAGE_AREA ) + if ( ioinfo[irq] != INVALID_STORAGE_AREA ) { s390irq_spin_lock( irq ); - lock = 1; + lock = 1; dev_oper = ioinfo[irq]->ui.flags.oper; @@ -5417,20 +5447,21 @@ void s390_do_crw_pending( crwe_t *pcrwe ) if ( ioinfo[irq] != INVALID_STORAGE_AREA ) { - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - /* - * If the device has gone - * call not oper handler - */ - if ( ( dev_oper == 1 ) - && ( ioinfo[irq]->nopfunc != NULL ) ) - { + not_oper_handler_func_t nopfunc = ioinfo[irq]->nopfunc; - free_irq( irq, + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + /* + * If the device has gone + * call not oper handler + */ + if ( ( dev_oper == 1 ) + && ( nopfunc != NULL ) ) { + + free_irq( irq, ioinfo[irq]->irq_desc.action->dev_id ); - ioinfo[irq]->nopfunc( irq, - DEVSTAT_DEVICE_GONE ); + nopfunc( irq, DEVSTAT_DEVICE_GONE ); + } /* endif */ } else @@ -5466,10 +5497,10 @@ void s390_do_crw_pending( crwe_t *pcrwe ) * ... it is and was operational, but * the devno may have changed */ - else if ( ioinfo[irq]->devno != dev_no ) + else if ( ( ioinfo[irq]->devno != dev_no ) + && ( nopfunc != NULL ) ) { - ioinfo[irq]->nopfunc( irq, - DEVSTAT_REVALIDATE ); + nopfunc( irq, DEVSTAT_REVALIDATE ); } /* endif */ diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 9980903cc458..55a72d21dacc 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -273,6 +273,8 @@ __initfunc(void setup_arch(char **cmdline_p, break; if (cn == '\n') cn = ' '; /* replace newlines with space */ + if (cn == 0x0d) + cn = ' '; /* replace 0x0d with space */ if (cn == ' ' && c == ' ') continue; /* remove additional spaces */ c = cn; diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index f0a50b423ffe..4c6eb802b184 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -301,6 +301,7 @@ static void *setup_frame_common(int sig, struct k_sigaction *ka, /* Set up registers for signal handler */ regs->gprs[15] = (addr_t)frame; regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + regs->psw.mask = _USER_PSW_MASK; } /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 7d4963c2bf48..cd67259b7ae3 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -426,7 +426,33 @@ int smp_signal_others(sigp_order_code order_code, u32 parameter, void smp_send_stop(void) { - smp_signal_others(sigp_stop_and_store_status, 0, TRUE, NULL); + int i; + u32 dummy; + unsigned long low_core_addr; + + /* write magic number to zero page (absolute 0) */ + + get_cpu_lowcore(smp_processor_id()).panic_magic = __PANIC_MAGIC; + + /* stop all processors */ + + smp_signal_others(sigp_stop, 0, TRUE, NULL); + + /* store status of all processors in their lowcores (real 0) */ + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + int ccode; + low_core_addr = (unsigned long)&get_cpu_lowcore(i); + do { + ccode = signal_processor_ps( + &dummy, + low_core_addr, + i, + sigp_store_status_at_address); + } while(ccode == sigp_busy); + } + } } /* diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 27b210e1ffd2..3c4657fc81f8 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -37,6 +37,8 @@ #include #endif +#include "cpcmd.h" + /* Called from entry.S only */ extern void handle_per_exception(struct pt_regs *regs); @@ -44,6 +46,7 @@ typedef void pgm_check_handler_t(struct pt_regs *, long); pgm_check_handler_t *pgm_check_table[128]; extern pgm_check_handler_t do_page_fault; +extern pgm_check_handler_t do_pseudo_page_fault; static inline void console_verbose(void) { @@ -113,7 +116,7 @@ int do_debugger_trap(struct pt_regs *regs,int signal) return(FALSE); } -DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler); +DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler) DO_ERROR(SIGILL, "privileged operation", privileged_op) DO_ERROR(SIGILL, "execute exception", execute_exception) DO_ERROR(SIGSEGV, "addressing exception", addressing_exception) @@ -317,18 +320,20 @@ __initfunc(void trap_init(void)) pgm_check_table[1] = &illegal_op; pgm_check_table[2] = &privileged_op; pgm_check_table[3] = &execute_exception; + pgm_check_table[4] = &do_page_fault; pgm_check_table[5] = &addressing_exception; pgm_check_table[6] = &specification_exception; pgm_check_table[7] = &data_exception; pgm_check_table[9] = ÷_exception; + pgm_check_table[0x10] = &do_page_fault; + pgm_check_table[0x11] = &do_page_fault; pgm_check_table[0x12] = &translation_exception; pgm_check_table[0x13] = &special_op_exception; + pgm_check_table[0x14] = &do_pseudo_page_fault; pgm_check_table[0x15] = &operand_exception; - pgm_check_table[4] = &do_page_fault; - pgm_check_table[0x10] = &do_page_fault; - pgm_check_table[0x11] = &do_page_fault; pgm_check_table[0x1C] = &privileged_op; - + if (MACHINE_IS_VM) + cpcmd("SET PAGEX ON", NULL, 0); } diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 5ee70d1fa1a4..6d79b9f02423 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -243,3 +244,123 @@ do_sigbus: goto no_context; } +typedef struct _pseudo_wait_t { + struct _pseudo_wait_t *next; + wait_queue_head_t queue; + unsigned long address; + int resolved; +} pseudo_wait_t; + +static pseudo_wait_t *pseudo_lock_queue = NULL; +static spinlock_t pseudo_wait_spinlock; /* spinlock to protect lock queue */ + +/* + * This routine handles pseudo page faults. + */ +asmlinkage void +do_pseudo_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + DECLARE_WAITQUEUE(wait, current); + pseudo_wait_t wait_struct; + pseudo_wait_t *ptr, *last, *next; + unsigned long psw_mask; + unsigned long address; + int kernel_address; + + /* + * get psw mask of Program old psw to find out, + * if user or kernel mode + */ + psw_mask = S390_lowcore.program_old_psw.mask; + + /* + * get the failing address + * more specific the segment and page table portion of + * the address + */ + address = S390_lowcore.trans_exc_code & 0xfffff000; + + if (address & 0x80000000) { + /* high bit set -> a page has been swapped in by VM */ + address &= 0x7fffffff; + spin_lock(&pseudo_wait_spinlock); + last = NULL; + ptr = pseudo_lock_queue; + while (ptr != NULL) { + next = ptr->next; + if (address == ptr->address) { + /* + * This is one of the processes waiting + * for the page. Unchain from the queue. + * There can be more than one process + * waiting for the same page. VM presents + * an initial and a completion interrupt for + * every process that tries to access a + * page swapped out by VM. + */ + if (last == NULL) + pseudo_lock_queue = next; + else + last->next = next; + /* now wake up the process */ + ptr->resolved = 1; + wake_up(&ptr->queue); + } else + last = ptr; + ptr = next; + } + spin_unlock(&pseudo_wait_spinlock); + } else { + /* Pseudo page faults in kernel mode is a bad idea */ + if (!(psw_mask & PSW_PROBLEM_STATE)) { + /* + * VM presents pseudo page faults if the interrupted + * state was not disabled for interrupts. So we can + * get pseudo page fault interrupts while running + * in kernel mode. We simply access the page here + * while we are running disabled. VM will then swap + * in the page synchronously. + */ + kernel_address = 0; + switch (S390_lowcore.trans_exc_code & 3) { + case 0: /* Primary Segment Table Descriptor */ + kernel_address = 1; + break; + case 1: /* STD determined via access register */ + if (S390_lowcore.exc_access_id == 0 || + regs->acrs[S390_lowcore.exc_access_id]==0) + kernel_address = 1; + break; + case 2: /* Secondary Segment Table Descriptor */ + case 3: /* Home Segment Table Descriptor */ + break; + } + if (kernel_address) + /* dereference a virtual kernel address */ + __asm__ __volatile__ ( + " ic 0,0(%0)" + : : "a" (address) : "0"); + else + /* dereference a virtual user address */ + __asm__ __volatile__ ( + " la 2,0(%0)\n" + " sacf 512\n" + " ic 2,0(2)\n" + " sacf 0" + : : "a" (address) : "2" ); + + return; + } + /* initialize and add element to pseudo_lock_queue */ + init_waitqueue_head (&wait_struct.queue); + wait_struct.address = address; + wait_struct.resolved = 0; + spin_lock(&pseudo_wait_spinlock); + wait_struct.next = pseudo_lock_queue; + pseudo_lock_queue = &wait_struct; + spin_unlock(&pseudo_wait_spinlock); + /* go to sleep */ + wait_event(wait_struct.queue, wait_struct.resolved); + } +} + diff --git a/drivers/Makefile b/drivers/Makefile index 57b2b05101fe..866cf97c667f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -54,6 +54,10 @@ MOD_SUB_DIRS += usb else ifeq ($(CONFIG_USB),m) MOD_SUB_DIRS += usb + else + ifdef CONFIG_INPUT_ADBHID + SUB_DIRS += usb + endif endif endif diff --git a/drivers/block/ide-pmac.c b/drivers/block/ide-pmac.c index 446bb67e9d8d..b4c7f22efeeb 100644 --- a/drivers/block/ide-pmac.c +++ b/drivers/block/ide-pmac.c @@ -906,7 +906,7 @@ static void idepmac_wake_device(ide_drive_t *drive, int used_dma) * Problem: This can schedule. I moved the block device * wakeup almost late by priority because of that. */ - if (DRIVER(drive)) + if (DRIVER(drive) && DRIVER(drive)->media_change) DRIVER(drive)->media_change(drive); /* We kick the VFS too (see fix in ide.c revalidate) */ diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index b66d56ccf067..0eadfefdd839 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1063,6 +1063,9 @@ __initfunc(int blk_dev_init(void)) #ifdef CONFIG_DASD dasd_init(); #endif +#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_BLOCK) + tapeblock_init(); +#endif #ifdef CONFIG_BLK_DEV_XPRAM xpram_init(); #endif diff --git a/drivers/char/mem.c b/drivers/char/mem.c index dfab308263c5..3cf076b10eef 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -49,6 +49,9 @@ extern void prom_con_init(void); #ifdef CONFIG_MDA_CONSOLE extern void mda_console_init(void); #endif +#ifdef CONFIG_S390_TAPE_CHAR +extern void tapechar_init(void); +#endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) @@ -655,6 +658,9 @@ __initfunc(int chr_dev_init(void)) #endif #ifdef CONFIG_PHONE telephony_init(); +#endif +#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR) + tapechar_init(); #endif return 0; } diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index e3a8ae2ffc87..ac6819446050 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -617,7 +617,7 @@ static void receive_packet(struct device *dev, int len) } else { skb_reserve(skb, 2); target = skb_put(skb, rlen); - if (virt_to_bus(target + rlen) >= MAX_DMA_ADDRESS) { + if ((unsigned long)(target + rlen) >= MAX_DMA_ADDRESS) { adapter->current_dma.target = target; target = adapter->dma_buffer; } else { @@ -1062,11 +1062,13 @@ static int send_packet(struct device *dev, struct sk_buff *skb) adapter->current_dma.direction = 1; adapter->current_dma.start_time = jiffies; - target = virt_to_bus(skb->data); - if ((target + nlen) >= MAX_DMA_ADDRESS) { + if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS) { memcpy(adapter->dma_buffer, skb->data, nlen); target = virt_to_bus(adapter->dma_buffer); } + else { + target = virt_to_bus(skb->data); + } adapter->current_dma.skb = skb; flags=claim_dma_lock(); diff --git a/drivers/s390/Config.in b/drivers/s390/Config.in index 6da5d6c11b6c..62a3304f518c 100644 --- a/drivers/s390/Config.in +++ b/drivers/s390/Config.in @@ -75,3 +75,16 @@ if [ "$CONFIG_HWC" = "y" ]; then fi endmenu +mainmenu_option next_comment +comment 'S/390 character device drivers' + +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 +fi +endmenu diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 1cc89d0536b9..75c5d5097de2 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -13,4 +13,31 @@ ifeq ($(CONFIG_HWC),y) O_OBJS += hwc_con.o hwc_rw.o hwc_tty.o endif +# stuff for building tape390.o +T390_OBJS = tape.o +ifeq ($(CONFIG_S390_TAPE_CHAR),y) + T390_OBJS += tapechar.o +endif +ifeq ($(CONFIG_S390_TAPE_BLOCK),y) + T390_OBJS += tapeblock.o +endif +ifeq ($(CONFIG_S390_TAPE_3480),y) + T390_OBJS += tape3480.o + CONFIG_S390_TAPE_NEED_34xx = y +endif +ifeq ($(CONFIG_S390_TAPE_3490),y) + T390_OBJS += tape3490.o + CONFIG_S390_TAPE_NEED_34xx = y +endif +ifeq ($(CONFIG_S390_TAPE_NEED_34xx),y) + T390_OBJS += tape34xx.o +endif +ifeq ($(CONFIG_S390_TAPE),y) + O_OBJS += tape390.o +endif +ifeq ($(CONFIG_S390_TAPE),m) + M_OBJS += tape390.o +endif +tape390.o: $(T390_OBJS) + $(LD) -r -o $@ $(T390_OBJS) include $(TOPDIR)/Rules.make diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 24f91c68f503..d01c7e87c2fc 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -834,7 +834,7 @@ con3215_write(struct console *co, const char *str, unsigned int count) kdev_t con3215_device(struct console *c) { - return MKDEV(TTY_MAJOR, c->index); + return MKDEV(TTY_MAJOR, c->index + 64); } /* diff --git a/drivers/s390/char/hwc.h b/drivers/s390/char/hwc.h index 4e5893696538..4a1b120026cf 100644 --- a/drivers/s390/char/hwc.h +++ b/drivers/s390/char/hwc.h @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke * * * @@ -13,6 +13,9 @@ #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 diff --git a/drivers/s390/char/hwc_con.c b/drivers/s390/char/hwc_con.c index 39b8444b68bc..393f67f9537b 100644 --- a/drivers/s390/char/hwc_con.c +++ b/drivers/s390/char/hwc_con.c @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke */ #include diff --git a/drivers/s390/char/hwc_rw.c b/drivers/s390/char/hwc_rw.c index 4655b01faf13..75d0cafca4c3 100644 --- a/drivers/s390/char/hwc_rw.c +++ b/drivers/s390/char/hwc_rw.c @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke * * * @@ -27,6 +27,7 @@ #include #include #include +#include #ifndef MIN #define MIN(a,b) (((awake_up != NULL) - (hwc_data.calls->wake_up) (); + } + + 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) +{ + u32 ext_int_param = hwc_ext_int_param (); + int cpu = smp_processor_id (); + + 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 diff --git a/drivers/s390/char/hwc_rw.h b/drivers/s390/char/hwc_rw.h index 912024158436..60f4f8455f46 100644 --- a/drivers/s390/char/hwc_rw.h +++ b/drivers/s390/char/hwc_rw.h @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke */ #ifndef __HWC_RW_H__ diff --git a/drivers/s390/char/hwc_tty.c b/drivers/s390/char/hwc_tty.c index 0ff5d86e34e3..3cbf9b64590f 100644 --- a/drivers/s390/char/hwc_tty.c +++ b/drivers/s390/char/hwc_tty.c @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke * * Thanks to Martin Schwidefsky. */ @@ -171,9 +171,7 @@ hwc_tty_ioctl ( switch (cmd) { case TIOCHWCTTYSINTRC: - count = strnlen_user((const char *)arg, HWC_TTY_MAX_CNTL_SIZE); - if (!count) - return -EFAULT; + count = strlen_user ((const char *) arg); if (count > HWC_TTY_MAX_CNTL_SIZE) return -EINVAL; strncpy_from_user (hwc_tty_data.ioctl.intr_char, diff --git a/drivers/s390/char/hwc_tty.h b/drivers/s390/char/hwc_tty.h index 0e71dc150820..bbc2035e3660 100644 --- a/drivers/s390/char/hwc_tty.h +++ b/drivers/s390/char/hwc_tty.h @@ -4,7 +4,7 @@ * * S390 version * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Peschke + * Author(s): Martin Peschke */ #ifndef __HWC_TTY_H__ diff --git a/drivers/s390/char/tape.c b/drivers/s390/char/tape.c new file mode 100644 index 000000000000..4b71acfedf8e --- /dev/null +++ b/drivers/s390/char/tape.c @@ -0,0 +1,1120 @@ + +/*********************************************************************** + * 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 --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h new file mode 100644 index 000000000000..05bf2ad31f8d --- /dev/null +++ b/drivers/s390/char/tape.h @@ -0,0 +1,203 @@ +/*************************************************************************** + * + * drivers/s390/char/tape.h + * tape device driver for 3480/3490E tapes. + * + * S390 and zSeries version + * Copyright (C) 2001 IBM Corporation + * Author(s): Carsten Otte + * Tuan Ngo-Anh + * + **************************************************************************** + */ + +#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*); +#ifdef CONFIG_DEVFS_FS +typedef void (*tape_devfs_handler_t) (struct _tape_info_t*); +#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; +#ifdef CONFIG_DEVFS_FS + tape_devfs_handler_t mkdevfstree; + tape_devfs_handler_t rmdevfstree; +#endif + void* next; +} tape_frontend_t __attribute__ ((aligned(8))); + + +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 */ +#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); + +/* 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); + +/* 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 + +/* 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; + +/* The debug area */ +#ifdef TAPE_DEBUG +extern debug_info_t *tape_debug_area; +#endif + +#endif /* for ifdef tape.h */ diff --git a/drivers/s390/char/tape3480.c b/drivers/s390/char/tape3480.c new file mode 100644 index 000000000000..0f02d12ab008 --- /dev/null +++ b/drivers/s390/char/tape3480.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tape3480.h b/drivers/s390/char/tape3480.h new file mode 100644 index 000000000000..8f48263e6cae --- /dev/null +++ b/drivers/s390/char/tape3480.h @@ -0,0 +1,23 @@ +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tape3490.c b/drivers/s390/char/tape3490.c new file mode 100644 index 000000000000..d7c9440a82b2 --- /dev/null +++ b/drivers/s390/char/tape3490.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tape3490.h b/drivers/s390/char/tape3490.h new file mode 100644 index 000000000000..59f6b58b7f4e --- /dev/null +++ b/drivers/s390/char/tape3490.h @@ -0,0 +1,24 @@ + +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tape34xx.c b/drivers/s390/char/tape34xx.c new file mode 100644 index 000000000000..cf6754a99cde --- /dev/null +++ b/drivers/s390/char/tape34xx.c @@ -0,0 +1,2382 @@ +/*************************************************************************** + * + * 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_interruptible (&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_interruptible (&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) { + tape34xx_error_recovery_has_failed(ti,ENOSPC); + } + 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 recieved. 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 recieved, 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: + 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 --git a/drivers/s390/char/tape34xx.h b/drivers/s390/char/tape34xx.h new file mode 100644 index 000000000000..48435e4b4792 --- /dev/null +++ b/drivers/s390/char/tape34xx.h @@ -0,0 +1,183 @@ + +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tapeblock.c b/drivers/s390/char/tapeblock.c new file mode 100644 index 000000000000..e27219cbff63 --- /dev/null +++ b/drivers/s390/char/tapeblock.c @@ -0,0 +1,598 @@ + +/*************************************************************************** + * + * 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 + 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); + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif /* MODULE */ + 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); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + 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 --git a/drivers/s390/char/tapeblock.h b/drivers/s390/char/tapeblock.h new file mode 100644 index 000000000000..c4a57bba0e70 --- /dev/null +++ b/drivers/s390/char/tapeblock.h @@ -0,0 +1,36 @@ + +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tapechar.c b/drivers/s390/char/tapechar.c new file mode 100644 index 000000000000..61ddecbeffaf --- /dev/null +++ b/drivers/s390/char/tapechar.c @@ -0,0 +1,761 @@ + +/*************************************************************************** + * + * 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_interruptible (ti->wq,ti->wanna_wakeup); + ti->cqr = NULL; + ti->discipline->free_read_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); + 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; + + 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); + return 0; + } + if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) { +#ifdef TAPE_DEBUG + debug_text_event (tape_debug_area,6,"c:notidle!"); +#endif + return -ENXIO; /* error in tape_release */ + } +#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); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + return 0; +} diff --git a/drivers/s390/char/tapechar.h b/drivers/s390/char/tapechar.h new file mode 100644 index 000000000000..eb4c35b6d647 --- /dev/null +++ b/drivers/s390/char/tapechar.h @@ -0,0 +1,34 @@ + +/*************************************************************************** + * + * 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 --git a/drivers/s390/char/tapedefs.h b/drivers/s390/char/tapedefs.h new file mode 100644 index 000000000000..fa714c2c46ef --- /dev/null +++ b/drivers/s390/char/tapedefs.h @@ -0,0 +1,76 @@ +/*********************************************************************** + * 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 --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c index d1151b9d086a..2b29c53eae19 100644 --- a/drivers/s390/net/iucv.c +++ b/drivers/s390/net/iucv.c @@ -20,6 +20,7 @@ #include #include "iucv.h" #include +#include #include #include #include @@ -1531,11 +1532,16 @@ iucv_send2way_prmmsg_array (ushort pathid, ulong * msgid, inline void top_half_interrupt (struct pt_regs *regs, __u16 code) { + int cpu = smp_processor_id(); iucv_packet *pkt; + + irq_enter(cpu, 0x4000); + pkt = (iucv_packet *) kmalloc (sizeof (iucv_packet), GFP_ATOMIC); if (pkt == NULL) { printk (KERN_DEBUG "out of memory\n"); + irq_exit(cpu, 0x4000); return; } memcpy (pkt->data, iucv_external_int_buffer, 40); @@ -1560,6 +1566,7 @@ printk (KERN_EMERG "TH: Queuing BH\n"); queue_task (&short_task, &tq_immediate); mark_bh (IMMEDIATE_BH); } + irq_exit(cpu, 0x4000); return; } diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c index e06f2a4d1638..05537805ecf5 100644 --- a/drivers/sbus/char/aurora.c +++ b/drivers/sbus/char/aurora.c @@ -1,7 +1,7 @@ /* * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver * - * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro) + * Copyright (c) 1999 by Oliver Aldulea (oli at bv.ro) * * This code is based on the RISCom/8 multiport serial driver written * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index cf91f2acc0d7..4ac7eb1aa634 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -23,7 +23,7 @@ export-objs := usb.o input.o # Multipart objects. list-multi := usbcore.o -usbcore-objs := usb.o usb-debug.o usb-core.o hub.o +usbcore-objs := usb.o usb-debug.o hub.o # Optional parts of multipart objects. diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index d26f6417ea31..091de61dc267 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -1,5 +1,5 @@ /* - * acm.c Version 0.16 + * acm.c Version 0.18 * * Copyright (c) 1999 Armin Fuerst * Copyright (c) 1999 Pavel Machek @@ -19,6 +19,8 @@ * v0.14 - sized down struct acm * v0.15 - fixed flow control again - characters could be lost * v0.16 - added code for modems with swapped data and control interfaces + * v0.17 - added new style probing + * v0.18 - fixed new style probing for devices with more configurations */ /* @@ -45,14 +47,21 @@ #include #include #include +#include #include #include -#include #include -//#define DEBUG +#undef DEBUG #include #include +/* + * Version Information + */ +#define DRIVER_VERSION "v0.18" +#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik" +#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" + /* * CMSPAR, some architectures can't have space and mark parity. */ @@ -145,7 +154,7 @@ struct acm { static struct usb_driver acm_driver; static struct tty_driver acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, /* .... */ }; +static struct acm *acm_table[ACM_TTY_MINORS]; #define ACM_READY(acm) (acm && acm->dev && acm->used) @@ -232,8 +241,14 @@ static void acm_read_bulk(struct urb *urb) dbg("nonzero read bulk status received: %d", urb->status); if (!urb->status & !acm->throttle) { - for (i = 0; i < urb->actual_length && !acm->throttle; i++) + for (i = 0; i < urb->actual_length && !acm->throttle; i++) { + /* if we insert more than TTY_FLIPBUF_SIZE characters, + * we drop them. */ + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } tty_insert_flip_char(tty, data[i], 0); + } tty_flip_buffer_push(tty); } @@ -244,6 +259,7 @@ static void acm_read_bulk(struct urb *urb) } urb->actual_length = 0; + urb->dev = acm->dev; if (usb_submit_urb(urb)) dbg("failed resubmitting read urb"); @@ -292,14 +308,20 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (acm->used++) return 0; + acm->ctrlurb.dev = acm->dev; if (usb_submit_urb(&acm->ctrlurb)) dbg("usb_submit_urb(ctrl irq) failed"); + acm->readurb.dev = acm->dev; if (usb_submit_urb(&acm->readurb)) dbg("usb_submit_urb(read bulk) failed"); acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); + /* force low_latency on so that our tty_push actually forces the data through, + otherwise it is scheduled, and with high data rates data can get lost. */ + tty->low_latency = 1; + return 0; } @@ -340,6 +362,7 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c memcpy(acm->writeurb.transfer_buffer, buf, count); acm->writeurb.transfer_buffer_length = count; + acm->writeurb.dev = acm->dev; if (usb_submit_urb(&acm->writeurb)) dbg("usb_submit_urb(write bulk) failed"); @@ -500,7 +523,7 @@ static void *acm_probe(struct usb_device *dev, unsigned int ifnum) ifcom = cfacm->interface[0].altsetting + 0; ifdata = cfacm->interface[1].altsetting + 0; - if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) { ifcom = cfacm->interface[1].altsetting + 0; ifdata = cfacm->interface[0].altsetting + 0; if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) @@ -563,7 +586,7 @@ static void *acm_probe(struct usb_device *dev, unsigned int ifnum) FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf += readsize, acm->writesize, acm_write_bulk, acm); - + printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); acm_set_control(acm, acm->ctrlout); @@ -679,6 +702,8 @@ static int __init acm_init(void) return -1; } + info(DRIVER_VERSION ":" DRIVER_DESC); + return 0; } @@ -691,5 +716,6 @@ static void __exit acm_exit(void) module_init(acm_init); module_exit(acm_exit); -MODULE_AUTHOR("Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"); -MODULE_DESCRIPTION("USB Abstract Control Model driver for USB modems and ISDN adapters"); +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); + diff --git a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c index a365acb2aca5..7e8d569d2d01 100644 --- a/drivers/usb/bluetooth.c +++ b/drivers/usb/bluetooth.c @@ -1287,8 +1287,7 @@ int usb_bluetooth_init(void) return -1; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c index 510cb1d80d5b..2dff98ddada0 100644 --- a/drivers/usb/dabusb.c +++ b/drivers/usb/dabusb.c @@ -832,8 +832,7 @@ static int __init dabusb_init (void) dbg("dabusb_init: driver registered"); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index 3b403f0e82cd..654399261388 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -507,8 +507,7 @@ int __init usb_dc2xx_init(void) { if (usb_register (&camera_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index da8aae4d8c63..b4d907103f3e 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -35,7 +35,7 @@ static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); static int khubd_pid = 0; /* PID of khubd */ -static int khubd_running = 0; +static DECLARE_MUTEX_LOCKED(khubd_exited); static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) { @@ -76,34 +76,33 @@ static int usb_get_port_status(struct usb_device *dev, int port, void *data) data, sizeof(struct usb_hub_status), HZ); } -/* - * A irq handler returns non-zero to indicate to - * the low-level driver that it wants to be re-activated, - * or zero to say "I'm done". - */ static void hub_irq(struct urb *urb) { struct usb_hub *hub = (struct usb_hub *)urb->context; unsigned long flags; + /* Cause a hub reset after 10 consecutive errors */ if (urb->status) { - if (urb->status != -ENOENT) - dbg("nonzero status in irq %d", urb->status); + if (urb->status == -ENOENT) + return; - return; + dbg("nonzero status in irq %d", urb->status); + + if ((++hub->nerrors < 10) || hub->error) + return; + + hub->error = urb->status; } + hub->nerrors = 0; + /* Something happened, let khubd figure it out */ - if (waitqueue_active(&khubd_wait)) { - /* Add the hub to the event queue */ - spin_lock_irqsave(&hub_event_lock, flags); - if (hub->event_list.next == &hub->event_list) { - list_add(&hub->event_list, &hub_event_list); - /* Wake up khubd */ - wake_up(&khubd_wait); - } - spin_unlock_irqrestore(&hub_event_lock, flags); + spin_lock_irqsave(&hub_event_lock, flags); + if (list_empty(&hub->event_list)) { + list_add(&hub->event_list, &hub_event_list); + wake_up(&khubd_wait); } + spin_unlock_irqrestore(&hub_event_lock, flags); } static void usb_hub_power_on(struct usb_hub *hub) @@ -114,40 +113,46 @@ static void usb_hub_power_on(struct usb_hub *hub) dbg("enabling power on all ports"); for (i = 0; i < hub->nports; i++) usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); + + /* Wait for power to be enabled */ + wait_ms(hub->descriptor->bPwrOn2PwrGood * 2); } -static int usb_hub_configure(struct usb_hub *hub) +static int usb_hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { struct usb_device *dev = hub->dev; - unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap; - struct usb_hub_descriptor *descriptor; - struct usb_descriptor_header *header; - struct usb_hub_status *hubsts; - int i, ret; + struct usb_hub_status hubstatus; + char portstr[USB_MAXCHILDREN + 1]; + unsigned int pipe; + int i, maxp, ret; + + hub->descriptor = kmalloc(HUB_DESCRIPTOR_MAX_SIZE, GFP_KERNEL); + if (!hub->descriptor) { + err("Unable to kmalloc %d bytes for hub descriptor", HUB_DESCRIPTOR_MAX_SIZE); + return -1; + } /* Request the entire hub descriptor. */ - header = (struct usb_descriptor_header *)buffer; - ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer)); - /* is large enough for a hub with 127 ports; + ret = usb_get_hub_descriptor(dev, hub->descriptor, HUB_DESCRIPTOR_MAX_SIZE); + /* descriptor> is large enough for a hub with 127 ports; * the hub can/will return fewer bytes here. */ if (ret < 0) { err("Unable to get hub descriptor (err = %d)", ret); + kfree(hub->descriptor); return -1; } - bitmap = kmalloc(header->bLength, GFP_KERNEL); - if (!bitmap) { - err("Unable to kmalloc %d bytes for bitmap", header->bLength); - return -1; - } - - memcpy (bitmap, buffer, header->bLength); - descriptor = (struct usb_hub_descriptor *)bitmap; + le16_to_cpus(&hub->descriptor->wHubCharacteristics); - hub->nports = dev->maxchild = descriptor->bNbrPorts; + hub->nports = dev->maxchild = hub->descriptor->bNbrPorts; info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s"); - switch (descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { + if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) + dbg("part of a compound device"); + else + dbg("standalone hub"); + + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: dbg("ganged power switching"); break; @@ -160,12 +165,7 @@ static int usb_hub_configure(struct usb_hub *hub) break; } - if (descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) - dbg("part of a compound device"); - else - dbg("standalone hub"); - - switch (descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { + switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: dbg("global over-current protection"); break; @@ -178,28 +178,55 @@ static int usb_hub_configure(struct usb_hub *hub) break; } - dbg("power on to power good time: %dms", descriptor->bPwrOn2PwrGood * 2); - dbg("hub controller current requirement: %dmA", descriptor->bHubContrCurrent); + dbg("power on to power good time: %dms", hub->descriptor->bPwrOn2PwrGood * 2); + dbg("hub controller current requirement: %dmA", hub->descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) - dbg("port %d is%s removable", i + 1, - bitmap[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) - ? " not" : ""); + portstr[i] = hub->descriptor->bitmap[((i + 1) / 8)] & (1 << ((i + 1) % 8)) ? 'F' : 'R'; + portstr[dev->maxchild] = 0; - kfree(bitmap); + dbg("port removable status: %s", portstr); - ret = usb_get_hub_status(dev, buffer); + ret = usb_get_hub_status(dev, &hubstatus); if (ret < 0) { err("Unable to get hub status (err = %d)", ret); + kfree(hub->descriptor); return -1; } - hubsts = (struct usb_hub_status *)buffer; + le16_to_cpus(&hubstatus.wHubStatus); + dbg("local power source is %s", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); + (hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); dbg("%sover-current condition exists", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); + (hubstatus.wHubStatus & HUB_STATUS_OVERCURRENT) ? "" : "no "); + + /* Start the interrupt endpoint */ + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + if (maxp > sizeof(hub->buffer)) + maxp = sizeof(hub->buffer); + + hub->urb = usb_alloc_urb(0); + if (!hub->urb) { + err("couldn't allocate interrupt urb"); + kfree(hub->descriptor); + return -1; + } + + FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + ret = usb_submit_urb(hub->urb); + if (ret) { + err("usb_submit_urb failed (%d)", ret); + kfree(hub->descriptor); + return -1; + } + + /* Wake up khubd */ + wake_up(&khubd_wait); usb_hub_power_on(hub); @@ -212,8 +239,6 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; unsigned long flags; - unsigned int pipe; - int maxp, ret; interface = &dev->actconfig->interface[i].altsetting[0]; @@ -224,24 +249,32 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) /* Some hubs have a subclass of 1, which AFAICT according to the */ /* specs is not defined, but it works */ if ((interface->bInterfaceSubClass != 0) && - (interface->bInterfaceSubClass != 1)) + (interface->bInterfaceSubClass != 1)) { + err("invalid subclass (%d) for USB hub device #%d", + interface->bInterfaceSubClass, dev->devnum); return NULL; + } /* Multiple endpoints? What kind of mutant ninja-hub is this? */ - if (interface->bNumEndpoints != 1) + if (interface->bNumEndpoints != 1) { + err("invalid bNumEndpoints (%d) for USB hub device #%d", + interface->bNumEndpoints, dev->devnum); return NULL; + } endpoint = &interface->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { - err("Device is hub class, but has output endpoint?"); + err("Device #%d is hub class, but has output endpoint?", + dev->devnum); return NULL; } /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) { - err("Device is hub class, but has endpoint other than interrupt?"); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { + err("Device #%d is hub class, but has endpoint other than interrupt?", + dev->devnum); return NULL; } @@ -258,6 +291,7 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) INIT_LIST_HEAD(&hub->event_list); hub->dev = dev; + atomic_set(&hub->refcnt, 1); /* Record the new hub's existence */ spin_lock_irqsave(&hub_event_lock, flags); @@ -265,34 +299,11 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) list_add(&hub->hub_list, &hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); - if (usb_hub_configure(hub) >= 0) { - pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - if (maxp > sizeof(hub->buffer)) - maxp = sizeof(hub->buffer); - - hub->urb = usb_alloc_urb(0); - if (!hub->urb) { - err("couldn't allocate interrupt urb"); - goto fail; - } - - FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, - hub, endpoint->bInterval); - ret = usb_submit_urb(hub->urb); - if (ret) { - err("usb_submit_urb failed (%d)", ret); - goto fail; - } - - /* Wake up khubd */ - wake_up(&khubd_wait); - } + if (usb_hub_configure(hub, endpoint) >= 0) + return hub; - return hub; + err("hub configuration failed for device #%d", dev->devnum); -fail: /* free hub, but first clean up its list. */ spin_lock_irqsave(&hub_event_lock, flags); @@ -309,11 +320,34 @@ fail: return NULL; } +static void hub_get(struct usb_hub *hub) +{ + atomic_inc(&hub->refcnt); +} + +static void hub_put(struct usb_hub *hub) +{ + if (atomic_dec_and_test(&hub->refcnt)) { + if (hub->descriptor) { + kfree(hub->descriptor); + hub->descriptor = NULL; + } + + kfree(hub); + } +} + static void hub_disconnect(struct usb_device *dev, void *ptr) { struct usb_hub *hub = (struct usb_hub *)ptr; unsigned long flags; + if (hub->urb) { + usb_unlink_urb(hub->urb); + usb_free_urb(hub->urb); + hub->urb = NULL; + } + spin_lock_irqsave(&hub_event_lock, flags); /* Delete it and then reset it */ @@ -324,17 +358,10 @@ static void hub_disconnect(struct usb_device *dev, void *ptr) spin_unlock_irqrestore(&hub_event_lock, flags); - if (hub->urb) { - usb_unlink_urb(hub->urb); - usb_free_urb(hub->urb); - hub->urb = NULL; - } - - /* Free the memory */ - kfree(hub); + hub_put(hub); } -static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data) +static int hub_ioctl(struct usb_device *hub, unsigned int code, void *user_data) { /* assert ifno == 0 (part of hub spec) */ switch (code) { @@ -343,19 +370,19 @@ static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data unsigned long flags; int i; - spin_lock_irqsave (&hub_event_lock, flags); + spin_lock_irqsave(&hub_event_lock, flags); if (hub->devnum <= 0) info->nports = 0; else { info->nports = hub->maxchild; for (i = 0; i < info->nports; i++) { - if (hub->children [i] == NULL) - info->port [i] = 0; + if (hub->children[i] == NULL) + info->port[i] = 0; else - info->port [i] = hub->children [i]->devnum; + info->port[i] = hub->children[i]->devnum; } } - spin_unlock_irqrestore (&hub_event_lock, flags); + spin_unlock_irqrestore(&hub_event_lock, flags); return info->nports + 1; } @@ -365,121 +392,262 @@ static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data } } -static void usb_hub_port_connect_change(struct usb_device *hub, int port) +static int usb_hub_reset(struct usb_hub *hub) { - struct usb_device *usb; - struct usb_port_status portsts; - unsigned short portstatus, portchange; - int ret, tries; - - wait_ms(100); + struct usb_device *dev = hub->dev; + int i; - ret = usb_get_port_status(hub, port + 1, &portsts); - if (ret < 0) { - err("get_port_status(%d) failed (err = %d)", port + 1, ret); - return; + /* Disconnect any attached devices */ + for (i = 0; i < hub->nports; i++) { + if (dev->children[i]) + usb_disconnect(&dev->children[i]); } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - dbg("portstatus %x, change %x, %s", portstatus, portchange, - portstatus&(1<urb) + usb_unlink_urb(hub->urb); + else + return -1; - /* Clear the connection change status */ - usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); + if (usb_reset_device(dev)) + return -1; - /* Disconnect any existing devices under this port */ - if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE)))|| (hub->children[port])) { - usb_disconnect(&hub->children[port]); - /* Return now if nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return; + hub->urb->dev = dev; + if (usb_submit_urb(hub->urb)) + return -1; + + usb_hub_power_on(hub); + + return 0; +} + +static void usb_hub_disconnect(struct usb_device *dev) +{ + struct usb_device *parent = dev->parent; + int i; + + /* Find the device pointer to disconnect */ + if (parent) { + for (i = 0; i < parent->maxchild; i++) { + if (parent->children[i] == dev) { + usb_disconnect(&parent->children[i]); + return; + } + } } - wait_ms(400); - down(&usb_address0_sem); + err("cannot disconnect hub %d", dev->devnum); +} -#define MAX_TRIES 5 - /* Reset the port */ - for (tries = 0; tries < MAX_TRIES ; tries++) { - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); - wait_ms(200); +#define HUB_RESET_TRIES 5 +#define HUB_PROBE_TRIES 2 +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 +static int usb_hub_port_wait_reset(struct usb_device *hub, int port, + struct usb_device *dev, unsigned int delay) +{ + int delay_time, ret; + struct usb_port_status portsts; + unsigned short portchange, portstatus; + + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { + /* wait to give the device a chance to reset */ + wait_ms(delay); + + /* read and decode port status */ ret = usb_get_port_status(hub, port + 1, &portsts); if (ret < 0) { err("get_port_status(%d) failed (err = %d)", port + 1, ret); - goto out; + return -1; } portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); - dbg("portstatus %x, change %x, %s", portstatus ,portchange, - portstatus&(1<slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + return 0; + } - wait_ms(200); + /* switch to the long delay after two short delay failures */ + if (delay_time >= 2 * HUB_SHORT_RESET_TIME) + delay = HUB_LONG_RESET_TIME; + + dbg("port %d of hub %d not reset yet, waiting %dms", port + 1, + hub->devnum, delay); } - if (tries >= MAX_TRIES) { - err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES); - err("Maybe the USB cable is bad?"); - goto out; + return -1; +} + +static int usb_hub_port_reset(struct usb_device *hub, int port, + struct usb_device *dev, unsigned int delay) +{ + int i; + + /* Reset the port */ + for (i = 0; i < HUB_RESET_TRIES; i++) { + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + + /* return success if the port reset OK */ + if (!usb_hub_port_wait_reset(hub, port, dev, delay)) { + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + return 0; + } + + dbg("port %d of hub %d not enabled, trying reset again...", + port + 1, hub->devnum); + delay = HUB_LONG_RESET_TIME; } - usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + err("Cannot enable port %i of hub %d, disabling port.", + port + 1, hub->devnum); + err("Maybe the USB cable is bad?"); - /* Allocate a new device struct for it */ - usb = usb_alloc_dev(hub, hub->bus); - if (!usb) { - err("couldn't allocate usb_device"); - goto out; + return -1; +} + +void usb_hub_port_disable(struct usb_device *hub, int port) +{ + int ret; + + ret = usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE); + if (ret) + err("cannot disable port %d of hub %d (err = %d)", + port + 1, hub->devnum, ret); +} + +static void usb_hub_port_connect_change(struct usb_device *hub, int port, + struct usb_port_status *portsts) +{ + struct usb_device *dev; + unsigned short portstatus, portchange; + unsigned int delay = HUB_SHORT_RESET_TIME; + int i; + char *portstr, *tempstr; + + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, + portchange, portstatus & (1 << USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); + + /* Clear the connection change status */ + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (hub->children[port]) + usb_disconnect(&hub->children[port]); + + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + if (portstatus & USB_PORT_STAT_ENABLE) + usb_hub_port_disable(hub, port); + + return; } - usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; + /* Some low speed devices have problems with the quick delay, so */ + /* be a bit pessimistic with those devices. RHbug #23670 */ + if (portstatus & USB_PORT_STAT_LOW_SPEED) { + wait_ms(400); + delay = HUB_LONG_RESET_TIME; + } - hub->children[port] = usb; + down(&usb_address0_sem); - /* Find a new device ID for it */ - usb_connect(usb); + tempstr = kmalloc(1024, GFP_KERNEL); + portstr = kmalloc(1024, GFP_KERNEL); - /* Run it through the hoops (find a driver, etc) */ - ret = usb_new_device(usb); - if (ret) { - /* Try resetting the device. Windows does this and it */ - /* gets some devices working correctly */ - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + for (i = 0; i < HUB_PROBE_TRIES; i++) { + struct usb_device *pdev, *cdev; + + /* Allocate a new device struct */ + dev = usb_alloc_dev(hub, hub->bus); + if (!dev) { + err("couldn't allocate usb_device"); + break; + } - ret = usb_new_device(usb); - if (ret) { - usb_disconnect(&hub->children[port]); + hub->children[port] = dev; - /* Woops, disable the port */ - dbg("hub: disabling port %d", port + 1); - usb_clear_port_feature(hub, port + 1, - USB_PORT_FEAT_ENABLE); + /* Reset the device */ + if (usb_hub_port_reset(hub, port, dev, delay)) { + usb_free_dev(dev); + break; } + + /* Find a new device ID for it */ + usb_connect(dev); + + /* Create a readable topology string */ + cdev = dev; + pdev = dev->parent; + if (portstr && tempstr) { + portstr[0] = 0; + while (pdev) { + int port; + + for (port = 0; port < pdev->maxchild; port++) + if (pdev->children[port] == cdev) + break; + + strcpy(tempstr, portstr); + if (!strlen(tempstr)) + sprintf(portstr, "%d", port + 1); + else + sprintf(portstr, "%d/%s", port + 1, tempstr); + + cdev = pdev; + pdev = pdev->parent; + } + info("USB new device connect on bus%d/%s, assigned device number %d", + dev->bus->busnum, portstr, dev->devnum); + } else + info("USB new device connect on bus%d, assigned device number %d", + dev->bus->busnum, dev->devnum); + + /* Run it through the hoops (find a driver, etc) */ + if (!usb_new_device(dev)) + goto done; + + /* Free the configuration if there was an error */ + usb_free_dev(dev); + + /* Switch to a long reset time */ + delay = HUB_LONG_RESET_TIME; } -out: + hub->children[port] = NULL; + usb_hub_port_disable(hub, port); +done: up(&usb_address0_sem); + if (portstr) + kfree(portstr); + if (tempstr) + kfree(tempstr); } static void usb_hub_events(void) { unsigned long flags; - int i; struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; struct usb_hub_status hubsts; unsigned short hubstatus, hubchange; + int i, ret; /* * We restart the list everytime to avoid a deadlock with @@ -491,7 +659,7 @@ static void usb_hub_events(void) spin_lock_irqsave(&hub_event_lock, flags); if (list_empty(&hub_event_list)) - goto he_unlock; + break; /* Grab the next entry from the beginning of the list */ tmp = hub_event_list.next; @@ -502,14 +670,30 @@ static void usb_hub_events(void) list_del(tmp); INIT_LIST_HEAD(tmp); + hub_get(hub); spin_unlock_irqrestore(&hub_event_lock, flags); + if (hub->error) { + dbg("resetting hub %d for error %d", dev->devnum, hub->error); + + if (usb_hub_reset(hub)) { + err("error resetting hub %d - disconnecting", dev->devnum); + usb_hub_disconnect(dev); + hub_put(hub); + continue; + } + + hub->nerrors = 0; + hub->error = 0; + } + for (i = 0; i < hub->nports; i++) { struct usb_port_status portsts; unsigned short portstatus, portchange; - if (usb_get_port_status(dev, i + 1, &portsts) < 0) { - err("get_port_status failed"); + ret = usb_get_port_status(dev, i + 1, &portsts); + if (ret < 0) { + err("get_port_status failed (err = %d)", ret); continue; } @@ -519,27 +703,27 @@ static void usb_hub_events(void) if (portchange & USB_PORT_STAT_C_CONNECTION) { dbg("port %d connection change", i + 1); - usb_hub_port_connect_change(dev, i); - } - - if (portchange & USB_PORT_STAT_C_ENABLE) { + usb_hub_port_connect_change(dev, i, &portsts); + } else if (portchange & USB_PORT_STAT_C_ENABLE) { dbg("port %d enable change, status %x", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); - // EM interference sometimes causes bad shielded USB devices to - // be shutdown by the hub, this hack enables them again. - // Works at least with mouse driver. + /* + * EM interference sometimes causes bad shielded USB devices to + * be shutdown by the hub, this hack enables them again. + * Works at least with mouse driver. + */ if (!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { err("already running port %i disabled by hub (EMI?), re-enabling...", i + 1); - usb_hub_port_connect_change(dev, i); + usb_hub_port_connect_change(dev, i, &portsts); } } - if (portstatus & USB_PORT_STAT_SUSPEND) { + if (portchange & USB_PORT_STAT_C_SUSPEND) { dbg("port %d suspend change", i + 1); - usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_SUSPEND); } if (portchange & USB_PORT_STAT_C_OVERCURRENT) { @@ -555,9 +739,9 @@ static void usb_hub_events(void) } /* end for i */ /* deal with hub status changes */ - if (usb_get_hub_status(dev, &hubsts) < 0) { + if (usb_get_hub_status(dev, &hubsts) < 0) err("get_hub_status failed"); - } else { + else { hubstatus = le16_to_cpup(&hubsts.wHubStatus); hubchange = le16_to_cpup(&hubsts.wHubChange); if (hubchange & HUB_CHANGE_LOCAL_POWER) { @@ -566,21 +750,19 @@ static void usb_hub_events(void) } if (hubchange & HUB_CHANGE_OVERCURRENT) { dbg("hub overcurrent change"); - wait_ms(500); //Cool down + wait_ms(500); /* Cool down */ usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); usb_hub_power_on(hub); } } + hub_put(hub); } /* end while (1) */ -he_unlock: spin_unlock_irqrestore(&hub_event_lock, flags); } static int usb_hub_thread(void *__hub) { - khubd_running = 1; - lock_kernel(); /* @@ -602,11 +784,12 @@ static int usb_hub_thread(void *__hub) } while (!signal_pending(current)); dbg("usb_hub_thread exiting"); - khubd_running = 0; + up(&khubd_exited); return 0; } + static struct usb_driver hub_driver = { name: "hub", probe: hub_probe, @@ -636,6 +819,7 @@ int usb_hub_init(void) /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); + err("failed to start usb_hub_thread"); return -1; } @@ -646,21 +830,11 @@ void usb_hub_cleanup(void) /* Kill the thread */ ret = kill_proc(khubd_pid, SIGTERM, 1); - if (!ret) { - /* Wait 10 seconds */ - int count = 10 * HZ; - while (khubd_running && --count) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - - if (!count) - err("giving up on killing khubd"); - } + down(&khubd_exited); /* - * Hub resources are freed for us by usb_deregister. It + * Hub resources are freed for us by usb_deregister. It calls * usb_driver_purge on every device which in turn calls that * devices disconnect function if it is using this driver. * The hub_disconnect function takes care of releasing the @@ -701,23 +875,23 @@ int usb_reset_device(struct usb_device *dev) down(&usb_address0_sem); /* Send a reset to the device */ - usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET); - - wait_ms(200); - - usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET); + if (usb_hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) { + usb_hub_port_disable(parent, port); + up(&usb_address0_sem); + return(-ENODEV); + } /* Reprogram the Address */ ret = usb_set_address(dev); if (ret < 0) { err("USB device not accepting new address (error=%d)", ret); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - dev->devnum = -1; + usb_hub_port_disable(parent, port); up(&usb_address0_sem); return ret; } - wait_ms(10); /* Let the SET_ADDRESS settle */ + /* Let the SET_ADDRESS settle */ + wait_ms(10); up(&usb_address0_sem); @@ -748,7 +922,7 @@ int usb_reset_device(struct usb_device *dev) if (ret < 0) err("unable to get device descriptor (error=%d)", ret); else - err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret); + err("USB device descriptor short read (expected %Zi, got %i)", sizeof(dev->descriptor), ret); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; @@ -768,30 +942,23 @@ int usb_reset_device(struct usb_device *dev) usb_set_maxpacket(dev); return 1; - } else { - ret = usb_set_configuration(dev, - dev->actconfig->bConfigurationValue); - if (ret < 0) { - err("failed to set active configuration (error=%d)", - ret); - return ret; - } + } - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { - struct usb_interface *intf = - &dev->actconfig->interface[i]; - struct usb_interface_descriptor *as = - &intf->altsetting[intf->act_altsetting]; + ret = usb_set_configuration(dev, dev->actconfig->bConfigurationValue); + if (ret < 0) { + err("failed to set active configuration (error=%d)", ret); + return ret; + } - ret = usb_set_interface(dev, as->bInterfaceNumber, - as->bAlternateSetting); - if (ret < 0) { - err("failed to set active alternate setting for interface %d (error=%d)", i, ret); - return ret; - } - } + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = &dev->actconfig->interface[i]; + struct usb_interface_descriptor *as = &intf->altsetting[intf->act_altsetting]; - return 0; + ret = usb_set_interface(dev, as->bInterfaceNumber, as->bAlternateSetting); + if (ret < 0) { + err("failed to set active alternate setting for interface %d (error=%d)", i, ret); + return ret; + } } return 0; diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index b2c3dffeb091..724f3cee3304 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -82,48 +82,34 @@ struct usb_hub_descriptor { __u16 wHubCharacteristics; __u8 bPwrOn2PwrGood; __u8 bHubContrCurrent; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length bitmaps that hold max 256 entries, but for now they're ignored */ + __u8 bitmap[0]; } __attribute__ ((packed)); struct usb_device; -typedef enum { - USB_PORT_UNPOWERED = 0, /* Default state */ - USB_PORT_POWERED, /* When we've put power to it */ - USB_PORT_ENABLED, /* When it's been enabled */ - USB_PORT_DISABLED, /* If it's been disabled */ - USB_PORT_ADMINDISABLED, /* Forced down */ -} usb_hub_port_state; - -struct usb_hub_port { - usb_hub_port_state cstate; /* Configuration state */ - - struct usb_device *child; /* Device attached to this port */ - - struct usb_hub *parent; /* Parent hub */ -}; - struct usb_hub { - /* Device structure */ struct usb_device *dev; - /* Interrupt polling pipe */ - struct urb *urb; + struct urb *urb; /* Interrupt polling pipe */ char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 bit for hub status change */ /* and add 7 bits to round up to byte boundary */ + int error; + int nerrors; - /* List of hubs */ struct list_head hub_list; - /* Temporary event list */ struct list_head event_list; /* Number of ports on the hub */ int nports; - struct usb_hub_port ports[0]; /* Dynamically allocated */ + struct usb_hub_descriptor *descriptor; + + atomic_t refcnt; }; #endif diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c index 11b9e3876283..aaae2689f19d 100644 --- a/drivers/usb/mdc800.c +++ b/drivers/usb/mdc800.c @@ -928,8 +928,7 @@ int __init usb_mdc800_init (void) if (usb_register (&mdc800_usb_driver) < 0) goto cleanup_on_fail; - info (DRIVER_VERSION " " DRIVER_AUTHOR); - info (DRIVER_DESC); + info (DRIVER_VERSION " " DRIVER_DESC); return 0; diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index be2a88164095..64880abecaa1 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -3442,8 +3442,7 @@ static int __init usb_ov511_init(void) if (usb_register(&ov511_driver) < 0) return -1; - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 02c1eff8056a..d382ae59a8ad 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -40,7 +40,6 @@ */ -#include #include #include #include @@ -48,163 +47,40 @@ #include #include #include +#include +#include "pegasus.h" - -static const char *version = __FILE__ ": v0.4.13 2000/10/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)"; - +/* + * Version Information + */ +#define DRIVER_VERSION "v0.4.18 2001/03/18 (C) 1999-2000" +#define DRIVER_AUTHOR "Petko Manolov " +#define DRIVER_DESC "ADMtek AN986 Pegasus USB Ethernet driver" #define PEGASUS_USE_INTR - - -#define PEGASUS_II 0x80000000 -#define HAS_HOME_PNA 0x40000000 - -#define PEGASUS_MTU 1500 -#define PEGASUS_MAX_MTU 1536 - -#define EPROM_WRITE 0x01 -#define EPROM_READ 0x02 -#define EPROM_DONE 0x04 -#define EPROM_WR_ENABLE 0x10 -#define EPROM_LOAD 0x20 - -#define MII_BMCR 0x00 -#define MII_BMSR 0x01 -#define BMSR_MEDIA 0x7808 -#define MII_ANLPA 0x05 -#define ANLPA_100TX_FD 0x0100 -#define ANLPA_100TX_HD 0x0080 -#define ANLPA_10T_FD 0x0040 -#define ANLPA_10T_HD 0x0020 -#define PHY_DONE 0x80 -#define PHY_READ 0x40 -#define PHY_WRITE 0x20 -#define DEFAULT_GPIO_RESET 0x24 -#define LINKSYS_GPIO_RESET 0x24 -#define DEFAULT_GPIO_SET 0x26 - -#define PEGASUS_PRESENT 0x00000001 -#define PEGASUS_RUNNING 0x00000002 -#define PEGASUS_TX_BUSY 0x00000004 -#define PEGASUS_RX_BUSY 0x00000008 -#define CTRL_URB_RUNNING 0x00000010 -#define CTRL_URB_SLEEP 0x00000020 -#define PEGASUS_UNPLUG 0x00000040 -#define ETH_REGS_CHANGE 0x40000000 -#define ETH_REGS_CHANGED 0x80000000 - -#define RX_MULTICAST 2 -#define RX_PROMISCUOUS 4 - -#define REG_TIMEOUT (HZ) -#define PEGASUS_TX_TIMEOUT (HZ*10) - -#define TX_UNDERRUN 0x80 -#define EXCESSIVE_COL 0x40 -#define LATE_COL 0x20 -#define NO_CARRIER 0x10 -#define LOSS_CARRIER 0x08 -#define JABBER_TIMEOUT 0x04 - -#define PEGASUS_REQT_READ 0xc0 -#define PEGASUS_REQT_WRITE 0x40 -#define PEGASUS_REQ_GET_REGS 0xf0 -#define PEGASUS_REQ_SET_REGS 0xf1 -#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS -#define NUM_CTRL_URBS 0x10 -#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) - -enum pegasus_registers { - EthCtrl0 = 0, - EthCtrl1 = 1, - EthCtrl2 = 2, - EthID = 0x10, - Reg1d = 0x1d, - EpromOffset = 0x20, - EpromData = 0x21, /* 0x21 low, 0x22 high byte */ - EpromCtrl = 0x23, - PhyAddr = 0x25, - PhyData = 0x26, /* 0x26 low, 0x27 high byte */ - PhyCtrl = 0x28, - UsbStst = 0x2a, - EthTxStat0 = 0x2b, - EthTxStat1 = 0x2c, - EthRxStat = 0x2d, - Reg7b = 0x7b, - Gpio0 = 0x7e, - Gpio1 = 0x7f, - Reg81 = 0x81, -}; - - -typedef struct pegasus { - struct usb_device *usb; - struct net_device *net; - struct net_device_stats stats; - unsigned flags; - unsigned features; - int intr_interval; - struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; - devrequest dr; - wait_queue_head_t ctrl_wait; - struct semaphore ctrl_sem; - unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(intr_buff[8]); - __u8 eth_regs[4]; - __u8 phy; - __u8 gpio_res; -} pegasus_t; - -struct usb_eth_dev { - char *name; - __u16 vendor; - __u16 device; - __u32 private; /* LSB is gpio reset value */ -}; - +#define PEGASUS_WRITE_EEPROM static int loopback = 0; static int mii_mode = 0; static int multicast_filter_limit = 32; +static struct usb_eth_dev usb_dev_id[] = { +#define PEGASUS_DEV(pn, vid, pid, flags) \ + {name:pn, vendor:vid, device:pid, private:flags}, +#include "pegasus.h" +#undef PEGASUS_DEV + {NULL, 0, 0, 0} +}; + -MODULE_AUTHOR("Petko Manolov "); -MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_PARM(loopback, "i"); -MODULE_PARM(mode, "i"); +MODULE_PARM(mii_mode, "i"); MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); -MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0"); +MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); -static struct usb_eth_dev usb_dev_id[] = { - {"Billionton USB-100", 0x08dd, 0x0986, DEFAULT_GPIO_RESET}, - {"Corega FEter USB-TX", 0x7aa, 0x0004, DEFAULT_GPIO_RESET}, - {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, DEFAULT_GPIO_RESET}, - {"D-Link DSB-650TX", 0x2001, 0x4001, LINKSYS_GPIO_RESET}, - {"D-Link DSB-650TX", 0x2001, 0x4002, LINKSYS_GPIO_RESET}, - {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, - HAS_HOME_PNA | DEFAULT_GPIO_RESET}, - {"D-Link DSB-650", 0x2001, 0xabc1, DEFAULT_GPIO_RESET}, - {"D-Link DU-E10", 0x07b8, 0xabc1, DEFAULT_GPIO_RESET}, - {"D-Link DU-E100", 0x07b8, 0x4002, DEFAULT_GPIO_RESET}, - {"Linksys USB10TX", 0x066b, 0x2202, LINKSYS_GPIO_RESET}, - {"Linksys USB100TX", 0x066b, 0x2203, LINKSYS_GPIO_RESET}, - {"Linksys USB100TX", 0x066b, 0x2204, HAS_HOME_PNA | LINKSYS_GPIO_RESET}, - {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, LINKSYS_GPIO_RESET}, - {"SMC 202 USB Ethernet", 0x0707, 0x0200, DEFAULT_GPIO_RESET}, - {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, - HAS_HOME_PNA | DEFAULT_GPIO_RESET}, - {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, - DEFAULT_GPIO_RESET}, - {"IO DATA USB ET/TX", 0x04bb, 0x0904, DEFAULT_GPIO_RESET}, - {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, DEFAULT_GPIO_RESET}, - {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, DEFAULT_GPIO_RESET}, - {"ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511, - PEGASUS_II | DEFAULT_GPIO_RESET}, - {NULL, 0, 0, 0} -}; - static int update_eth_regs_async( pegasus_t * ); /* Aargh!!! I _really_ hate such tweaks */ @@ -242,29 +118,35 @@ static void ctrl_callback( urb_t *urb ) static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; + DECLARE_WAITQUEUE(wait, current); - if ( pegasus->flags & ETH_REGS_CHANGED ) { + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); } pegasus->dr.requesttype = PEGASUS_REQT_READ; pegasus->dr.request = PEGASUS_REQ_GET_REGS; - pegasus->dr.value = 0; + pegasus->dr.value = cpu_to_le16 (0); pegasus->dr.index = cpu_to_le16p(&indx); - pegasus->dr.length = - pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p(&size); + pegasus->dr.length = cpu_to_le16p(&size); + pegasus->ctrl_urb.transfer_buffer_length = size; FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, data, size, ctrl_callback, pegasus ); + add_wait_queue( &pegasus->ctrl_wait, &wait ); + set_current_state( TASK_INTERRUPTIBLE ); + pegasus->flags |= CTRL_URB_SLEEP; + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { err( __FUNCTION__ " BAD CTRLs %d", ret); goto out; } - pegasus->flags |= CTRL_URB_SLEEP; - interruptible_sleep_on( &pegasus->ctrl_wait ); + + schedule(); + remove_wait_queue( &pegasus->ctrl_wait, &wait ); out: return ret; } @@ -273,29 +155,35 @@ out: static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; + DECLARE_WAITQUEUE(wait, current); - if ( pegasus->flags & ETH_REGS_CHANGED ) { + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP ; interruptible_sleep_on( &pegasus->ctrl_wait ); } pegasus->dr.requesttype = PEGASUS_REQT_WRITE; pegasus->dr.request = PEGASUS_REQ_SET_REGS; - pegasus->dr.value = 0; + pegasus->dr.value = cpu_to_le16 (0); pegasus->dr.index = cpu_to_le16p( &indx ); - pegasus->dr.length = - pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p( &size ); + pegasus->dr.length = cpu_to_le16p( &size ); + pegasus->ctrl_urb.transfer_buffer_length = size; FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, data, size, ctrl_callback, pegasus ); + + add_wait_queue( &pegasus->ctrl_wait, &wait ); + set_current_state( TASK_INTERRUPTIBLE ); + pegasus->flags |= CTRL_URB_SLEEP; if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { err( __FUNCTION__ " BAD CTRL %d", ret); return ret; } - pegasus->flags |= CTRL_URB_SLEEP; - interruptible_sleep_on( &pegasus->ctrl_wait ); + + schedule(); + remove_wait_queue( &pegasus->ctrl_wait, &wait ); return ret; } @@ -304,29 +192,37 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) { int ret; - - if ( pegasus->flags & ETH_REGS_CHANGED ) { + __u16 dat = data; + DECLARE_WAITQUEUE(wait, current); + + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); } pegasus->dr.requesttype = PEGASUS_REQT_WRITE; pegasus->dr.request = PEGASUS_REQ_SET_REG; - pegasus->dr.value = data; + pegasus->dr.value = cpu_to_le16p( &dat); pegasus->dr.index = cpu_to_le16p( &indx ); - pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1; + pegasus->dr.length = cpu_to_le16( 1 ); + pegasus->ctrl_urb.transfer_buffer_length = 1; FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, &data, 1, ctrl_callback, pegasus ); + add_wait_queue( &pegasus->ctrl_wait, &wait ); + set_current_state( TASK_INTERRUPTIBLE ); + pegasus->flags |= CTRL_URB_SLEEP; + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { err( __FUNCTION__ " BAD CTRL %d", ret); return ret; } - pegasus->flags |= CTRL_URB_SLEEP; - interruptible_sleep_on( &pegasus->ctrl_wait ); - + + schedule(); + remove_wait_queue( &pegasus->ctrl_wait, &wait ); + return ret; } @@ -338,8 +234,8 @@ static int update_eth_regs_async( pegasus_t *pegasus ) pegasus->dr.requesttype = PEGASUS_REQT_WRITE; pegasus->dr.request = PEGASUS_REQ_SET_REGS; pegasus->dr.value = 0; - pegasus->dr.index = EthCtrl0; - pegasus->dr.length = + pegasus->dr.index = cpu_to_le16(EthCtrl0); + pegasus->dr.length = cpu_to_le16(3); pegasus->ctrl_urb.transfer_buffer_length = 3; FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, @@ -354,11 +250,12 @@ static int update_eth_regs_async( pegasus_t *pegasus ) } -static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) +static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) { int i; __u8 data[4] = { phy, 0, 0, indx }; - + __u16 regdi; + set_register( pegasus, PhyCtrl, 0 ); set_registers( pegasus, PhyAddr, sizeof(data), data ); set_register( pegasus, PhyCtrl, (indx | PHY_READ) ); @@ -368,7 +265,8 @@ static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) break; } if ( i < REG_TIMEOUT ) { - get_registers( pegasus, PhyData, 2, regd ); + get_registers( pegasus, PhyData, 2, ®di ); + *regd = le16_to_cpu(regdi); return 0; } warn( __FUNCTION__ " failed" ); @@ -377,7 +275,7 @@ static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) } -static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) +static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) { int i; __u8 data[4] = { phy, 0, 0, indx }; @@ -401,18 +299,22 @@ static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata ) { - int i, tmp; - + int i; + __u8 tmp; + __u16 retdatai; + set_register( pegasus, EpromCtrl, 0 ); set_register( pegasus, EpromOffset, index ); set_register( pegasus, EpromCtrl, EPROM_READ); + for ( i=0; i < REG_TIMEOUT; i++ ) { get_registers( pegasus, EpromCtrl, 1, &tmp ); if ( tmp & EPROM_DONE ) break; } if ( i < REG_TIMEOUT ) { - get_registers( pegasus, EpromData, 2, retdata ); + get_registers( pegasus, EpromData, 2, &retdatai ); + *retdata = le16_to_cpu (retdatai); return 0; } warn( __FUNCTION__ " failed" ); @@ -467,9 +369,12 @@ static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data ) static inline void get_node_id( pegasus_t *pegasus, __u8 *id ) { int i; - - for (i = 0; i < 3; i++) - read_eprom_word( pegasus, i, (__u16 *)&id[i*2]); + __u16 w16; + + for (i = 0; i < 3; i++) { + read_eprom_word( pegasus, i, &w16); + ((__u16 *) id)[i] = cpu_to_le16p (&w16); + } } @@ -505,6 +410,15 @@ static inline int reset_mac( pegasus_t *pegasus ) } if ( i == REG_TIMEOUT ) return 1; + + if ( usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || + usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK1 ) { + __u16 auxmode; + + read_mii_word( pegasus, 0, 0x1b, &auxmode ); + write_mii_word( pegasus, 0, 0x1b, auxmode | 4 ); + } + return 0; } @@ -516,13 +430,13 @@ static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) pegasus_t *pegasus = dev->priv; - if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) - return 2; + if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) + return 1; if ( !(bmsr & 0x20) && !loopback ) warn( "%s: link NOT established (0x%x) - check the cable.", dev->name, bmsr ); - if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) - return 4; + if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) + return 2; if ( !(linkpart & 1) ) warn( "link partner stat %x", linkpart ); @@ -536,7 +450,7 @@ static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) data[1] = 0; data[2] = (loopback & 1) ? 0x09 : 0x01; - *(unsigned *)pegasus->eth_regs = *(unsigned *)data; + memcpy( pegasus->eth_regs, data, sizeof(data) ); set_registers( pegasus, EthCtrl0, 3, data ); @@ -562,23 +476,29 @@ static void read_bulk_callback( struct urb *urb ) if ( pegasus->flags & PEGASUS_RX_BUSY ) { pegasus->stats.rx_errors++; + dbg("pegasus Rx busy"); return; } pegasus->flags |= PEGASUS_RX_BUSY; - rx_status = *(int *)(pegasus->rx_buff + count - 4); - - if (urb->status) { - dbg("%s: RX status %d", net->name, urb->status); - goto goon; + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_NORESPONSE: + dbg( "reset MAC" ); + pegasus->flags &= ~PEGASUS_RX_BUSY; + break; + default: + dbg( "%s: RX status %d", net->name, urb->status ); + goto goon; } if ( !count ) goto goon; + rx_status = le32_to_cpu(*(int *)(pegasus->rx_buff + count - 4)); if ( rx_status & 0x000e0000 ) { - - dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000); + dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000); pegasus->stats.rx_errors++; if ( rx_status & 0x060000 ) pegasus->stats.rx_length_errors++; @@ -586,7 +506,6 @@ static void read_bulk_callback( struct urb *urb ) pegasus->stats.rx_crc_errors++; if ( rx_status & 0x100000 ) pegasus->stats.rx_frame_errors++; - goto goon; } @@ -629,6 +548,7 @@ static void write_bulk_callback( struct urb *urb ) if ( urb->status ) info("%s: TX status %d", pegasus->net->name, urb->status); + pegasus->net->trans_start = jiffies; netif_wake_queue( pegasus->net ); } @@ -641,6 +561,16 @@ static void intr_callback( struct urb *urb ) if ( !pegasus ) return; + + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_URB_KILLED: + return; + default: + info("intr status %d", urb->status); + } + d = urb->transfer_buffer; net = pegasus->net; if ( d[0] & 0xfc ) { @@ -654,31 +584,23 @@ static void intr_callback( struct urb *urb ) if ( d[0] & (NO_CARRIER | LOSS_CARRIER) ) pegasus->stats.tx_carrier_errors++; } - switch ( urb->status ) { - case USB_ST_NOERROR: - break; - case USB_ST_URB_KILLED: - break; - default: - info("intr status %d", urb->status); - } } #endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48) static void pegasus_tx_timeout( struct net_device *net ) { pegasus_t *pegasus = net->priv; if ( !pegasus ) return; - - usb_unlink_urb( &pegasus->tx_urb ); + warn("%s: Tx timed out.", net->name); + pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb( &pegasus->tx_urb ); pegasus->stats.tx_errors++; - net->trans_start = jiffies; - - netif_wake_queue( net ); } +#endif static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) @@ -686,17 +608,17 @@ static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) pegasus_t *pegasus = net->priv; int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; int res; - + __u16 l16 = skb->len; + netif_stop_queue( net ); - ((__u16 *)pegasus->tx_buff)[0] = skb->len; + ((__u16 *)pegasus->tx_buff)[0] = cpu_to_le16( l16 ); memcpy(pegasus->tx_buff+2, skb->data, skb->len); FILL_BULK_URB( &pegasus->tx_urb, pegasus->usb, usb_sndbulkpipe(pegasus->usb, 2), pegasus->tx_buff, PEGASUS_MAX_MTU, write_bulk_callback, pegasus ); pegasus->tx_urb.transfer_buffer_length = count; - pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; if ((res = usb_submit_urb(&pegasus->tx_urb))) { warn("failed tx_urb %d", res); pegasus->stats.tx_errors++; @@ -732,6 +654,14 @@ static inline void get_interrupt_interval( pegasus_t *pegasus ) __u8 data[2]; read_eprom_word( pegasus, 4, (__u16 *)data ); + if ( data[1] < 0x80 ) { + info( "intr interval will be changed from %ums to %ums", + data[1], 0x80 ); + data[1] = 0x80; +#ifdef PEGASUS_WRITE_EEPROM + write_eprom_word( pegasus, 4, *(__u16 *)data ); +#endif + } pegasus->intr_interval = data[1]; } @@ -754,7 +684,6 @@ static int pegasus_open(struct net_device *net) if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) warn( __FUNCTION__ " failed rx_urb %d", res ); #ifdef PEGASUS_USE_INTR - get_interrupt_interval( pegasus ); FILL_INT_URB( &pegasus->intr_urb, pegasus->usb, usb_rcvintpipe(pegasus->usb, 3), pegasus->intr_buff, sizeof(pegasus->intr_buff), @@ -781,8 +710,9 @@ static int pegasus_close( struct net_device *net ) usb_unlink_urb( &pegasus->rx_urb ); usb_unlink_urb( &pegasus->tx_urb ); usb_unlink_urb( &pegasus->ctrl_urb ); +#ifdef PEGASUS_USE_INTR usb_unlink_urb( &pegasus->intr_urb ); - +#endif MOD_DEC_USE_COUNT; return 0; @@ -798,12 +728,12 @@ static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) case SIOCDEVPRIVATE: data[0] = pegasus->phy; case SIOCDEVPRIVATE+1: - read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]); + read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); return 0; case SIOCDEVPRIVATE+2: if ( !capable(CAP_NET_ADMIN) ) return -EPERM; - write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); + write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -858,14 +788,14 @@ static __u8 mii_phy_probe( pegasus_t *pegasus ) __u16 tmp; for ( i=0; i < 32; i++ ) { - read_phy_word( pegasus, i, MII_BMSR, &tmp ); + read_mii_word( pegasus, i, MII_BMSR, &tmp ); if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 ) continue; else return i; } - return 0; + return 0xff; } @@ -882,11 +812,11 @@ static inline void setup_pegasus_II( pegasus_t *pegasus ) static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) { - struct net_device *net; - pegasus_t *pegasus; - int dev_indx; + struct net_device *net; + pegasus_t *pegasus; + int dev_index; - if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) { + if ( (dev_index = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) { return NULL; } @@ -902,7 +832,7 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) usb_inc_dev_use( dev ); memset(pegasus, 0, sizeof(struct pegasus)); - init_MUTEX( &pegasus-> ctrl_sem ); + pegasus->dev_index = dev_index; init_waitqueue_head( &pegasus->ctrl_wait ); net = init_etherdev( NULL, 0 ); @@ -926,7 +856,10 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; - pegasus->features = usb_dev_id[dev_indx].private; + pegasus->features = usb_dev_id[dev_index].private; +#ifdef PEGASUS_USE_INTR + get_interrupt_interval( pegasus ); +#endif if ( reset_mac(pegasus) ) { err("can't reset MAC"); unregister_netdev( pegasus->net ); @@ -935,21 +868,21 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) return NULL; } + info( "%s: %s", net->name, usb_dev_id[dev_index].name ); + set_ethernet_addr( pegasus ); - + if ( pegasus->features & PEGASUS_II ) { info( "setup Pegasus II specific registers" ); setup_pegasus_II( pegasus ); } pegasus->phy = mii_phy_probe( pegasus ); - if ( !pegasus->phy ) { + if ( pegasus->phy == 0xff ) { warn( "can't locate MII phy, using default" ); pegasus->phy = 1; } - info( "%s: %s", net->name, usb_dev_id[dev_indx].name ); - return pegasus; } @@ -979,7 +912,8 @@ static struct usb_driver pegasus_driver = { int __init pegasus_init(void) { - info( "%s", version ); + info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_DESC); return usb_register( &pegasus_driver ); } diff --git a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h new file mode 100644 index 000000000000..f9ab77dae460 --- /dev/null +++ b/drivers/usb/pegasus.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef PEGASUS_DEV + +#define PEGASUS_II 0x80000000 +#define HAS_HOME_PNA 0x40000000 + +#define PEGASUS_MTU 1500 +#define PEGASUS_MAX_MTU 1536 + +#define EPROM_WRITE 0x01 +#define EPROM_READ 0x02 +#define EPROM_DONE 0x04 +#define EPROM_WR_ENABLE 0x10 +#define EPROM_LOAD 0x20 + +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define BMSR_MEDIA 0x7808 +#define MII_ANLPA 0x05 +#define ANLPA_100TX_FD 0x0100 +#define ANLPA_100TX_HD 0x0080 +#define ANLPA_10T_FD 0x0040 +#define ANLPA_10T_HD 0x0020 +#define PHY_DONE 0x80 +#define PHY_READ 0x40 +#define PHY_WRITE 0x20 +#define DEFAULT_GPIO_RESET 0x24 +#define LINKSYS_GPIO_RESET 0x24 +#define DEFAULT_GPIO_SET 0x26 + +#define PEGASUS_PRESENT 0x00000001 +#define PEGASUS_RUNNING 0x00000002 +#define PEGASUS_TX_BUSY 0x00000004 +#define PEGASUS_RX_BUSY 0x00000008 +#define CTRL_URB_RUNNING 0x00000010 +#define CTRL_URB_SLEEP 0x00000020 +#define PEGASUS_UNPLUG 0x00000040 +#define ETH_REGS_CHANGE 0x40000000 +#define ETH_REGS_CHANGED 0x80000000 + +#define RX_MULTICAST 2 +#define RX_PROMISCUOUS 4 + +#define REG_TIMEOUT (HZ) +#define PEGASUS_TX_TIMEOUT (HZ*10) + +#define TX_UNDERRUN 0x80 +#define EXCESSIVE_COL 0x40 +#define LATE_COL 0x20 +#define NO_CARRIER 0x10 +#define LOSS_CARRIER 0x08 +#define JABBER_TIMEOUT 0x04 + +#define PEGASUS_REQT_READ 0xc0 +#define PEGASUS_REQT_WRITE 0x40 +#define PEGASUS_REQ_GET_REGS 0xf0 +#define PEGASUS_REQ_SET_REGS 0xf1 +#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS +#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + +enum pegasus_registers { + EthCtrl0 = 0, + EthCtrl1 = 1, + EthCtrl2 = 2, + EthID = 0x10, + Reg1d = 0x1d, + EpromOffset = 0x20, + EpromData = 0x21, /* 0x21 low, 0x22 high byte */ + EpromCtrl = 0x23, + PhyAddr = 0x25, + PhyData = 0x26, /* 0x26 low, 0x27 high byte */ + PhyCtrl = 0x28, + UsbStst = 0x2a, + EthTxStat0 = 0x2b, + EthTxStat1 = 0x2c, + EthRxStat = 0x2d, + Reg7b = 0x7b, + Gpio0 = 0x7e, + Gpio1 = 0x7f, + Reg81 = 0x81, +}; + + +typedef struct pegasus { + struct usb_device *usb; + struct net_device *net; + struct net_device_stats stats; + unsigned flags; + unsigned features; + int dev_index; + int intr_interval; + struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; + devrequest dr; + wait_queue_head_t ctrl_wait; + struct semaphore ctrl_sem; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(intr_buff[8]); + __u8 eth_regs[4]; + __u8 phy; + __u8 gpio_res; +} pegasus_t; + + +struct usb_eth_dev { + char *name; + __u16 vendor; + __u16 device; + __u32 private; /* LSB is gpio reset value */ +}; + +#define VENDOR_3COM 0x0506 +#define VENDOR_ACCTON 0x083a +#define VENDOR_ADMTEK 0x07a6 +#define VENDOR_BILLIONTON 0x08dd +#define VENDOR_COREGA 0x07aa +#define VENDOR_DLINK1 0x2001 +#define VENDOR_DLINK2 0x07b8 +#define VENDOR_IODATA 0x04bb +#define VENDOR_LANEED 0x056e +#define VENDOR_LINKSYS 0x066b +#define VENDOR_MELCO 0x0411 +#define VENDOR_SMARTBRIDGES 0x08d1 +#define VENDOR_SMC 0x0707 +#define VENDOR_SOHOWARE 0x15e8 + +#else /* PEGASUS_DEV */ + +PEGASUS_DEV( "3Com USB Ethernet 3C460B", VENDOR_3COM, 0x4601, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet", + VENDOR_ADMTEK, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", + VENDOR_ADMTEK, 0x0986, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4001, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK1, 0x4002, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK1, 0x4003, + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK1, 0xabc1, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DU-E10", VENDOR_DLINK2, 0xabc1, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "D-Link DU-E100", VENDOR_DLINK2, 0x4002, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "FiberLine USB", VENDOR_DLINK2, 0x4102, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204, + LINKSYS_GPIO_RESET | HAS_HOME_PNA ) +PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206, + LINKSYS_GPIO_RESET ) +PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003, + DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, + DEFAULT_GPIO_RESET ) +PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, + DEFAULT_GPIO_RESET ) + +#endif /* PEGASUS_DEV */ diff --git a/drivers/usb/plusb.c b/drivers/usb/plusb.c index b3f4f2f5a91d..86596cba5919 100644 --- a/drivers/usb/plusb.c +++ b/drivers/usb/plusb.c @@ -1,9 +1,11 @@ /*****************************************************************************/ /* - * plusb.c -- prolific pl-2302 driver. + * plusb.c -- prolific pl-2301/pl-2302 driver. * * Copyright (C) 2000 Deti Fliegl (deti@fliegl.de) + * Copyright (C) 2000 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2000 Eric Z. Ayers (eric@compgen.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 @@ -20,9 +22,100 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * + * This driver creates a network interface (plusb0, plusb1, ...) that will + * send messages over a USB host-host cable based on the Prolific ASIC. + * It works a lot like plip or PP over an RS-232C null modem cable. + * + * Expect speeds of around 330Kbytes/second over a UHCI host controller. + * OHCI should be faster. Increase the MTU for faster transfers of large + * files (up-to 800Kbytes/second). (16384 is a good size) * * $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $ * + * Changelog: + * + * v0.1 deti + * Original Version of driver. + * v0.2 15 Sep 2000 pavel + * Patches to decrease latency by rescheduling the bottom half of + * interrupt code. + * v0.3 10 Oct 2000 eric + * Patches to work in v2.2 backport (v2.4 changes the way net_dev.name + * is allocated) + * v0.4 19 Oct 2000 eric + * Some more performance fixes. Lock re-submitting urbs. + * Lower the number of sk_buff's to queue. + * v0.5 25 Oct 2000 eric + * Removed use of usb_bulk_msg() all together. This caused + * the driver to block in an interrupt context. + * Consolidate read urb submission into read_urb_submit(). + * Performance is the same as v0.4. + * v0.5.1 27 Oct 2000 eric + * Extra debugging messages to help diagnose problem with uchi.o stack. + * v0.5.2 27 Oct 2000 eric + * Set the 'start' flag for the network device in plusb_net_start() + * and plusb_net_stop() (doesn't help) + * v0.5.3 27 Oct 2000 pavel + * Commented out handlers when -EPIPE is received, + * (remove calls to usb_clear_halt()) Since the callback is in + * an interrupt context, it doesn't help, it just panics + * the kernel. (what do we do?) + * Under high load, dev_alloc_skb() fails, the read URB must + * be re-submitted. + * Added plusb_change_mtu() and increased the size of _BULK_DATA_LEN + * v0.5.4 31 Oct 2000 eric + * Fix race between plusb_net_xmit() and plusb_bulk_write_complete() + * v0.5.5 1 Nov 2000 eric + * Remove dev->start field, otherwise, it won't compile in 2.4 + * Use dev_kfree_skb_any(). (important in 2.4 kernel) + * v0.5.6 2 Nov 2000 pavel,eric + * Add calls to netif_stop_queue() and netif_start_queue() + * Drop packets that come in while the free list is empty. + * (This version is being submitted after the release of 2.4-test10) + * v0.5.7 6 Nov 2000 + * Fix to not re-submit the urb on error to help when cables + * are yanked (not tested) + * + * + * KNOWN PROBLEMS: (Any suggestions greatfully accepted!) + * + * 2 Nov 2000 + * - The shutdown for this may not be entirely clean. Sometimes, the + * kernel will Oops when the cable is unplugged, or + * if the plusb module is removed. + * - If you ifdown a device and then ifup it again, the link will not + * always work. You have to 'rmmod plusb ; modprobe plusb' on + * both machines to get it to work again. Something must be wrong with + * plusb_net_open() and plusb_net_start() ? Maybe + * the 'suspend' and 'resume' entry points need to be + * implemented? + * - Needs to handle -EPIPE correctly in bulk complete handlers. + * (replace usb_clear_halt() function with async urbs?) + * - I think this code relies too much on one spinlock and does + * too much in the interrupt handler. The net1080 code is + * much more elegant, and should work for this chip. Its + * only drawback is that it is going to be tough to backport + * it to v2.2. + * - Occasionally the device will hang under the 'uhci.o' + * driver. The workaround is to ifdown the device and + * remove the modules, then re-insert them. You may have + * better luck with the 'usb-uhci.o' driver. + * - After using ifconfig down ; ifconfig up, sometimes packets + * continue to be received, but there is a framing problem. + * + * FUTURE DIRECTIONS: + * + * - Fix the known problems. + * - There isn't much functional difference between the net1080 + * driver and this one. It would be neat if the same driver + * could handle both types of chips. Or if both drivers + * could handle both types of chips - this one is easier to + * backport to the 2.2 kernel. + * - Get rid of plusb_add_buf_tail and the single spinlock. + * Use a separate spinlock for the 2 lists, and use atomic + * operators for writeurb_submitted and readurb_submitted members. + * + * */ /*****************************************************************************/ @@ -40,23 +133,113 @@ #include #include #include -#define DEBUG +//#define DEBUG 1 #include -#include "plusb.h" +#if (LINUX_VERSION_CODE < 0x020300) +#define dev_kfree_skb_any dev_kfree_skb +#endif + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.5.7" +#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de" +#define DRIVER_DESC "PL-2302 USB Interface Driver for Linux (c)2000" + +/* Definitions formerly in plusb.h relocated. No need to export them -EZA */ + +#define _PLUSB_INTPIPE 0x1 +#define _PLUSB_BULKOUTPIPE 0x2 +#define _PLUSB_BULKINPIPE 0x3 + +#define _SKB_NUM 32 + +/* increase size of BULK_DATA_LEN so we can use bigger MTU's*/ +#define _BULK_DATA_LEN 32768 + + +typedef struct +{ + int connected; /* indicates if this structure is active */ + struct usb_device *usbdev; + /* keep track of USB structure */ + int status; /* Prolific status byte returned from interrupt */ + int in_bh; /* flag to indicate that we are in the bulk handler */ + int opened; /* flag to indicate that network dev is open */ + + spinlock_t lock; /* Lock for the buffer list. re-used for + locking around submitting the readurb member. + */ + urb_t *inturb; /* Read buffer for the interrupt callback */ + unsigned char * interrupt_in_buffer; + /* holds data for the inturb*/ + urb_t *readurb; /* Read buffer for the bulk data callback */ + unsigned char * bulk_in_buffer; + /* kmalloc'ed data for the readurb */ + int readurb_submitted; + /* Flag to indicate that readurb already sent */ + urb_t *writeurb; /* Write buffer for the bulk data callback */ + int writeurb_submitted; + /* Flag to indicate that writeurb already sent */ + + struct list_head tx_skb_list; + /* sk_buff's read from net device */ + struct list_head free_skb_list; + /* free sk_buff list */ + struct net_device net_dev; + /* handle to linux network device */ + struct net_device_stats net_stats; + /* stats to return for ifconfig output */ +} plusb_t,*pplusb_t; + +/* + * skb_list - queue of packets from the network driver to be delivered to USB + */ +typedef struct +{ + struct list_head skb_list; + struct sk_buff *skb; + int state; + plusb_t *s; +} skb_list_t,*pskb_list_t; + /* --------------------------------------------------------------------- */ #define NRPLUSB 4 +/* + * Interrupt endpoint status byte, from Prolific PL-2301 docs + * Check the 'download' link at www.prolifictech.com + */ +#define _PL_INT_RES1 0x80 /* reserved */ +#define _PL_INT_RES2 0x40 /* reserved */ +#define _PL_INT_RXD _PL_INT_RES2 /* Read data ready - Not documented by Prolific, but seems to work! */ +#define _PL_INT_TX_RDY 0x20 /* OK to transmit data */ +#define _PL_INT_RESET_O 0x10 /* reset output pipe */ +#define _PL_INT_RESET_I 0x08 /* reset input pipe */ +#define _PL_INT_TX_C 0x04 /* transmission complete */ +#define _PL_INT_TX_REQ 0x02 /* transmission received */ +#define _PL_INT_PEER_E 0x01 /* peer exists */ + /*-------------------------------------------------------------------*/ static plusb_t plusb[NRPLUSB]; +static void plusb_write_bulk_complete(urb_t *purb); +static void plusb_read_bulk_complete(urb_t *purb); +static void plusb_int_complete(urb_t *purb); + /* --------------------------------------------------------------------- */ + +/* + * plusb_add_buf_tail - Take the head of the src list and append it to + * the tail of the dest list + */ static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src) { - unsigned long flags; + unsigned long flags = 0; struct list_head *tmp; int ret = 0; @@ -76,126 +259,218 @@ static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_he } /*-------------------------------------------------------------------*/ -static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length) +/* + * dequeue_next_skb - submit the first thing on the tx_skb_list to the + * USB stack. This function should be called each time we get a new + * message to send to the other host, or each time a message is successfully + * sent. + */ +static void dequeue_next_skb(char * func, plusb_t * s) { - int ret; + skb_list_t * skb_list; + unsigned long flags = 0; - dbg("plusb_my_bulk: len:%d",size); + if (!s->connected) + return; + + spin_lock_irqsave (&s->lock, flags); + + if (!list_empty (&s->tx_skb_list) && !s->writeurb_submitted) { + int submit_ret; + skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); - ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500); - if(ret<0) { - err("plusb: usb_bulk_msg failed(%d)",ret); + if (skb_list->skb) { + s->writeurb_submitted = 1; + + /* Use the buffer inside the sk_buff directly. why copy? */ + FILL_BULK_URB_TO(s->writeurb, s->usbdev, + usb_sndbulkpipe(s->usbdev, _PLUSB_BULKOUTPIPE), + skb_list->skb->data, skb_list->skb->len, + plusb_write_bulk_complete, skb_list, 500); + + dbg ("%s: %s: submitting urb. skb_list %p", s->net_dev.name, func, skb_list); + + submit_ret = usb_submit_urb(s->writeurb); + if (submit_ret) { + s->writeurb_submitted = 0; + printk (KERN_CRIT "%s: %s: can't submit writeurb: %d\n", + s->net_dev.name, func, submit_ret); + } + } /* end if the skb value has been filled in */ } - if( ret == -EPIPE ) { - warn("CLEAR_FEATURE request to remove STALL condition."); - if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) - err("request failed"); - } - - dbg("plusb_my_bulk: finished act: %d", *actual_length); - return ret; + spin_unlock_irqrestore (&s->lock, flags); } -/* --------------------------------------------------------------------- */ - -static void plusb_bh(void *context) +/* + * submit_read_urb - re-submit the read URB to the stack + */ +void submit_read_urb(char * func, plusb_t * s) { - plusb_t *s=context; - struct net_device_stats *stats=&s->net_stats; - int ret=0; - int actual_length; - skb_list_t *skb_list; - struct sk_buff *skb; - - dbg("plusb_bh: i:%d",in_interrupt()); - - while(!list_empty(&s->tx_skb_list)) { - - if(!(s->status&_PLUSB_TXOK)) - break; - - skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); - if(!skb_list->state) { - dbg("plusb_bh: not yet ready"); - schedule(); - continue; - } + unsigned long flags=0; - skb=skb_list->skb; - ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), - skb->data, skb->len, &actual_length); + if (!s->connected) + return; - if(ret || skb->len != actual_length ||!(skb->len%64)) { - plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), - NULL, 0, &actual_length); - } - - if(!ret) { - stats->tx_packets++; - stats->tx_bytes+=skb->len; - } - else { - stats->tx_errors++; - stats->tx_aborted_errors++; + spin_lock_irqsave (&s->lock, flags); + + if (!s->readurb_submitted) { + int ret; + s->readurb_submitted=1; + s->readurb->dev=s->usbdev; + ret = usb_submit_urb(s->readurb); + if (ret) { + printk (KERN_CRIT "%s: %s: error %d submitting read URB\n", + s->net_dev.name, func, ret); + s->readurb_submitted=0; } - - dbg("plusb_bh: dev_kfree_skb"); - - dev_kfree_skb(skb); - skb_list->state=0; - plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list); } - dbg("plusb_bh: finished"); - s->in_bh=0; + spin_unlock_irqrestore (&s->lock, flags); + } - /* --------------------------------------------------------------------- */ +/* + * plusb_net_xmit - callback from the network device driver for outgoing data + * + * Data has arrived to the network device from the local machine and needs + * to be sent over the USB cable. This is in an interrupt, so we don't + * want to spend too much time in this function. + * + */ static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev) { plusb_t *s=dev->priv; skb_list_t *skb_list; - int ret=NET_XMIT_SUCCESS; + unsigned int flags; dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt()); - if(!s->connected || list_empty(&s->free_skb_list)) { - ret=NET_XMIT_CN; - goto lab; - } + if(!s->connected || !s->opened) { + /* + NOTE: If we get to this point, you'll return the error + kernel: virtual device plusb0 asks to queue packet + + Other things we could do: + 1) just drop this packet + 2) drop other packets in the queue + */ + return 1; + } + + spin_lock_irqsave (&s->lock, flags); - plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list); + if (list_empty(&s->free_skb_list) + || plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list)) { + /* The buffers on this side are full. DROP the packet + I think that this shouldn't happen with the correct + use of the netif_XXX functions -EZA + */ + dbg ("plusb: Free list is empty."); + kfree_skb(skb); + s->net_stats.tx_dropped++; + spin_unlock_irqrestore (&s->lock, flags); + return 0; + } + skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list); skb_list->skb=skb; skb_list->state=1; + skb_list->s=s; -lab: - if(s->in_bh) - return ret; + if (list_empty(&s->free_skb_list)) { + /* apply "backpressure". Tell the net layer to stop sending + the driver packets. + */ + netif_stop_queue(dev); + } + + spin_unlock_irqrestore (&s->lock, flags); + + /* If there is no write urb outstanding, pull the first thing + off of the list and submit it to the USB stack + */ + dequeue_next_skb("plusb_net_xmit", s); + + return 0; +} - dbg("plusb_net_xmit: queue_task"); +/* --------------------------------------------------------------------- */ - s->in_bh=1; - queue_task(&s->bh, &tq_scheduler); +/* + * plusb_write_bulk_complete () - callback after the data has been + * sent to the USB device, or a timeout occurred. + */ +static void plusb_write_bulk_complete(urb_t *purb) +{ + skb_list_t * skb_list=purb->context; + plusb_t *s=skb_list->s; - dbg("plusb_net_xmit: finished"); - return ret; + dbg ("%s: plusb_write_bulk_complete: status:%d skb_list:%p\n", + s->net_dev.name, purb->status, skb_list); -} + skb_list->state=0; -/* --------------------------------------------------------------------- */ + if( purb->status == -EPIPE ) + printk(KERN_CRIT "%s: plusb_write_bulk_complete: got -EPIPE and don't know what to do!\n", + s->net_dev.name); + + if(!purb->status) { + s->net_stats.tx_packets++; + s->net_stats.tx_bytes+=skb_list->skb->len; + } + else { + err ("%s: plusb_write_bulk_complete: returned ERROR status:%d\n", + s->net_dev.name, purb->status); + + s->net_stats.tx_errors++; + s->net_stats.tx_aborted_errors++; + } + + dbg("plusb_bh: dev_kfree_skb"); + + /* NOTE: In 2.4 it's a problem to call dev_kfree_skb() in a hard IRQ: + Oct 28 23:42:14 bug kernel: Warning: kfree_skb on hard IRQ c023329a + */ + dev_kfree_skb_any(skb_list->skb); + + skb_list->skb = NULL; + if (plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list)) { + err ("plusb: tx list empty. This shouldn't happen."); + } + + purb->status = 0; + s->writeurb_submitted = 0; + + netif_wake_queue((&s->net_dev)); + + dequeue_next_skb("plusb_write_bulk_complete", s); -static void plusb_bulk_complete(urb_t *purb) + +} + +/* + * plusb_read_bulk_complete - Callback for data arriving from the USB device + * + * This gets called back when a full 'urb' is received from the remote system. + * This urb was allocated by this driver and is kept in the member: s->readurb + * + */ +static void plusb_read_bulk_complete(urb_t *purb) { + plusb_t *s=purb->context; - dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length); + dbg("plusb_read_bulk_complete: status:%d length:%d", purb->status,purb->actual_length); + if(!s->connected) return; - if( !purb->status) { + if( purb->status == -EPIPE ) + printk(KERN_CRIT "%s: plusb_read_bulk_complete: got -EPIPE and I don't know what to do!\n", + s->net_dev.name); + else if (!purb->status) { struct sk_buff *skb; unsigned char *dst; int len=purb->transfer_buffer_length; @@ -204,31 +479,69 @@ static void plusb_bulk_complete(urb_t *purb) skb=dev_alloc_skb(len); if(!skb) { - err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len); + printk (KERN_CRIT "%s: plusb_read_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame\n", s->net_dev.name, len); stats->rx_dropped++; - return; + } else { + dst=(char *)skb_put(skb, len); + memcpy( dst, purb->transfer_buffer, len); + + skb->dev=&s->net_dev; + skb->protocol=eth_type_trans(skb, skb->dev); + stats->rx_packets++; + stats->rx_bytes+=len; + netif_rx(skb); } + + } + + s->readurb_submitted = 0; + + if (purb->status) { + /* Give the system a chance to "catch its breath". Shortcut + re-submitting the read URB> It will be re-submitted if + another interrupt comes back. The problem scenario is that + the plub is pulled and the read returns an error. + You don't want to resumbit in this case. + */ + err ("%s: plusb_read_bulk_complete: returned status %d\n", + s->net_dev.name, purb->status); + return; + } - dst=(char *)skb_put(skb, len); - memcpy( dst, purb->transfer_buffer, len); - skb->dev=&s->net_dev; - skb->protocol=eth_type_trans(skb, skb->dev); - stats->rx_packets++; - stats->rx_bytes+=len; - netif_rx(skb); - } - else - purb->status=0; + purb->status=0; + + /* Keep it coming! resubmit the URB for reading.. Make sure + we aren't in contention with the interrupt callback. + */ + submit_read_urb("plusb_read_bulk_complete", s); } /* --------------------------------------------------------------------- */ - +/* + * plusb_int_complete - USB driver callback for interrupt msg from the device + * + * Interrupts are scheduled to go off on a periodic basis (see FILL_INT_URB) + * For the prolific device, this is basically just returning a register + * filled with bits. See the macro definitions for _PL_INT_XXX above. + * Most of these bits are for implementing a machine-machine protocol + * and can be set with a special message (described as the "Quicklink" + * feature in the prolific documentation.) + * + * I don't think we need any of that to work as a network device. If a + * message is lost, big deal - that's what UNIX networking expects from + * the physical layer. + * + */ static void plusb_int_complete(urb_t *purb) { plusb_t *s=purb->context; s->status=((unsigned char*)purb->transfer_buffer)[0]&255; + #if 0 + /* This isn't right because 0x20 is TX_RDY and + sometimes will not be set + */ if((s->status&0x3f)!=0x20) { warn("invalid device status %02X", s->status); return; @@ -237,66 +550,95 @@ static void plusb_int_complete(urb_t *purb) if(!s->connected) return; - if(s->status&_PLUSB_RXD) { - int ret; - - if(s->bulkurb->status) { - err("plusb_int_complete: URB still in use"); - return; - } - - ret=usb_submit_urb(s->bulkurb); - if(ret && ret!=-EBUSY) { - err("plusb_int_complete: usb_submit_urb failed"); - } - } - - if(purb->status || s->status!=160) - dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status); + /* Don't turn this on unless you want to see the log flooded. */ +#if 0 + printk("plusb_int_complete: PEER_E:%d TX_REQ:%d TX_C:%d RESET_IN:%d RESET_O: %d TX_RDY:%d RES1:%d RES2:%d\n", + s->status & _PL_INT_PEER_E ? 1 : 0, + s->status & _PL_INT_TX_REQ ? 1 : 0, + s->status & _PL_INT_TX_C ? 1 : 0, + s->status & _PL_INT_RESET_I ? 1 : 0, + s->status & _PL_INT_RESET_O ? 1 : 0, + s->status & _PL_INT_TX_RDY ? 1 : 0, + s->status & _PL_INT_RES1 ? 1 : 0, + s->status & _PL_INT_RES2 ? 1 : 0); +#endif + +#if 1 + /* At first glance, this logic appears to not really be needed, but + it can help recover from intermittent problems where the + usb_submit_urb() fails in the read callback. -EZA + */ + + /* Try to submit the read URB again. Make sure + we aren't in contention with the bulk read callback + */ + submit_read_urb ("plusb_int_complete", s); + + /* While we are at it, why not check to see if the + write urb should be re-submitted? + */ + dequeue_next_skb("plusb_int_complete", s); + +#endif + } /* --------------------------------------------------------------------- */ - +/* + * plusb_free_all - deallocate all memory kept for an instance of the device. + */ static void plusb_free_all(plusb_t *s) { struct list_head *skb; skb_list_t *skb_list; dbg("plusb_free_all"); + + /* set a flag to tell all callbacks to cease and desist */ + s->connected = 0; + + /* If the interrupt handler is about to fire, let it finish up */ run_task_queue(&tq_immediate); if(s->inturb) { dbg("unlink inturb"); usb_unlink_urb(s->inturb); - } - - if(s->inturb && s->inturb->transfer_buffer) { - dbg("kfree inturb->transfer_buffer"); - kfree(s->inturb->transfer_buffer); - s->inturb->transfer_buffer=NULL; - } - - if(s->inturb) { dbg("free_urb inturb"); usb_free_urb(s->inturb); s->inturb=NULL; } + + if(s->interrupt_in_buffer) { + dbg("kfree s->interrupt_in_buffer"); + kfree(s->interrupt_in_buffer); + s->interrupt_in_buffer=NULL; + } - if(s->bulkurb) { - dbg("unlink bulkurb"); - usb_unlink_urb(s->bulkurb); + if(s->readurb) { + dbg("unlink readurb"); + usb_unlink_urb(s->readurb); + dbg("free_urb readurb:"); + usb_free_urb(s->readurb); + s->readurb=NULL; } - - if(s->bulkurb && s->bulkurb->transfer_buffer) { - dbg("kfree bulkurb->transfer_buffer"); - kfree(s->bulkurb->transfer_buffer); - s->bulkurb->transfer_buffer=NULL; + + if(s->bulk_in_buffer) { + dbg("kfree s->bulk_in_buffer"); + kfree(s->bulk_in_buffer); + s->bulk_in_buffer=NULL; } - if(s->bulkurb) { - dbg("free_urb bulkurb"); - usb_free_urb(s->bulkurb); - s->bulkurb=NULL; + + s->readurb_submitted = 0; + + if(s->writeurb) { + dbg("unlink writeurb"); + usb_unlink_urb(s->writeurb); + dbg("free_urb writeurb:"); + usb_free_urb(s->writeurb); + s->writeurb=NULL; } + + s->writeurb_submitted = 0; while(!list_empty(&s->free_skb_list)) { skb=s->free_skb_list.next; @@ -309,20 +651,30 @@ static void plusb_free_all(plusb_t *s) skb=s->tx_skb_list.next; list_del(skb); skb_list = list_entry (skb, skb_list_t, skb_list); - kfree(skb_list); + if (skb_list->skb) { + dbg ("Freeing SKB in queue"); + dev_kfree_skb_any(skb_list->skb); + skb_list->skb = NULL; + } + kfree(skb_list); } + + s->in_bh=0; + dbg("plusb_free_all: finished"); } /*-------------------------------------------------------------------*/ - +/* + * plusb_alloc - allocate memory associated with one instance of the device + */ static int plusb_alloc(plusb_t *s) { int i; skb_list_t *skb; dbg("plusb_alloc"); - + for(i=0 ; i < _SKB_NUM ; i++) { skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL); if(!skb) { @@ -340,47 +692,63 @@ static int plusb_alloc(plusb_t *s) goto reject; } - dbg("bulkurb allocation:"); - s->bulkurb=usb_alloc_urb(0); - if(!s->bulkurb) { + dbg("bulk read urb allocation:"); + s->readurb=usb_alloc_urb(0); + if(!s->readurb) { err("alloc_urb failed"); goto reject; } - dbg("bulkurb/inturb init:"); - s->inturb->dev=s->usbdev; - s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE); - s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL); - if(!s->inturb->transfer_buffer) { - err("kmalloc failed"); + dbg("bulk write urb allocation:"); + s->writeurb=usb_alloc_urb(0); + if(!s->writeurb) { + err("alloc_urb for writeurb failed"); goto reject; } - s->inturb->transfer_buffer_length=1; - s->inturb->complete=plusb_int_complete; - s->inturb->context=s; - s->inturb->interval=10; + dbg("readurb/inturb init:"); + s->interrupt_in_buffer=kmalloc(64, GFP_KERNEL); + if(!s->interrupt_in_buffer) { + err("kmalloc failed"); + goto reject; + } + + /* The original value of '10' makes this interrupt fire off a LOT. + It was set so low because the callback determined when to + sumbit the buld read URB. I've lowered it to 100 - the driver + doesn't depend on that logic anymore. -EZA + */ + FILL_INT_URB(s->inturb, s->usbdev, + usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE), + s->interrupt_in_buffer, 1, + plusb_int_complete, s, HZ); dbg("inturb submission:"); if(usb_submit_urb(s->inturb)<0) { err("usb_submit_urb failed"); goto reject; } - - dbg("bulkurb init:"); - s->bulkurb->dev=s->usbdev; - s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE); - s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL); - if(!s->bulkurb->transfer_buffer) { - err("kmalloc failed"); - goto reject; - } - s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN; - s->bulkurb->complete=plusb_bulk_complete; - s->bulkurb->context=s; - - dbg("plusb_alloc: finished"); + dbg("readurb init:"); + s->bulk_in_buffer = kmalloc(_BULK_DATA_LEN, GFP_KERNEL); + if (!s->bulk_in_buffer) { + err("kmalloc %d bytes for bulk in buffer failed", _BULK_DATA_LEN); + } + + FILL_BULK_URB(s->readurb, s->usbdev, + usb_rcvbulkpipe(s->usbdev, _PLUSB_BULKINPIPE), + s->bulk_in_buffer, _BULK_DATA_LEN, + plusb_read_bulk_complete, s); + + /* The write urb will be initialized inside the network + interrupt. + */ + + /* get the bulk read going */ + submit_read_urb("plusb_alloc", s); + + dbg ("plusb_alloc: finished. readurb=%p writeurb=%p inturb=%p", + s->readurb, s->writeurb, s->inturb); return 0; @@ -403,8 +771,11 @@ static int plusb_net_open(struct net_device *dev) return -ENOMEM; s->opened=1; - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + dbg("plusb_net_open: success"); return 0; @@ -416,11 +787,14 @@ static int plusb_net_open(struct net_device *dev) static int plusb_net_stop(struct net_device *dev) { plusb_t *s=dev->priv; + + netif_stop_queue(dev); dbg("plusb_net_stop"); - plusb_free_all(s); s->opened=0; + plusb_free_all(s); + MOD_DEC_USE_COUNT; dbg("plusb_net_stop:finished"); return 0; @@ -457,33 +831,43 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) { plusb_t *s = ptr; - printk ("plusb_net_disconnect: Starting\n"); - dbg("plusb_disconnect"); - s->connected = 0; - plusb_free_all(s); if(!s->opened && s->net_dev.name) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0'; -#if (LINUX_VERSION_CODE < 0x020300) - kfree (s->net_dev.name); - s->net_dev.name = NULL; -#endif +#if (LINUX_VERSION_CODE < 0x020300) + dbg("plusb_disconnect: About to free name"); + kfree (s->net_dev.name); + s->net_dev.name = NULL; +#endif } dbg("plusb_disconnect: finished"); - - printk ("plusb_net_disconnect: Finished\n"); - MOD_DEC_USE_COUNT; } /* --------------------------------------------------------------------- */ +static int plusb_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > _BULK_DATA_LEN)) + return -EINVAL; + + printk("plusb: changing mtu to %d\n", new_mtu); + dev->mtu = new_mtu; + + /* NOTE: Could we change the size of the READ URB here dynamically + to save kernel memory? + */ + return 0; +} + +/* --------------------------------------------------------------------- */ + int plusb_net_init(struct net_device *dev) { dbg("plusb_net_init"); @@ -493,7 +877,14 @@ int plusb_net_init(struct net_device *dev) dev->hard_start_xmit=plusb_net_xmit; dev->get_stats = plusb_net_get_stats; ether_setup(dev); - dev->tx_queue_len = 0; + dev->change_mtu = plusb_change_mtu; + /* Setting the default MTU to 16K gives good performance for + me, and keeps the ping latency low too. Setting it up + to 32K made performance go down. -EZA + Pavel says it would be best not to do this... + */ + /*dev->mtu=16384; */ + dev->tx_queue_len = 0; dev->flags = IFF_POINTOPOINT|IFF_NOARP; @@ -507,14 +898,8 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) { plusb_t *s; - printk ("plusb_probe: Starting\n"); - - if (usbdev) { - printk("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); - } else { - printk ("plusb: usbdev is NULL!\n"); - } + dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1) return NULL; @@ -523,61 +908,58 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) if (usbdev->descriptor.bNumConfigurations != 1) return NULL; - printk ("plusb_probe: Looking for Struct\n"); s = plusb_find_struct (); if (!s) return NULL; s->usbdev = usbdev; - printk ("plusb_probe: Setting Configuration\n"); if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) { err("set_configuration failed"); return NULL; } - printk ("plusb_probe: Setting Interface\n"); if (usb_set_interface (s->usbdev, 0, 0) < 0) { err("set_interface failed"); return NULL; } - printk ("plusb_probe: Checking device name\n"); - #if (LINUX_VERSION_CODE < 0x020300) - { - int i; + { + int i; - /* EZA: find the device number... we seem to have lost it...*/ - for (i=0; inet_dev.name) { - s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL); - sprintf (s->net_dev.name, "plusb%d", i); - s->net_dev.init=plusb_net_init; - s->net_dev.priv=s; - - printk ("plusb_probe: Registering Device\n"); - if(!register_netdev(&s->net_dev)) - info("registered: %s", s->net_dev.name); - else { - err("register_netdev failed"); - s->net_dev.name[0] = '\0'; - } - printk ("plusb_probe: Connected!\n"); - } - } + /* For Kernel version 2.2, the driver is responsible for + allocating this memory. For version 2.4, the rules + have apparently changed, but there is a nifty function + 'init_netdev' that might make this easier... It's in + ../net/net_init.c - but can we get there from here? (no) + -EZA + */ + + /* Find the device number... we seem to have lost it... -EZA */ + for (i=0; inet_dev.name) { + s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL); + sprintf (s->net_dev.name, "plusb%d", i); + s->net_dev.init=plusb_net_init; + s->net_dev.priv=s; + + printk ("plusb_probe: Registering Device\n"); + if(!register_netdev(&s->net_dev)) + info("registered: %s", s->net_dev.name); + else { + err("register_netdev failed"); + s->net_dev.name[0] = '\0'; + } + dbg ("plusb_probe: Connected!"); + } + } #else - /* Kernel version 2.3+ works a little bit differently */ + /* Kernel version 2.3+ works a little bit differently than 2.2 */ if(!s->net_dev.name[0]) { strcpy(s->net_dev.name, "plusb%d"); s->net_dev.init=plusb_net_init; @@ -589,10 +971,9 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) s->net_dev.name[0] = '\0'; } } - #endif + s->connected = 1; - printk ("plusb_probe: Set Connected\n"); if(s->opened) { dbg("net device already allocated, restarting USB transfers"); @@ -617,32 +998,24 @@ static struct usb_driver plusb_driver = static int __init plusb_init (void) { unsigned u; - dbg("plusb_init"); /* initialize struct */ for (u = 0; u < NRPLUSB; u++) { - plusb_t *s; - dbg("plusb_init: u=%ud about to assign s\n", u); - s= &plusb[u]; - dbg("plusb_init: u=%ud about to memset\n", u); + plusb_t *s = &plusb[u]; memset (s, 0, sizeof (plusb_t)); - s->bh.routine = (void (*)(void *))plusb_bh; - s->bh.data = s; - dbg("plusb_init: u=%ud about to init list head\n", u); INIT_LIST_HEAD (&s->tx_skb_list); INIT_LIST_HEAD (&s->free_skb_list); - dbg("plusb_init: u=%ud about to init spin lock\n", u); spin_lock_init (&s->lock); } - - dbg ("plusb_init: Initialized structure\n"); - + /* register misc device */ usb_register (&plusb_driver); dbg("plusb_init: driver registered"); + info(DRIVER_VERSION ":" DRIVER_DESC); + return 0; } @@ -655,10 +1028,21 @@ static void __exit plusb_cleanup (void) dbg("plusb_cleanup"); for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; +#if (LINUX_VERSION_CODE < 0x020300) + if(s->net_dev.name) { + dbg("unregistering netdev: %s",s->net_dev.name); + unregister_netdev(&s->net_dev); + s->net_dev.name[0] = '\0'; + kfree (s->net_dev.name); + s->net_dev.name = NULL; + } +#else if(s->net_dev.name[0]) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); + s->net_dev.name[0] = '\0'; } +#endif } usb_deregister (&plusb_driver); dbg("plusb_cleanup: finished"); @@ -666,8 +1050,8 @@ static void __exit plusb_cleanup (void) /* --------------------------------------------------------------------- */ -MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de"); -MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000"); +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); module_init (plusb_init); diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c index 3de5fc281716..9fbaac3be2c0 100644 --- a/drivers/usb/rio500.c +++ b/drivers/usb/rio500.c @@ -492,8 +492,7 @@ int usb_rio_init(void) return -1; info("USB Rio support registered."); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index dcd9e557cf24..af51410d68e2 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -581,8 +581,7 @@ static int __init belkin_sa_init (void) usb_serial_register (&belkin_old_device); usb_serial_register (&peracom_device); usb_serial_register (&gocom232_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 8860180b84fd..266580252697 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -2062,8 +2062,7 @@ static int __init digi_init (void) { usb_serial_register (&digi_acceleport_2_device); usb_serial_register (&digi_acceleport_4_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 9387c08d1771..3a4493832708 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -13,6 +13,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (07/29/2001) gb + * remove unused code in empeg_close() (thanks to Oliver Neukum for pointing this + * out) and rewrote empeg_set_termios(). + * * (04/08/2001) gb * Identify version on module load. * @@ -71,7 +75,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.0.0" +#define DRIVER_VERSION "v1.0.1" #define DRIVER_AUTHOR "Greg Kroah-Hartman , Gary Brubaker " #define DRIVER_DESC "USB Empeg Mark I/II Driver" @@ -158,55 +162,8 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) if (!port->active) { - /* gb - 2000/11/05 - * personally, I think these termios should be set in - * empeg_startup(), but it appears doing so leads to one - * of those chicken/egg problems. :) - */ - port->tty->termios->c_iflag - &= ~(IGNBRK - | BRKINT - | PARMRK - | ISTRIP - | INLCR - | IGNCR - | ICRNL - | IXON); - - port->tty->termios->c_oflag - &= ~OPOST; - - port->tty->termios->c_lflag - &= ~(ECHO - | ECHONL - | ICANON - | ISIG - | IEXTEN); - - port->tty->termios->c_cflag - &= ~(CSIZE - | PARENB); - - port->tty->termios->c_cflag - |= CS8; - - /* gb - 2000/12/03 - * Contributed by Borislav Deianov - * Notify the tty driver that the termios have changed!! - */ - port->tty->ldisc.set_termios(port->tty, NULL); - - /* gb - 2000/11/05 - * force low_latency on - * - * The tty_flip_buffer_push()'s in empeg_read_bulk_callback() will actually - * force the data through if low_latency is set. Otherwise the pushes are - * scheduled; this is bad as it opens up the possibility of dropping bytes - * on the floor. We are trying to sustain high data transfer rates; and - * don't want to drop bytes on the floor. - * Moral: use low_latency - drop no bytes - life is good. :) - */ - port->tty->low_latency = 1; + /* Force default termio settings */ + empeg_set_termios (port, NULL) ; port->active = 1; bytes_in = 0; @@ -241,7 +198,6 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) static void empeg_close (struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial; - unsigned char *transfer_buffer; unsigned long flags; if (port_paranoia_check (port, __FUNCTION__)) @@ -258,14 +214,6 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) --port->open_count; if (port->open_count <= 0) { - transfer_buffer = kmalloc (0x12, GFP_KERNEL); - - if (!transfer_buffer) { - err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); - } else { - kfree (transfer_buffer); - } - /* shutdown our bulk read */ usb_unlink_urb (port->read_urb); port->active = 0; @@ -586,65 +534,63 @@ static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsign } -/* This function is all nice and good, but we don't change anything based on it :) */ static void empeg_set_termios (struct usb_serial_port *port, struct termios *old_termios) { - unsigned int cflag = port->tty->termios->c_cflag; dbg(__FUNCTION__ " - port %d", port->number); - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg(__FUNCTION__ " - nothing to change..."); - return; - } - } - if ((!port->tty) || (!port->tty->termios)) { dbg(__FUNCTION__" - no tty structures"); return; } - /* get the byte size */ - switch (cflag & CSIZE) { - case CS5: dbg(__FUNCTION__ " - data bits = 5"); break; - case CS6: dbg(__FUNCTION__ " - data bits = 6"); break; - case CS7: dbg(__FUNCTION__ " - data bits = 7"); break; - default: - case CS8: dbg(__FUNCTION__ " - data bits = 8"); break; - } - - /* determine the parity */ - if (cflag & PARENB) - if (cflag & PARODD) - dbg(__FUNCTION__ " - parity = odd"); - else - dbg(__FUNCTION__ " - parity = even"); - else - dbg(__FUNCTION__ " - parity = none"); - - /* figure out the stop bits requested */ - if (cflag & CSTOPB) - dbg(__FUNCTION__ " - stop bits = 2"); - else - dbg(__FUNCTION__ " - stop bits = 1"); - - /* figure out the flow control settings */ - if (cflag & CRTSCTS) - dbg(__FUNCTION__ " - RTS/CTS is enabled"); - else - dbg(__FUNCTION__ " - RTS/CTS is disabled"); - - /* determine software flow control */ - if (I_IXOFF(port->tty)) - dbg(__FUNCTION__ " - XON/XOFF is enabled, XON = %2x, XOFF = %2x", START_CHAR(port->tty), STOP_CHAR(port->tty)); - else - dbg(__FUNCTION__ " - XON/XOFF is disabled"); - - /* get the baud rate wanted */ - dbg(__FUNCTION__ " - baud rate = %d", tty_get_baud_rate(port->tty)); + /* + * The empeg-car player wants these particular tty settings. + * You could, for example, change the baud rate, however the + * player only supports 115200 (currently), so there is really + * no point in support for changes to the tty settings. + * (at least for now) + * + * The default requirements for this device are: + */ + port->tty->termios->c_iflag + &= ~(IGNBRK /* disable ignore break */ + | BRKINT /* disable break causes interrupt */ + | PARMRK /* disable mark parity errors */ + | ISTRIP /* disable clear high bit of input characters */ + | INLCR /* disable translate NL to CR */ + | IGNCR /* disable ignore CR */ + | ICRNL /* disable translate CR to NL */ + | IXON); /* disable enable XON/XOFF flow control */ + + port->tty->termios->c_oflag + &= ~OPOST; /* disable postprocess output characters */ + + port->tty->termios->c_lflag + &= ~(ECHO /* disable echo input characters */ + | ECHONL /* disable echo new line */ + | ICANON /* disable erase, kill, werase, and rprnt special characters */ + | ISIG /* disable interrupt, quit, and suspend special characters */ + | IEXTEN); /* disable non-POSIX special characters */ + + port->tty->termios->c_cflag + &= ~(CSIZE /* no size */ + | PARENB /* disable parity bit */ + | CBAUD); /* clear current baud rate */ + + port->tty->termios->c_cflag + |= (CS8 /* character size 8 bits */ + | B115200); /* baud rate 115200 */ + + /* + * Force low_latency on; otherwise the pushes are scheduled; + * this is bad as it opens up the possibility of dropping bytes + * on the floor. We don't want to drop bytes on the floor. :) + */ + port->tty->low_latency = 1; + + /* Notify the tty driver that the termios have changed. */ + port->tty->ldisc.set_termios(port->tty, NULL); return; @@ -676,8 +622,7 @@ static int __init empeg_init (void) } } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 6a4083f87377..711c41c2e72f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -962,8 +962,7 @@ static int __init ftdi_sio_init (void) dbg(__FUNCTION__); usb_serial_register (&ftdi_sio_device); usb_serial_register (&ftdi_8U232AM_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index abc750bfb97e..6e74c386c8ca 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3037,8 +3037,7 @@ int __init edgeport_init(void) usb_serial_register (&edgeport_16dual_device); usb_serial_register (&edgeport_compat_id_device); usb_serial_register (&edgeport_8i_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 786c37db6811..4594c49fb1e5 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -174,8 +174,7 @@ int keyspan_init (void) usb_serial_register (&keyspan_usa28x_device); usb_serial_register (&keyspan_usa49w_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 8e0b14144d86..70cee184fb86 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -805,8 +805,7 @@ static int __init keyspan_pda_init (void) { usb_serial_register (&keyspan_pda_fake_device); usb_serial_register (&keyspan_pda_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 4ee6237c9dc6..eb7d50d7fd1b 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -853,8 +853,7 @@ static int __init mct_u232_init (void) usb_serial_register (&mct_u232_device); usb_serial_register (&mct_u232_sitecom_device); usb_serial_register (&mct_u232_du_h3sp_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 226ffa557a66..96af3822a948 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -400,8 +400,7 @@ static void omninet_shutdown (struct usb_serial *serial) static int __init omninet_init (void) { usb_serial_register (&zyxel_omninet_device); - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index a34c45419480..cc115cfcf886 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -1375,8 +1375,7 @@ int usb_serial_init(void) return -1; } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 0332c23871a3..22e7f78a7f5e 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -654,8 +654,7 @@ static int __init visor_init (void) } } - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + info(DRIVER_VERSION ":" DRIVER_DESC); return 0; } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 79c692a6e73f..d3d56b5052d3 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1,7 +1,7 @@ /* * USB ConnectTech WhiteHEAT driver * - * Copyright (C) 1999, 2000 + * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -10,6 +10,14 @@ * (at your option) any later version. * * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (04/08/2001) gb + * Identify version on module load. + * + * 2001_Mar_19 gkh + * Fixed MOD_INC and MOD_DEC logic, the ability to open a port more + * than once, and the got the proper usb_device_id table entries so + * the driver works again. * * (10/05/2000) gkh * Fixed bug with urb->dev not being set properly, now that the usb @@ -59,22 +67,25 @@ #include #include #include +#include #ifdef CONFIG_USB_SERIAL_DEBUG - #define DEBUG + static int debug = 1; #else - #undef DEBUG + static int debug; #endif -#include - -static int debug; #include "usb-serial.h" - #include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */ - #include "whiteheat.h" /* WhiteHEAT specific commands */ +/* + * Version Information + */ +#define DRIVER_VERSION "v1.0.0" +#define DRIVER_AUTHOR "Greg Kroah-Hartman " +#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" + #define CONNECT_TECH_VENDOR_ID 0x0710 #define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 #define CONNECT_TECH_WHITE_HEAT_ID 0x8001 @@ -268,43 +279,44 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) dbg(__FUNCTION__" - port %d", port->number); - if (port->active) { - dbg (__FUNCTION__ " - device already open"); - return -EINVAL; - } - port->active = 1; - - /* set up some stuff for our command port */ - command_port = &port->serial->port[COMMAND_PORT]; - if (command_port->private == NULL) { - info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); - if (info == NULL) { - err(__FUNCTION__ " - out of memory"); - return -ENOMEM; + ++port->open_count; + MOD_INC_USE_COUNT; + + if (!port->active) { + port->active = 1; + + /* set up some stuff for our command port */ + command_port = &port->serial->port[COMMAND_PORT]; + if (command_port->private == NULL) { + info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); + if (info == NULL) { + err(__FUNCTION__ " - out of memory"); + return -ENOMEM; + } + + init_waitqueue_head(&info->wait_command); + command_port->private = info; + command_port->write_urb->complete = command_port_write_callback; + command_port->read_urb->complete = command_port_read_callback; + command_port->read_urb->dev = port->serial->dev; + command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */ + usb_submit_urb (command_port->read_urb); } - init_waitqueue_head(&info->wait_command); - command_port->private = info; - command_port->write_urb->complete = command_port_write_callback; - command_port->read_urb->complete = command_port_read_callback; - command_port->read_urb->dev = port->serial->dev; - command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */ - usb_submit_urb (command_port->read_urb); - } + /* Start reading from the device */ + port->read_urb->dev = port->serial->dev; + result = usb_submit_urb(port->read_urb); + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); - /* Start reading from the device */ - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); - - /* send an open port command */ - /* firmware uses 1 based port numbering */ - open_command.port = port->number - port->serial->minor + 1; - whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); - - /* Need to do device specific setup here (control lines, baud rate, etc.) */ - /* FIXME!!! */ + /* send an open port command */ + /* firmware uses 1 based port numbering */ + open_command.port = port->number - port->serial->minor + 1; + whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + + /* Need to do device specific setup here (control lines, baud rate, etc.) */ + /* FIXME!!! */ + } dbg(__FUNCTION__ " - exit"); @@ -318,18 +330,23 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) dbg(__FUNCTION__ " - port %d", port->number); - /* send a close command to the port */ - /* firmware uses 1 based port numbering */ - close_command.port = port->number - port->serial->minor + 1; - whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); + --port->open_count; - /* Need to change the control lines here */ - /* FIXME */ + if (port->open_count <= 0) { + /* send a close command to the port */ + /* firmware uses 1 based port numbering */ + close_command.port = port->number - port->serial->minor + 1; + whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; + /* Need to change the control lines here */ + /* FIXME */ + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + port->active = 0; + } + MOD_DEC_USE_COUNT; } @@ -527,9 +544,17 @@ static int whiteheat_startup (struct usb_serial *serial) static void whiteheat_shutdown (struct usb_serial *serial) { struct usb_serial_port *command_port; + int i; dbg(__FUNCTION__); + /* stop reads and writes on all ports */ + for (i=0; i < serial->num_ports; ++i) { + while (serial->port[i].open_count > 0) { + whiteheat_close (&serial->port[i], NULL); + } + } + /* free up our private data for our command port */ command_port = &serial->port[COMMAND_PORT]; if (command_port->private != NULL) { @@ -578,6 +603,7 @@ static int __init whiteheat_init (void) { usb_serial_register (&whiteheat_fake_device); usb_serial_register (&whiteheat_device); + info(DRIVER_VERSION " " DRIVER_DESC); return 0; } @@ -592,5 +618,9 @@ static void __exit whiteheat_exit (void) module_init(whiteheat_init); module_exit(whiteheat_exit); -MODULE_AUTHOR("Greg Kroah-Hartman "); -MODULE_DESCRIPTION("USB ConnectTech WhiteHEAT driver"); +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 5fc3d7f395c6..1cb3ba49ebbd 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -2,7 +2,7 @@ * drivers/usb/usb.c * * (C) Copyright Linus Torvalds 1999 - * (C) Copyright Johannes Erdfelt 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 * (C) Copyright Andreas Gal 1999 * (C) Copyright Gregory P. Smith 1999 * (C) Copyright Deti Fliegl 1999 (new USB architecture) @@ -15,8 +15,6 @@ * Think of this as a "USB library" rather than anything else. * It should be considered a slave, with no callbacks. Callbacks * are evil. - * - * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $ */ #include @@ -25,6 +23,7 @@ #include #include #include /* for in_interrupt() */ +#include #if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG) @@ -54,6 +53,9 @@ static const int usb_bandwidth_option = 0; #endif +extern int usb_hub_init(void); +extern void usb_hub_cleanup(void); + /* * Prototypes for the device driver probing/loading functions */ @@ -71,6 +73,15 @@ static struct usb_busmap busmap; static struct usb_driver *usb_minors[16]; +/** + * usb_register - register a USB driver + * @new_driver: USB operations for the driver + * + * Registers a USB driver with the USB core. The list of unattached + * interfaces will be rescanned whenever a new driver is added, allowing + * the new driver to attach to any recognized devices. + * Returns a negative error code on failure and 0 on success. + */ int usb_register(struct usb_driver *new_driver) { if (new_driver->fops != NULL) { @@ -86,19 +97,21 @@ int usb_register(struct usb_driver *new_driver) init_MUTEX(&new_driver->serialize); /* Add it to the list of known drivers */ - list_add(&new_driver->driver_list, &usb_driver_list); + list_add_tail(&new_driver->driver_list, &usb_driver_list); usb_scan_devices(); return 0; } -/* - * We go through all existing devices, and see if any of them would - * be acceptable to the new driver.. This is done using a depth-first - * search for devices without a registered driver already, then - * running 'probe' with each of the drivers registered on every one - * of these. +/** + * usb_scan_devices - scans all unclaimed USB interfaces + * + * Goes through all unclaimed USB interfaces, and offers them to all + * registered USB drivers through the 'probe' function. + * This will automatically be called after usb_register is called. + * It is called by some of the USB subsystems after one of their subdrivers + * are registered. */ void usb_scan_devices(void) { @@ -140,7 +153,9 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) down(&driver->serialize); driver->disconnect(dev, interface->private_data); up(&driver->serialize); - usb_driver_release_interface(driver, interface); + /* if driver->disconnect didn't release the interface */ + if (interface->driver) + usb_driver_release_interface(driver, interface); /* * This will go through the list looking for another * driver that can handle the device @@ -150,8 +165,11 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) } } -/* - * Unlink a driver from the driver list when it is unloaded +/** + * usb_deregister - unregister a USB driver + * @driver: USB operations of the driver to unregister + * + * Unlinks the specified driver from the internal USB driver list. */ void usb_deregister(struct usb_driver *driver) { @@ -327,8 +345,28 @@ void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc) urb->bandwidth = 0; } -/* - * New functions for (de)registering a controller +static void usb_bus_get(struct usb_bus *bus) +{ + atomic_inc(&bus->refcnt); +} + +static void usb_bus_put(struct usb_bus *bus) +{ + if (atomic_dec_and_test(&bus->refcnt)) + kfree(bus); +} + +/** + * usb_alloc_bus - creates a new USB host controller structure + * @op: pointer to a struct usb_operations that this bus structure should use + * + * Creates a USB host controller bus structure with the specified + * usb_operations and initializes all the necessary internal objects. + * (For use only by USB Host Controller Drivers.) + * + * If no memory is available, NULL is returned. + * + * The caller should call usb_free_bus() when it is finished with the structure. */ struct usb_bus *usb_alloc_bus(struct usb_operations *op) { @@ -340,6 +378,10 @@ struct usb_bus *usb_alloc_bus(struct usb_operations *op) memset(&bus->devmap, 0, sizeof(struct usb_devmap)); +#ifdef DEVNUM_ROUND_ROBIN + bus->devnum_next = 1; +#endif /* DEVNUM_ROUND_ROBIN */ + bus->op = op; bus->root_hub = NULL; bus->hcpriv = NULL; @@ -351,17 +393,31 @@ struct usb_bus *usb_alloc_bus(struct usb_operations *op) INIT_LIST_HEAD(&bus->bus_list); INIT_LIST_HEAD(&bus->inodes); + atomic_set(&bus->refcnt, 1); + return bus; } +/** + * usb_free_bus - frees the memory used by a bus structure + * @bus: pointer to the bus to free + * + * (For use only by USB Host Controller Drivers.) + */ void usb_free_bus(struct usb_bus *bus) { if (!bus) return; - kfree(bus); + usb_bus_put(bus); } +/** + * usb_register_bus - registers the USB host controller with the usb core + * @bus: pointer to the bus to register + * + * (For use only by USB Host Controller Drivers.) + */ void usb_register_bus(struct usb_bus *bus) { int busnum; @@ -373,6 +429,8 @@ void usb_register_bus(struct usb_bus *bus) } else warn("too many buses"); + usb_bus_get(bus); + /* Add it to the list of buses */ list_add(&bus->bus_list, &usb_bus_list); @@ -381,6 +439,12 @@ void usb_register_bus(struct usb_bus *bus) info("new USB bus registered, assigned bus number %d", bus->busnum); } +/** + * usb_deregister_bus - deregisters the USB host controller + * @bus: pointer to the bus to deregister + * + * (For use only by USB Host Controller Drivers.) + */ void usb_deregister_bus(struct usb_bus *bus) { info("USB bus %d deregistered", bus->busnum); @@ -395,6 +459,8 @@ void usb_deregister_bus(struct usb_bus *bus) usbdevfs_remove_bus(bus); clear_bit(bus->busnum, busmap.busmap); + + usb_bus_put(bus); } /* @@ -722,7 +788,8 @@ static void call_policy (char *verb, struct usb_device *dev) value = call_usermodehelper (argv [0], argv, envp); kfree (buf); kfree (envp); - dbg ("kusbd policy returned 0x%x", value); + if (value != 0) + dbg ("kusbd policy returned 0x%x", value); } #else @@ -783,6 +850,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) memset(dev, 0, sizeof(*dev)); + usb_bus_get(bus); + dev->bus = bus; dev->parent = parent; atomic_set(&dev->refcnt, 1); @@ -799,6 +868,9 @@ void usb_free_dev(struct usb_device *dev) if (atomic_dec_and_test(&dev->refcnt)) { dev->bus->op->deallocate(dev); usb_destroy_configuration(dev); + + usb_bus_put(dev->bus); + kfree(dev); } } @@ -807,10 +879,23 @@ void usb_inc_dev_use(struct usb_device *dev) { atomic_inc(&dev->refcnt); } + /* ------------------------------------------------------------------------------------- * New USB Core Functions * -------------------------------------------------------------------------------------*/ +/** + * usb_alloc_urb - creates a new urb for a USB driver to use + * @iso_packets: number of iso packets for this urb + * + * Creates an urb for the USB driver to use and returns a pointer to it. + * If no memory is available, NULL is returned. + * + * If the driver want to use this urb for interrupt, control, or bulk + * endpoints, pass '0' as the number of iso packets. + * + * The driver should call usb_free_urb() when it is finished with the urb. + */ urb_t *usb_alloc_urb(int iso_packets) { urb_t *urb; @@ -829,7 +914,14 @@ urb_t *usb_alloc_urb(int iso_packets) return urb; } -/*-------------------------------------------------------------------*/ +/** + * usb_free_urb - frees the memory used by a urb + * @urb: pointer to the urb to free + * + * If an urb is created with a call to usb_create_urb() it should be + * cleaned up with a call to usb_free_urb() when the driver is finished + * with it. + */ void usb_free_urb(urb_t* urb) { if (urb) @@ -894,6 +986,7 @@ static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) if (status) { // something went wrong usb_free_urb(urb); + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); return status; } @@ -904,6 +997,7 @@ static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) } else status = 1; + current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); if (!status) { @@ -945,7 +1039,27 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, } -/*-------------------------------------------------------------------*/ +/** + * usb_control_msg - Builds a control urb, sends it off and waits for completion + * @dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @request: USB message request value + * @requesttype: USB message request type value + * @value: USB message value + * @index: USB message index value + * @data: pointer to the data to send + * @size: length in bytes of the data to send + * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever) + * + * This function sends a simple control message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * If successful, it returns 0, othwise a negative error number. + * + * Don't use this function from within an interrupt context, like a + * bottom half handler. If you need a asyncronous message, or need to send + * a message from within interrupt context, use usb_submit_urb() + */ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout) { @@ -970,10 +1084,27 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u return ret; } -/*-------------------------------------------------------------------*/ -/* compatibility wrapper, builds bulk urb, and waits for completion */ -/* synchronous behavior */ +/** + * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transfered in bytes + * @timeout: time to wait for the message to complete before timing out (if 0 the wait is forever) + * + * This function sends a simple bulk message to a specified endpoint + * and waits for the message to complete, or timeout. + * + * If successful, it returns 0, othwise a negative error number. + * The number of actual bytes transferred will be plaed in the + * actual_timeout paramater. + * + * Don't use this function from within an interrupt context, like a + * bottom half handler. If you need a asyncronous message, or need to + * send a message from within interrupt context, use usb_submit_urb() + */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { @@ -1479,8 +1610,6 @@ void usb_disconnect(struct usb_device **pdev) info("USB disconnect on device %d", dev->devnum); - call_policy ("remove", dev); - if (dev->actconfig) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; @@ -1489,7 +1618,9 @@ void usb_disconnect(struct usb_device **pdev) down(&driver->serialize); driver->disconnect(dev, interface->private_data); up(&driver->serialize); - usb_driver_release_interface(driver, interface); + /* if driver->disconnect didn't release the interface */ + if (interface->driver) + usb_driver_release_interface(driver, interface); } } } @@ -1501,6 +1632,9 @@ void usb_disconnect(struct usb_device **pdev) usb_disconnect(child); } + /* Let policy agent unload modules etc */ + call_policy ("remove", dev); + /* Free the device number and remove the /proc/bus/usb entry */ if (dev->devnum > 0) { clear_bit(dev->devnum, &dev->bus->devmap.devicemap); @@ -1522,10 +1656,20 @@ void usb_connect(struct usb_device *dev) int devnum; // FIXME needs locking for SMP!! /* why? this is called only from the hub thread, - * which hopefully doesn't run on multiple CPU's simulatenously 8-) + * which hopefully doesn't run on multiple CPU's simultaneously 8-) */ dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ +#ifndef DEVNUM_ROUND_ROBIN devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); +#else /* round_robin alloc of devnums */ + /* Try to allocate the next devnum beginning at bus->devnum_next. */ + devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next); + if (devnum >= 128) + devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); + + dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); +#endif /* round_robin alloc of devnums */ + if (devnum < 128) { set_bit(devnum, dev->bus->devmap.devicemap); dev->devnum = devnum; @@ -1556,9 +1700,9 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char while (i--) { if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 || + (type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) > 0 || result == -EPIPE) - break; + break; /* retry if the returned length was 0; flaky device */ } return result; } @@ -1933,7 +2077,8 @@ int usb_new_device(struct usb_device *dev) err = usb_set_address(dev); if (err < 0) { - err("USB device not accepting new address (error=%d)", err); + err("USB device not accepting new address=%d (error=%d)", + dev->devnum, err); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; @@ -1959,7 +2104,8 @@ int usb_new_device(struct usb_device *dev) if (err < 0) err("unable to get device descriptor (error=%d)", err); else - err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err); + err("USB device descriptor short read (expected %Zi, got %i)", + sizeof(dev->descriptor), err); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; @@ -1968,17 +2114,18 @@ int usb_new_device(struct usb_device *dev) err = usb_get_configuration(dev); if (err < 0) { - err("unable to get configuration (error=%d)", err); - usb_destroy_configuration(dev); + err("unable to get device %d configuration (error=%d)", dev->devnum, err); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; + usb_free_dev(dev); return 1; } /* we set the default configuration here */ err = usb_set_configuration(dev, dev->config[0].bConfigurationValue); if (err) { - err("failed to set default configuration (error=%d)", err); + err("failed to set device %d default configuration (error=%d)", + dev->devnum, err); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; @@ -2052,6 +2199,32 @@ struct list_head *usb_bus_get_list(void) } #endif + +/* + * Init + */ +static int __init usb_init(void) +{ + usb_major_init(); + usbdevfs_init(); + usb_hub_init(); + + return 0; +} + +/* + * Cleanup + */ +static void __exit usb_exit(void) +{ + usb_major_cleanup(); + usbdevfs_cleanup(); + usb_hub_cleanup(); +} + +module_init(usb_init); +module_exit(usb_exit); + /* * USB may be built into the kernel or be built as modules. * If the USB core [and maybe a host controller driver] is built diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c index eb091649ee1a..b81260be09e9 100644 --- a/drivers/video/aty128fb.c +++ b/drivers/video/aty128fb.c @@ -1747,15 +1747,43 @@ aty128_init(struct fb_info_aty128 *info, const char *name) memset(&var, 0, sizeof(var)); #ifdef CONFIG_FB_OF - /* New iBook */ - if (default_vmode == VMODE_CHOOSE && - machine_is_compatible("PowerBook2,2")) - default_vmode = VMODE_800_600_60; - - if (default_vmode == VMODE_CHOOSE) - var = default_var; - else if (mac_vmode_to_var(default_vmode, default_cmode, &var)) - var = default_var; + if (_machine == _MACH_Pmac) { + if (mode_option) { + if (mac_vmode_to_var(default_vmode, default_cmode, &var)) + var = default_var; + } else { + if (default_vmode <= 0 || default_vmode > VMODE_MAX) + default_vmode = VMODE_1024_768_60; + + /* iBook SE */ + if (machine_is_compatible("PowerBook2,2")) + default_vmode = VMODE_800_600_60; + + /* iMacs and newer iBooks need to use 1024x768 + * PowerMac2,1 first r128 iMacs + * PowerMac4,1 january 2001 iMacs "flower power" + */ + if (machine_is_compatible("PowerMac2,1") || + machine_is_compatible("PowerMac2,2") || + machine_is_compatible("PowerMac4,1")) + default_vmode = VMODE_1024_768_75; + + /* PowerBook Firewire (Pismo) and iBook2 */ + if (machine_is_compatible("PowerBook3,1") || + machine_is_compatible("PowerBook4,1")) + default_vmode = VMODE_1024_768_60; + + /* PowerBook Titanium */ + if (machine_is_compatible("PowerBook3,2")) + default_vmode = VMODE_1152_768_60; + + if (default_cmode < CMODE_8 || default_cmode > CMODE_32) + default_cmode = CMODE_8; + + if (mac_vmode_to_var(default_vmode, default_cmode, &var)) + var = default_var; + } + } else #else /* CONFIG_FB_OF */ var = default_var; #endif /* CONFIG_FB_OF */ @@ -2089,6 +2117,11 @@ aty128fb_of_init(struct device_node *dp) if (dp->name && !strcmp(dp->name, "ATY,RageM3pB")) return; + if (dp->name && !strcmp(dp->name, "ATY,RageM3p12A") && dp->parent) + dp = dp->parent; + if (dp->name && !strcmp(dp->name, "ATY,RageM3p12B")) + return; + switch (dp->n_addrs) { case 3: fb_addr = dp->addrs[0].address; diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c index 7ac48eba11d3..629b7a3ee1ab 100644 --- a/drivers/video/macmodes.c +++ b/drivers/video/macmodes.c @@ -202,8 +202,22 @@ static const struct mac_mode mac_mode_20 = { FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }; + /* 1152x768, 60 Hz, Titanium PowerBook */ -static const struct mac_mode *mac_modes[20] = { +static const struct mac_mode mac_mode_21 = { + VMODE_1152_768_60, 1152, 768, + 15386, 158, 26, 29, 3, 136, 6, + FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED +}; + + /* 1600x1024, 60 Hz, Non-Interlaced (112.27 MHz dotclock) */ +static const struct mac_mode mac_mode_22 = { + VMODE_1600_1024_60, 1600, 1024, + 8908, 88, 104, 1, 10, 16, 1, + FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED +}; + +static const struct mac_mode *mac_modes[22] = { NULL, /* 512x384, 60Hz interlaced (NTSC) */ NULL, /* 512x384, 60Hz */ NULL, /* 640x480, 50Hz interlaced (PAL) */ @@ -224,6 +238,8 @@ static const struct mac_mode *mac_modes[20] = { &mac_mode_18, /* 1152x870, 75Hz */ &mac_mode_19, /* 1280x960, 75Hz */ &mac_mode_20, /* 1280x1024, 75Hz */ + &mac_mode_21, /* 1152x768, 60Hz */ + &mac_mode_22, /* 1600x1024, 60Hz */ }; static const struct mac_mode *mac_modes_inv[] = { @@ -241,6 +257,7 @@ static const struct mac_mode *mac_modes_inv[] = { &mac_mode_18, /* 1152x870, 75Hz */ &mac_mode_19, /* 1280x960, 75Hz */ &mac_mode_20, /* 1280x1024, 75Hz */ + &mac_mode_22, /* 1600x1024, 60Hz */ }; @@ -267,6 +284,7 @@ static struct mon_map { { 0x730, VMODE_768_576_50I }, /* PAL (Alternate) */ { 0x73a, VMODE_1152_870_75 }, /* 3rd party 19" */ { 0x73f, VMODE_640_480_67 }, /* no sense lines connected at all */ + { 0xBEEF, VMODE_1600_1024_60 }, /* 22" Apple Cinema Display */ { -1, VMODE_640_480_60 }, /* catch-all, must be last */ }; diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 9614eba97a58..46c45abfe1c7 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -447,6 +447,7 @@ __initfunc(static int offb_init_driver(struct device_node *dp)) #ifdef CONFIG_FB_ATY128 if (!strncmp(dp->name, "ATY,Rage128", 11) || !strncmp(dp->name, "ATY,RageM3p1", 12) || + !strncmp(dp->name, "ATY,RageM3p2", 12) || !strncmp(dp->name, "ATY,RageM3pA", 12)) { aty128fb_of_init(dp); return 1; diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h index fe96954bccaf..61eb4541af03 100644 --- a/include/asm-s390/bitops.h +++ b/include/asm-s390/bitops.h @@ -841,7 +841,7 @@ ext2_find_next_zero_bit(void *vaddr, unsigned size, unsigned offset) " icm %0,2,1(%1)\n" " icm %0,4,2(%1)\n" " icm %0,8,3(%1)" - : "=&a" (word) : "a" (p) ); + : "=&a" (word) : "a" (p) : "cc" ); word >>= bit; res = bit; /* Look for zero in first longword */ @@ -857,7 +857,7 @@ ext2_find_next_zero_bit(void *vaddr, unsigned size, unsigned offset) "1: nr %1,0\n" " ic %1,0(%1,%2)\n" " alr %0,%1" - : "+&d" (res), "+&d" (word) + : "+&d" (res), "+&a" (word) : "a" (&_zb_findmap) : "cc", "0" ); if (res < 32) diff --git a/include/asm-s390/byteorder.h b/include/asm-s390/byteorder.h index b2fb3b9551fb..30ec4e2de6c2 100644 --- a/include/asm-s390/byteorder.h +++ b/include/asm-s390/byteorder.h @@ -23,7 +23,7 @@ static __inline__ __const__ __u32 ___arch__swab32(__u32 x) " icm %0,4,2(%1)\n" " icm %0,2,1(%1)\n" " ic %0,0(%1)" - : "+&d" (x) : "a" (&temp) : "memory" ); + : "+&d" (x) : "a" (&temp) : "memory", "cc" ); return x; } @@ -36,7 +36,7 @@ static __inline__ __const__ __u32 ___arch__swab32p(__u32 *x) " icm %0,4,2(%1)\n" " icm %0,2,1(%1)\n" " ic %0,0(%1)" - : "=&d" (result) : "a" (x) ); + : "=&d" (result) : "a" (x) : "cc" ); return result; } @@ -48,7 +48,7 @@ static __inline__ void ___arch__swab32s(__u32 *x) " icm 0,2,1(%0)\n" " ic 0,0(%0)\n" " st 0,0(%0)" - : : "a" (x) : "0", "memory"); + : : "a" (x) : "0", "memory", "cc" ); } static __inline__ __const__ __u16 ___arch__swab16(__u16 x) @@ -59,7 +59,7 @@ static __inline__ __const__ __u16 ___arch__swab16(__u16 x) " sth %0,0(%1)\n" " icm %0,2,1(%1)\n" " ic %0,0(%1)\n" - : "+&d" (x) : "a" (&temp) : "memory"); + : "+&d" (x) : "a" (&temp) : "memory", "cc" ); return x; } @@ -71,7 +71,7 @@ static __inline__ __const__ __u16 ___arch__swab16p(__u16 *x) " sr %0,%0\n" " icm %0,2,1(%1)\n" " ic %0,0(%1)\n" - : "=&d" (result) : "a" (x) ); + : "=&d" (result) : "a" (x) : "cc" ); return result; } @@ -81,7 +81,7 @@ static __inline__ void ___arch__swab16s(__u16 *x) " icm 0,2,1(%0)\n" " ic 0,0(%0)\n" " sth 0,0(%0)" - : : "a" (x) : "0", "memory"); + : : "a" (x) : "0", "memory", "cc" ); } #define __arch__swab32(x) ___arch__swab32(x) diff --git a/include/asm-s390/current.h b/include/asm-s390/current.h index 88e8fc04e964..fbc9f101febf 100644 --- a/include/asm-s390/current.h +++ b/include/asm-s390/current.h @@ -20,7 +20,7 @@ static inline struct task_struct * get_current(void) struct task_struct *current; __asm__("lhi %0,-8192\n\t" "nr %0,15" - : "=&r" (current) ); + : "=&r" (current) : : "cc" ); return current; } diff --git a/include/asm-s390/debug.h b/include/asm-s390/debug.h index 6de9551b9629..9be9863c8160 100644 --- a/include/asm-s390/debug.h +++ b/include/asm-s390/debug.h @@ -53,7 +53,7 @@ struct __debug_entry{ #define DEBUG_DATA(entry) (char*)(entry + 1) /* data is stored behind */ /* the entry information */ -#define STCK(x) asm volatile ("STCK %0":"=m" (x)) +#define STCK(x) asm volatile ("STCK %0" : "=m" (x) : : "cc" ) typedef struct __debug_entry debug_entry_t; diff --git a/include/asm-s390/io.h b/include/asm-s390/io.h index eda48f080f0f..9f3a470b61c9 100644 --- a/include/asm-s390/io.h +++ b/include/asm-s390/io.h @@ -29,7 +29,7 @@ extern inline unsigned long virt_to_phys(volatile void * address) " jz 0f\n" " sr %0,%0\n" "0:" - : "=a" (real_address) : "a" (address) ); + : "=a" (real_address) : "a" (address) : "cc" ); return real_address; } diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index 23304b92058c..aa2156c18b60 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -32,9 +32,9 @@ #define __LC_SUBCHANNEL_NR 0x0BA #define __LC_IO_INT_PARM 0x0BC #define __LC_MCCK_CODE 0x0E8 -#define __LC_AREGS_SAVE_AREA 0x200 -#define __LC_CREGS_SAVE_AREA 0x240 -#define __LC_RETURN_PSW 0x280 +#define __LC_AREGS_SAVE_AREA 0x120 +#define __LC_CREGS_SAVE_AREA 0x1c0 +#define __LC_RETURN_PSW 0x200 #define __LC_SYNC_IO_WORD 0x400 @@ -44,6 +44,7 @@ #define __LC_CPUID 0xC50 #define __LC_CPUADDR 0xC58 #define __LC_IPLDEV 0xC6C +#define __LC_PANIC_MAGIC 0xE00 /* interrupt handler start with all io, external and mcck interrupt disabled */ @@ -126,15 +127,14 @@ struct _lowcore __u32 failing_storage_address; /* 0x0f8 */ __u8 pad5[0x100-0xfc]; /* 0x0fc */ __u32 st_status_fixed_logout[4];/* 0x100 */ - __u8 pad6[0x160-0x110]; /* 0x110 */ + __u8 pad6[0x120-0x110]; /* 0x110 */ + __u32 access_regs_save_area[16];/* 0x120 */ __u32 floating_pt_save_area[8]; /* 0x160 */ __u32 gpregs_save_area[16]; /* 0x180 */ - __u8 pad7[0x200-0x1c0]; /* 0x1c0 */ + __u32 cregs_save_area[16]; /* 0x1c0 */ - __u32 access_regs_save_area[16];/* 0x200 */ - __u32 cregs_save_area[16]; /* 0x240 */ - psw_t return_psw; /* 0x280 */ - __u8 pad8[0x400-0x288]; /* 0x288 */ + psw_t return_psw; /* 0x200 */ + __u8 pad8[0x400-0x208]; /* 0x208 */ __u32 sync_io_word; /* 0x400 */ @@ -154,9 +154,14 @@ struct _lowcore atomic_t ext_call_fast; /* 0xc78 */ atomic_t ext_call_queue; /* 0xc7c */ atomic_t ext_call_count; /* 0xc80 */ + __u8 pad10[0xe00-0xc84]; /* 0xc84 */ - /* Align SMP info to the top 1k of prefix area */ - __u8 pad10[0x1000-0xc84]; /* 0xc84 */ + /* 0xe00 is used as indicator for dump tools */ + /* whether the kernel died with panic() or not */ + __u32 panic_magic; /* 0xe00 */ + + /* Align to the top 1k of prefix area */ + __u8 pad11[0x1000-0xe04]; /* 0xe04 */ } __attribute__((packed)); /* End structure*/ extern __inline__ void set_prefix(__u32 address) @@ -177,5 +182,7 @@ extern struct _lowcore *lowcore_ptr[]; #endif #endif /* __ASSEMBLY__ */ +#define __PANIC_MAGIC 0xDEADC0DE + #endif diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 3b5c12197665..65ba0fc56ac8 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -115,7 +115,7 @@ static inline void __flush_global_tlb_csp(void) "lr 4,%2\n\t" "csp 2,4" : : "d" (cs1), "d" (dum), "d" (adr) - : "2", "3", "4"); + : "2", "3", "4", "cc" ); } static inline void __flush_global_tlb(void) @@ -529,7 +529,7 @@ static inline void __flush_tlb_one(struct mm_struct *mm, __asm__ __volatile(" ic 0,2(%0)\n" " ipte %1,%2\n" " stc 0,2(%0)" - : : "a" (pte), "a" (pto), "a" (addr): "0"); + : : "a" (pte), "a" (pto), "a" (addr): "0", "cc" ); } /* diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index c364cccf8981..cd4a05903ef5 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -193,7 +193,7 @@ static inline void disabled_wait(unsigned long code) " stctl 0,15,0x1c0\n" /* store control registers */ " oi 0x1c0,0x10\n" /* fake protection bit */ " lpsw 0(%0)" - : : "a" (dw_psw), "a" (&ctl_buf)); + : : "a" (dw_psw), "a" (&ctl_buf) : "cc" ); } #endif /* __ASM_S390_PROCESSOR_H */ diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 1b1a9f040712..aaa2099cc4a4 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -50,7 +50,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) " jl 1b\n" " ex 0,4(2)" /* store *ptr to x */ : "+a&" (ptr) : "a" (&x) - : "memory", "0", "1", "2"); + : "memory", "cc", "0", "1", "2"); break; case 2: if(((__u32)ptr)&1) @@ -73,7 +73,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) " jl 1b\n" " ex 0,4(2)" /* store *ptr to x */ : "+a&" (ptr) : "a" (&x) - : "memory", "0", "1", "2"); + : "memory", "cc", "0", "1", "2"); break; case 4: if(((__u32)ptr)&3) @@ -84,7 +84,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) " jl 0b\n" " lr %0,0\n" : "+d&" (x) : "a" (ptr) - : "memory", "0" ); + : "memory", "cc", "0" ); break; default: abort(); @@ -140,7 +140,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) " st 0,0(1)\n" \ "1: ex %1,4(2)" /* execute lctl */ \ : "=m" (dummy) : "a" (cr*17), "a" (1<<(bit)) \ - : "0", "1", "2"); \ + : "cc", "0", "1", "2"); \ }) #define __ctl_clear_bit(cr, bit) ({ \ @@ -159,7 +159,7 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) " st 0,0(1)\n" \ "1: ex %1,4(2)" /* execute lctl */ \ : "=m" (dummy) : "a" (cr*17), "a" (~(1<<(bit))) \ - : "0", "1", "2"); \ + : "cc", "0", "1", "2"); \ }) #ifdef __SMP__ diff --git a/include/asm-s390/uaccess.h b/include/asm-s390/uaccess.h index 276e3338dfcb..d776de136546 100644 --- a/include/asm-s390/uaccess.h +++ b/include/asm-s390/uaccess.h @@ -104,7 +104,7 @@ extern inline int __put_user_asm_4(__u32 x, void *ptr) ".previous" : "=m" (*((__u32*) ptr)) , "=&d" (err) : "d" (x), "K" (-EFAULT) - : "4" ); + : "cc", "4" ); return err; } @@ -132,7 +132,7 @@ extern inline int __put_user_asm_2(__u16 x, void *ptr) ".previous" : "=m" (*((__u16*) ptr)) , "=&d" (err) : "d" (x), "K" (-EFAULT) - : "4" ); + : "cc", "4" ); return err; } @@ -160,7 +160,7 @@ extern inline int __put_user_asm_1(__u8 x, void *ptr) ".previous" : "=m" (*((__u8*) ptr)) , "=&d" (err) : "d" (x), "K" (-EFAULT) - : "4" ); + : "cc", "4" ); return err; } @@ -225,7 +225,7 @@ extern int __put_user_bad(void); ".previous" \ : "=d" (x) , "=&d" (err) \ : "m" (*(const __u32*) ptr), "K" (-EFAULT) \ - : "4" ); \ + : "cc", "4" ); \ }) #define __get_user_asm_2(x, ptr, err) \ @@ -250,7 +250,7 @@ extern int __put_user_bad(void); ".previous" \ : "=d" (x) , "=&d" (err) \ : "m" (*(const __u16*) ptr), "K" (-EFAULT) \ - : "4" ); \ + : "cc", "4" ); \ }) #define __get_user_asm_1(x, ptr, err) \ @@ -276,7 +276,7 @@ extern int __put_user_bad(void); ".previous" \ : "=d" (x) , "=&d" (err) \ : "m" (*(const __u8*) ptr), "K" (-EFAULT) \ - : "4" ); \ + : "cc", "4" ); \ }) #define __get_user(x, ptr) \ @@ -359,7 +359,7 @@ __copy_to_user_asm(void* to, const void* from, long n) " .long 0b,__copy_to_user_fixup\n" ".previous" : "+&d" (n) : "d" (to), "d" (from) - : "1", "2", "3", "4", "5" ); + : "cc", "1", "2", "3", "4", "5" ); return n; } @@ -397,7 +397,7 @@ __copy_from_user_asm(void* to, const void* from, long n) " .long 0b,__copy_from_user_fixup\n" ".previous" : "+&d" (n) : "d" (to), "d" (from) - : "1", "2", "3", "4", "5" ); + : "cc", "1", "2", "3", "4", "5" ); return n; } @@ -458,7 +458,7 @@ __strncpy_from_user(char *dst, const char *src, long count) : "=&a" (len) : "a" (dst), "d" (src), "d" (count), "K" (-EFAULT) - : "2", "3", "4", "memory" ); + : "2", "3", "4", "memory", "cc" ); return len; } @@ -505,6 +505,7 @@ strnlen_user(const char * src, unsigned long n) : "cc", "0", "4" ); return n; } +#define strlen_user(str) strnlen_user(str, ~0UL) /* * Zero Userspace diff --git a/include/linux/fddidevice.h b/include/linux/fddidevice.h index a093ccf71d9a..a7ebb6f09a0b 100644 --- a/include/linux/fddidevice.h +++ b/include/linux/fddidevice.h @@ -34,6 +34,8 @@ extern int fddi_header(struct sk_buff *skb, extern int fddi_rebuild_header(struct sk_buff *skb); extern unsigned short fddi_type_trans(struct sk_buff *skb, struct device *dev); +struct device * +init_fddidev(struct device *dev, int sizeof_priv); #endif #endif /* _LINUX_FDDIDEVICE_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a469ff74f1d8..1d4daf50ca05 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -441,6 +441,8 @@ extern int register_netdev(struct device *dev); extern void unregister_netdev(struct device *dev); extern int register_trdev(struct device *dev); extern void unregister_trdev(struct device *dev); +extern int register_fddidev(struct device *dev); +extern void unregister_fddidev(struct device *dev); extern int register_fcdev(struct device *dev); extern void unregister_fcdev(struct device *dev); /* Functions used for multicast support */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 1cb1c475fece..8b666645ad81 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -236,36 +236,36 @@ struct usb_device_descriptor { /* Endpoint descriptor */ struct usb_endpoint_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bEndpointAddress; - __u8 bmAttributes; - __u16 wMaxPacketSize; - __u8 bInterval; - __u8 bRefresh; - __u8 bSynchAddress; + __u8 bLength __attribute__ ((packed)); + __u8 bDescriptorType __attribute__ ((packed)); + __u8 bEndpointAddress __attribute__ ((packed)); + __u8 bmAttributes __attribute__ ((packed)); + __u16 wMaxPacketSize __attribute__ ((packed)); + __u8 bInterval __attribute__ ((packed)); + __u8 bRefresh __attribute__ ((packed)); + __u8 bSynchAddress __attribute__ ((packed)); unsigned char *extra; /* Extra descriptors */ int extralen; -} __attribute__ ((packed)); +}; /* Interface descriptor */ struct usb_interface_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bInterfaceNumber; - __u8 bAlternateSetting; - __u8 bNumEndpoints; - __u8 bInterfaceClass; - __u8 bInterfaceSubClass; - __u8 bInterfaceProtocol; - __u8 iInterface; + __u8 bLength __attribute__ ((packed)); + __u8 bDescriptorType __attribute__ ((packed)); + __u8 bInterfaceNumber __attribute__ ((packed)); + __u8 bAlternateSetting __attribute__ ((packed)); + __u8 bNumEndpoints __attribute__ ((packed)); + __u8 bInterfaceClass __attribute__ ((packed)); + __u8 bInterfaceSubClass __attribute__ ((packed)); + __u8 bInterfaceProtocol __attribute__ ((packed)); + __u8 iInterface __attribute__ ((packed)); struct usb_endpoint_descriptor *endpoint; - unsigned char *extra; /* Extra descriptors */ + unsigned char *extra; /* Extra descriptors */ int extralen; -} __attribute__ ((packed)); +}; struct usb_interface { struct usb_interface_descriptor *altsetting; @@ -280,20 +280,20 @@ struct usb_interface { /* Configuration descriptor information.. */ struct usb_config_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u16 wTotalLength; - __u8 bNumInterfaces; - __u8 bConfigurationValue; - __u8 iConfiguration; - __u8 bmAttributes; - __u8 MaxPower; + __u8 bLength __attribute__ ((packed)); + __u8 bDescriptorType __attribute__ ((packed)); + __u16 wTotalLength __attribute__ ((packed)); + __u8 bNumInterfaces __attribute__ ((packed)); + __u8 bConfigurationValue __attribute__ ((packed)); + __u8 iConfiguration __attribute__ ((packed)); + __u8 bmAttributes __attribute__ ((packed)); + __u8 MaxPower __attribute__ ((packed)); struct usb_interface *interface; - unsigned char *extra; /* Extra descriptors */ + unsigned char *extra; /* Extra descriptors */ int extralen; -} __attribute__ ((packed)); +}; /* String descriptor */ struct usb_string_descriptor { @@ -317,6 +317,7 @@ struct usb_driver { struct semaphore serialize; + /* ioctl -- userspace apps can talk to drivers through usbdevfs */ int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf); }; @@ -350,6 +351,8 @@ typedef int (*usb_device_irq)(int, void *, int, void *); #define USB_URB_EARLY_COMPLETE 0x0004 #define USB_ASYNC_UNLINK 0x0008 #define USB_QUEUE_BULK 0x0010 +#define USB_NO_FSBR 0x0020 +#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet #define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! typedef struct @@ -372,7 +375,7 @@ typedef struct urb struct usb_device *dev; // pointer to associated USB device unsigned int pipe; // pipe information int status; // returned status - unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | USB_URB_EARLY_COMPLETE + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc. void *transfer_buffer; // associated data buffer int transfer_buffer_length; // data buffer length int actual_length; // actual data buffer length @@ -486,12 +489,18 @@ struct usb_operations { int (*unlink_urb) (struct urb* purb); }; +#define DEVNUM_ROUND_ROBIN /***** OPTION *****/ + /* * Allocated per bus we have */ struct usb_bus { int busnum; /* Bus number (in order of reg) */ +#ifdef DEVNUM_ROUND_ROBIN + int devnum_next; /* Next open device number in round-robin allocation */ +#endif /* DEVNUM_ROUND_ROBIN */ + struct usb_devmap devmap; /* Device map */ struct usb_operations *op; /* Operations (specific to the HC) */ struct usb_device *root_hub; /* Root hub */ @@ -508,6 +517,8 @@ struct usb_bus { /* usbdevfs inode list */ struct list_head inodes; + + atomic_t refcnt; }; #define USB_MAXCHILDREN (16) /* This is arbitrary */ @@ -779,13 +790,13 @@ extern void usbdevfs_cleanup(void); #else /* CONFIG_USB_DEVICEFS */ -extern inline void usbdevfs_add_bus(struct usb_bus *bus) {} -extern inline void usbdevfs_remove_bus(struct usb_bus *bus) {} -extern inline void usbdevfs_add_device(struct usb_device *dev) {} -extern inline void usbdevfs_remove_device(struct usb_device *dev) {} +static inline void usbdevfs_add_bus(struct usb_bus *bus) {} +static inline void usbdevfs_remove_bus(struct usb_bus *bus) {} +static inline void usbdevfs_add_device(struct usb_device *dev) {} +static inline void usbdevfs_remove_device(struct usb_device *dev) {} -extern inline int usbdevfs_init(void) { return 0; } -extern inline void usbdevfs_cleanup(void) { } +static inline int usbdevfs_init(void) { return 0; } +static inline void usbdevfs_cleanup(void) { } #endif /* CONFIG_USB_DEVICEFS */ diff --git a/include/video/macmodes.h b/include/video/macmodes.h index c459987e331e..e96931cd50f5 100644 --- a/include/video/macmodes.h +++ b/include/video/macmodes.h @@ -38,7 +38,9 @@ #define VMODE_1152_870_75 18 /* 1152x870, 75Hz */ #define VMODE_1280_960_75 19 /* 1280x960, 75Hz */ #define VMODE_1280_1024_75 20 /* 1280x1024, 75Hz */ -#define VMODE_MAX 20 +#define VMODE_1152_768_60 21 /* 1152x768, 60Hz */ +#define VMODE_1600_1024_60 22 /* 1600x1024, 60Hz */ +#define VMODE_MAX 22 #define VMODE_CHOOSE 99 #define CMODE_NVRAM -1 diff --git a/net/ipv4/ip_masq_irc.c b/net/ipv4/ip_masq_irc.c index 0b39f98d12f8..9245b9e4bd57 100644 --- a/net/ipv4/ip_masq_irc.c +++ b/net/ipv4/ip_masq_irc.c @@ -22,6 +22,7 @@ * * Scottie Shore : added support for mIRC DCC resume negotiation * + * Juan Jose Ciarlante : src addr/port checking for better security (spotted by Michal Zalewski) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,7 +39,12 @@ * /etc/conf.modules (or /etc/modules.conf depending on your config) * where modload will pick it up should you use modload to load your * modules. - * + * + * Insecure "back" data channel opening + * The helper does some trivial checks when opening a new DCC data + * channel. Use module parameter + * insecure=1 + * ... to avoid this and get previous (pre 2.2.20) behaviour. */ #include @@ -72,6 +78,9 @@ MODULE_PARM(debug, "i"); MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +static int insecure=0; +MODULE_PARM(insecure, "i"); + /* * List of supported DCC protocols @@ -110,6 +119,30 @@ masq_irc_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) return 0; } + +/* + * Ugly workaround [TM] --mummy ... why does this protocol sucks? + * + * The <1024 check and same source address just raise the + * security "feeling" => they don't prevent a redirector listening + * in same src address at a higher port; you should protect + * your internal network with ipchains output rules anyway + */ + +static inline int masq_irc_out_check(const struct ip_masq *ms, __u32 data_saddr, __u16 data_sport) { + int allow=1; + + IP_MASQ_DEBUG(1-debug, "masq_irc_out_check( s_addr=%d.%d.%d.%d, data_saddr=%d.%d.%d.%d, data_sport=%d", + NIPQUAD(ms->saddr), NIPQUAD(data_saddr), ntohs(data_sport)); + + /* + * Ignore data channel back to other src addr, nor to port < 1024 + */ + if (ms->saddr != data_saddr || ntohs(data_sport) < 1024) + allow=0; + + return allow; +} int masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { @@ -118,7 +151,7 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb struct tcphdr *th; char *data, *data_limit; __u32 s_addr; - __u16 s_port; + __u32 s_port; /* larger to allow strtoul() return value validation */ struct ip_masq *n_ms; char buf[20]; /* "m_addr m_port" (dec base)*/ unsigned buf_len; @@ -199,12 +232,25 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb s_port = simple_strtoul(data,&data,10); addr_end_p = data; + /* Sanity checks */ + if (!s_addr || !s_port || s_port > 65535) + continue; + + /* Prefer net order from now on */ + s_addr = htonl(s_addr); + s_port = htons(s_port); + + /* Simple validation */ + if (!insecure && !masq_irc_out_check(ms, s_addr, s_port)) + /* We may just: return 0; */ + continue; + /* Do we already have a port open for this client? * If so, use it (for DCC ACCEPT) */ n_ms = ip_masq_out_get(IPPROTO_TCP, - htonl(s_addr),htons(s_port), + s_addr, s_port, 0, 0); /* @@ -216,7 +262,7 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb if (n_ms==NULL) n_ms = ip_masq_new(IPPROTO_TCP, maddr, 0, - htonl(s_addr),htons(s_port), + s_addr, s_port, 0, 0, IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR); if (n_ms==NULL) @@ -236,7 +282,10 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb diff = buf_len - (addr_end_p-addr_beg_p); *addr_beg_p = '\0'; - IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff); + IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d detected (diff=%d)\n", dcc_p, + NIPQUAD(s_addr), htons(s_port), + NIPQUAD(n_ms->maddr), htons(n_ms->mport), + diff); /* * No shift. -- 2.39.5